Добавление нового столбца в существующий фрейм данных в Python pandas

у меня есть следующий индексированный фрейм данных с именованными столбцами и строками не-непрерывных чисел:

          a         b         c         d
2  0.671399  0.101208 -0.181532  0.241273
3  0.446172 -0.243316  0.051767  1.577318
5  0.614758  0.075793 -0.451460 -0.012493

Я хотел бы добавить новый столбец,'e', к существующему фрейму данных и не хотят ничего менять в фрейме данных (т. е. новый столбец всегда имеет ту же длину, что и фрейм данных).

0   -0.335485
1   -1.166658
2   -0.385571
dtype: float64

Я пробовал разные версии join, append, merge, но я не получил желаемого результата, только ошибки. Как добавить столбец e к приведенному выше примеру?

21 ответов


используйте исходные индексы df1 для создания серии:

df1['e'] = Series(np.random.randn(sLength), index=df1.index)


редактировать 2015
Некоторые сообщили, чтобы получить SettingWithCopyWarning этот код.
Тем не менее, код по-прежнему работает идеально с текущей версией pandas 0.16.1.

>>> sLength = len(df1['a'])
>>> df1
          a         b         c         d
6 -0.269221 -0.026476  0.997517  1.294385
8  0.917438  0.847941  0.034235 -0.448948

>>> df1['e'] = p.Series(np.random.randn(sLength), index=df1.index)
>>> df1
          a         b         c         d         e
6 -0.269221 -0.026476  0.997517  1.294385  1.757167
8  0.917438  0.847941  0.034235 -0.448948  2.228131

>>> p.version.short_version
'0.16.1'

на SettingWithCopyWarning стремится сообщить о возможном недопустимом назначении на копии фрейма данных. Он не обязательно говорит, что вы сделали это неправильно (он может вызвать ложные срабатывания), но от 0.13.0 это позволит вам знаю, что есть более адекватные методы для той же цели. Затем, если вы получите предупреждение, просто следуйте его совету:попробуйте использовать .loc[row_index, col_indexer] = значение вместо

>>> df1.loc[:,'f'] = p.Series(np.random.randn(sLength), index=df1.index)
>>> df1
          a         b         c         d         e         f
6 -0.269221 -0.026476  0.997517  1.294385  1.757167 -0.050927
8  0.917438  0.847941  0.034235 -0.448948  2.228131  0.006109
>>> 

в самом деле, в настоящее время это более эффективный метод, как описано в pandas docs



изменить 2017

как указано в комментариях и @Alexander, в настоящее время лучший метод для добавления значений ряда как новый столбец фрейма данных может использовать assign:

df1 = df1.assign(e=p.Series(np.random.randn(sLength)).values)

Это простой способ добавления нового столбца:df['e'] = e


Я хотел бы добавить новый столбец " e " в существующий фрейм данных и ничего не менять в фрейме данных. (Серия всегда имеет ту же длину, что и фрейм данных.)

Я предполагаю, что значения индекса в e совпадают в df1.

самый простой способ инициировать новый столбец с именем e, и назначить ему значения из вашей серии e:

df['e'] = e.values

присвоить (панды 0.16.0+)

начиная с Pandas 0.16.0, вы также можете использовать assign, который назначает новые столбцы фрейму данных и возвращает новый объект (копию) со всеми исходными столбцами в дополнение к новым.

df1 = df1.assign(e=e.values)

по состоянию на (который также включает исходный код assign функция), вы также можете включить более одного столбца:

df = pd.DataFrame({'a': [1, 2], 'b': [3, 4]})
>>> df.assign(mean_a=df.a.mean(), mean_b=df.b.mean())
   a  b  mean_a  mean_b
0  1  3     1.5     3.5
1  2  4     1.5     3.5

в контексте вашего примера:

np.random.seed(0)
df1 = pd.DataFrame(np.random.randn(10, 4), columns=['a', 'b', 'c', 'd'])
mask = df1.applymap(lambda x: x <-0.7)
df1 = df1[-mask.any(axis=1)]
sLength = len(df1['a'])
e = pd.Series(np.random.randn(sLength))

>>> df1
          a         b         c         d
0  1.764052  0.400157  0.978738  2.240893
2 -0.103219  0.410599  0.144044  1.454274
3  0.761038  0.121675  0.443863  0.333674
7  1.532779  1.469359  0.154947  0.378163
9  1.230291  1.202380 -0.387327 -0.302303

>>> e
0   -1.048553
1   -1.420018
2   -1.706270
3    1.950775
4   -0.509652
dtype: float64

df1 = df1.assign(e=e.values)

>>> df1
          a         b         c         d         e
0  1.764052  0.400157  0.978738  2.240893 -1.048553
2 -0.103219  0.410599  0.144044  1.454274 -1.420018
3  0.761038  0.121675  0.443863  0.333674 -1.706270
7  1.532779  1.469359  0.154947  0.378163  1.950775
9  1.230291  1.202380 -0.387327 -0.302303 -0.509652

описание этой новой функции, когда она была впервые введена, можно найти здесь.


делать это напрямую через включает в себя будет наиболее эффективным:

df1['e'] = np.random.randn(sLength)

обратите внимание, что мое первоначальное (очень старое) предложение было использовать map (что гораздо медленнее):

df1['e'] = df1['a'].map(lambda x: np.random.random())

похоже, что в последних версиях Pandas путь - использовать df.назначить:

df1 = df1.assign(e=np.random.randn(sLength))

Он не производит SettingWithCopyWarning.


супер простое назначение столбцов

фрейм данных pandas реализован как упорядоченный дикт столбцов.

это означает, что __getitem__ [] может использоваться не только для получения определенного столбца, но и __setitem__ [] = можно использовать для назначения нового столбца.

например, в этот фрейм данных может быть добавлен столбец, просто используя [] аксессу

    size      name color
0    big      rose   red
1  small    violet  blue
2  small     tulip   red
3  small  harebell  blue

df['protected'] = ['no', 'no', 'no', 'yes']

    size      name color protected
0    big      rose   red        no
1  small    violet  blue        no
2  small     tulip   red        no
3  small  harebell  blue       yes

обратите внимание, что это работает, даже если индекс фрейма данных выключено.

df.index = [3,2,1,0]
df['protected'] = ['no', 'no', 'no', 'yes']
    size      name color protected
3    big      rose   red        no
2  small    violet  blue        no
1  small     tulip   red        no
0  small  harebell  blue       yes

[]= это путь, но берегись!

однако, если у вас есть pd.Series и попробуйте назначить его фрейму данных, где индексы выключены, вы столкнетесь с проблемами. См. пример:

df['protected'] = pd.Series(['no', 'no', 'no', 'yes'])
    size      name color protected
3    big      rose   red       yes
2  small    violet  blue        no
1  small     tulip   red        no
0  small  harebell  blue        no

это так pd.Series по умолчанию имеет индекс, перечисляемый от 0 до n. А панды!--14--> метод нах для "умной"

что на самом деле происходит.

при использовании the [] = метод pandas тихо выполняет внешнее соединение или внешнее слияние, используя индекс левого фрейма данных и индекс правого ряда. df['column'] = series

Примечание

это быстро вызывает когнитивный диссонанс, так как []= метод пытается сделать много разных вещей в зависимости от ввода, и результат не может быть предсказан, если вы просто знаю как работает панды. Поэтому я бы посоветовал не []= in код базы, но при изучении данных в ноутбуке, это нормально.

обойти проблему

если у вас pd.Series и хотите, чтобы он был назначен сверху вниз, или если вы кодируете продуктивный код, и вы не уверены в порядке индекса, это стоит того, чтобы гарантировать для такого рода проблемы.

вы могли бы опустить pd.Series до np.ndarray или list, это сделает уловка.

df['protected'] = pd.Series(['no', 'no', 'no', 'yes']).values

или

df['protected'] = list(pd.Series(['no', 'no', 'no', 'yes']))

но это не очень явные.

какой-то кодер может прийти и сказать: "Эй, это выглядит избыточным, я просто оптимизирую это".

явным образом

установка индекса pd.Series быть индексом df явный.

df['protected'] = pd.Series(['no', 'no', 'no', 'yes'], index=df.index)

или более реалистично, у вас, вероятно, есть pd.Series уже доступен.

protected_series = pd.Series(['no', 'no', 'no', 'yes'])
protected_series.index = df.index

3     no
2     no
1     no
0    yes

теперь можно быть назначено

df['protected'] = protected_series

    size      name color protected
3    big      rose   red        no
2  small    violet  blue        no
1  small     tulip   red        no
0  small  harebell  blue       yes

альтернативный способ с df.reset_index()

так как диссонанс индекса является проблемой, если вы чувствуете, что индекс фрейма данных должны не диктуйте вещи, вы можете просто отбросить индекс, это должно быть быстрее, но это не очень чисто, так как ваша функция теперь наверное делает две вещи.

df.reset_index(drop=True)
protected_series.reset_index(drop=True)
df['protected'] = protected_series

    size      name color protected
0    big      rose   red        no
1  small    violet  blue        no
2  small     tulip   red        no
3  small  harebell  blue       yes

Примечание df.assign

пока df.assign сделайте его более явным, что вы делаете, на самом деле у него все те же проблемы, что и выше []=

df.assign(protected=pd.Series(['no', 'no', 'no', 'yes']))
    size      name color protected
3    big      rose   red       yes
2  small    violet  blue        no
1  small     tulip   red        no
0  small  harebell  blue        no

просто следите с df.assign что ваша колонка не называется self. Это приведет к ошибкам. Это делает df.assign вонючий, так как в функции есть такие артефакты.

df.assign(self=pd.Series(['no', 'no', 'no', 'yes'])
TypeError: assign() got multiple values for keyword argument 'self'
self потом". Но кто знает, как эта функция изменится в будущем для поддержки новых аргументов. Возможно, ваше имя столбца будет аргументом в новое обновление панд, вызывающее проблемы с обновлением.

Если вы хотите установить для всего нового столбца начальное базовое значение (например,None), вы можете сделать это: df1['e'] = None

это фактически назначит тип" объект " ячейке. Поэтому позже вы можете поместить сложные типы данных, такие как list, в отдельные ячейки.


Я получил страшный SettingWithCopyWarning, и это не было исправлено с помощью синтаксиса Мот. Мой фрейм данных был создан read_sql из источника ODBC. Используя предложение lowtech выше, для меня сработало следующее:

df.insert(len(df.columns), 'e', pd.Series(np.random.randn(sLength),  index=df.index))

это отлично сработало, чтобы вставить столбец в конце. Я не знаю, является ли это наиболее эффективным, но я не люблю предупреждающие сообщения. Я думаю, что есть лучшее решение, но я не могу его найти, и я думаю, что это зависит от некоторых аспектов индекс.
Примечание. Что это работает только один раз и даст сообщение об ошибке при попытке перезаписи и существующего столбца.
Примечание как указано выше, и от 0.16.0 назначить лучшее решение. См. документацию http://pandas.pydata.org/pandas-docs/stable/generated/pandas.DataFrame.assign.html#pandas.DataFrame.assign Хорошо работает для типа потока данных, где вы не перезаписываете свои промежуточные значения.


защита от дурака:

df.loc[:, 'NewCol'] = 'New_Val'

пример:

df = pd.DataFrame(data=np.random.randn(20, 4), columns=['A', 'B', 'C', 'D'])

df

           A         B         C         D
0  -0.761269  0.477348  1.170614  0.752714
1   1.217250 -0.930860 -0.769324 -0.408642
2  -0.619679 -1.227659 -0.259135  1.700294
3  -0.147354  0.778707  0.479145  2.284143
4  -0.529529  0.000571  0.913779  1.395894
5   2.592400  0.637253  1.441096 -0.631468
6   0.757178  0.240012 -0.553820  1.177202
7  -0.986128 -1.313843  0.788589 -0.707836
8   0.606985 -2.232903 -1.358107 -2.855494
9  -0.692013  0.671866  1.179466 -1.180351
10 -1.093707 -0.530600  0.182926 -1.296494
11 -0.143273 -0.503199 -1.328728  0.610552
12 -0.923110 -1.365890 -1.366202 -1.185999
13 -2.026832  0.273593 -0.440426 -0.627423
14 -0.054503 -0.788866 -0.228088 -0.404783
15  0.955298 -1.430019  1.434071 -0.088215
16 -0.227946  0.047462  0.373573 -0.111675
17  1.627912  0.043611  1.743403 -0.012714
18  0.693458  0.144327  0.329500 -0.655045
19  0.104425  0.037412  0.450598 -0.923387


df.drop([3, 5, 8, 10, 18], inplace=True)

df

           A         B         C         D
0  -0.761269  0.477348  1.170614  0.752714
1   1.217250 -0.930860 -0.769324 -0.408642
2  -0.619679 -1.227659 -0.259135  1.700294
4  -0.529529  0.000571  0.913779  1.395894
6   0.757178  0.240012 -0.553820  1.177202
7  -0.986128 -1.313843  0.788589 -0.707836
9  -0.692013  0.671866  1.179466 -1.180351
11 -0.143273 -0.503199 -1.328728  0.610552
12 -0.923110 -1.365890 -1.366202 -1.185999
13 -2.026832  0.273593 -0.440426 -0.627423
14 -0.054503 -0.788866 -0.228088 -0.404783
15  0.955298 -1.430019  1.434071 -0.088215
16 -0.227946  0.047462  0.373573 -0.111675
17  1.627912  0.043611  1.743403 -0.012714
19  0.104425  0.037412  0.450598 -0.923387

df.loc[:, 'NewCol'] = 0

df
           A         B         C         D  NewCol
0  -0.761269  0.477348  1.170614  0.752714       0
1   1.217250 -0.930860 -0.769324 -0.408642       0
2  -0.619679 -1.227659 -0.259135  1.700294       0
4  -0.529529  0.000571  0.913779  1.395894       0
6   0.757178  0.240012 -0.553820  1.177202       0
7  -0.986128 -1.313843  0.788589 -0.707836       0
9  -0.692013  0.671866  1.179466 -1.180351       0
11 -0.143273 -0.503199 -1.328728  0.610552       0
12 -0.923110 -1.365890 -1.366202 -1.185999       0
13 -2.026832  0.273593 -0.440426 -0.627423       0
14 -0.054503 -0.788866 -0.228088 -0.404783       0
15  0.955298 -1.430019  1.434071 -0.088215       0
16 -0.227946  0.047462  0.373573 -0.111675       0
17  1.627912  0.043611  1.743403 -0.012714       0
19  0.104425  0.037412  0.450598 -0.923387       0

Если столбец, который вы пытаетесь добавить переменную серии, то просто :

df["new_columns_name"]=series_variable_name #this will do it for you

Это хорошо работает, даже если вы заменяете существующий столбец.просто введите new_columns_name так же, как столбец, который вы хотите заменить.Он просто перезапишет существующие данные столбца новыми данными серии.


позвольте мне просто добавить, что, как и для hum3, .loc не помогло SettingWithCopyWarning и мне пришлось прибегнуть к df.insert(). В моем случае false positive был сгенерирован" поддельным " индексированием цепочки dict['a']['e'], где 'e' это новая колонка, и dict['a'] является фреймом данных, поступающим из словаря.

также обратите внимание, что если вы знаете, что делаете, вы можете переключить предупреждение, используя pd.options.mode.chained_assignment = None и чем использовать другие решения проблемы.


Если фрейм данных и объект серии один и тот же индекс, pandas.concat также работает здесь:

import pandas as pd
df
#          a            b           c           d
#0  0.671399     0.101208   -0.181532    0.241273
#1  0.446172    -0.243316    0.051767    1.577318
#2  0.614758     0.075793   -0.451460   -0.012493

e = pd.Series([-0.335485, -1.166658, -0.385571])    
e
#0   -0.335485
#1   -1.166658
#2   -0.385571
#dtype: float64

# here we need to give the series object a name which converts to the new  column name 
# in the result
df = pd.concat([df, e.rename("e")], axis=1)
df

#          a            b           c           d           e
#0  0.671399     0.101208   -0.181532    0.241273   -0.335485
#1  0.446172    -0.243316    0.051767    1.577318   -1.166658
#2  0.614758     0.075793   -0.451460   -0.012493   -0.385571

в случае, если у них нет того же индекса:

e.index = df.index
df = pd.concat([df, e.rename("e")], axis=1)

  1. сначала создайте list_of_e python, который имеет соответствующие данные.
  2. использовать этот: df ['e'] = list_of_e

перед назначением нового столбца, если у вас есть индексированные данные, вам нужно отсортировать индекс. По крайней мере, в моем случае я должен был:--2-->

data.set_index(['index_column'], inplace=True)
"if index is unsorted, assignment of a new column will fail"        
data.sort_index(inplace = True)
data.loc['index_value1', 'column_y'] = np.random.randn(data.loc['index_value1', 'column_x'].shape[0])

одна вещь, чтобы отметить, однако, что если вы делаете

df1['e'] = Series(np.random.randn(sLength), index=df1.index)

это будет эффективно левый вступить в df1.индекс. Поэтому, если вы хотите иметь внешний join effect, мое, вероятно, несовершенное решение-создать фрейм данных со значениями индекса, охватывающими вселенную ваших данных, а затем использовать код выше. Например,

data = pd.DataFrame(index=all_possible_values)
df1['e'] = Series(np.random.randn(sLength), index=df1.index)

чтобы добавить новый столбец " e " в существующий фрейм данных

 df1.loc[:,'e'] = Series(np.random.randn(sLength))

Я искал общий способ добавления столбца numpy.nans в фрейм данных без получения тупого SettingWithCopyWarning.

из следующего:

  • ответ здесь
  • этот вопрос о передаче переменной в качестве аргумента ключевого слова
  • этот метод для создания numpy массив NaNs in-line

Я придумал это:

col = 'column_name'
df = df.assign(**{col:numpy.full(len(df), numpy.nan)})

для полноты-еще одно решение с использованием таблицы данных.eval () способ:

данные:

In [44]: e
Out[44]:
0    1.225506
1   -1.033944
2   -0.498953
3   -0.373332
4    0.615030
5   -0.622436
dtype: float64

In [45]: df1
Out[45]:
          a         b         c         d
0 -0.634222 -0.103264  0.745069  0.801288
4  0.782387 -0.090279  0.757662 -0.602408
5 -0.117456  2.124496  1.057301  0.765466
7  0.767532  0.104304 -0.586850  1.051297
8 -0.103272  0.958334  1.163092  1.182315
9 -0.616254  0.296678 -0.112027  0.679112

устранение:

In [46]: df1.eval("e = @e.values", inplace=True)

In [47]: df1
Out[47]:
          a         b         c         d         e
0 -0.634222 -0.103264  0.745069  0.801288  1.225506
4  0.782387 -0.090279  0.757662 -0.602408 -1.033944
5 -0.117456  2.124496  1.057301  0.765466 -0.498953
7  0.767532  0.104304 -0.586850  1.051297 -0.373332
8 -0.103272  0.958334  1.163092  1.182315  0.615030
9 -0.616254  0.296678 -0.112027  0.679112 -0.622436

следующее, что я сделал... Но я довольно новичок в панд и действительно Python в целом, поэтому никаких обещаний.

df = pd.DataFrame([[1, 2], [3, 4], [5,6]], columns=list('AB'))

newCol = [3,5,7]
newName = 'C'

values = np.insert(df.values,df.shape[1],newCol,axis=1)
header = df.columns.values.tolist()
header.append(newName)

df = pd.DataFrame(values,columns=header)

Если вы SettingWithCopyWarning, простым решением является копирование фрейма данных, в который вы пытаетесь добавить столбец.

df = df.copy()
df['col_name'] = values

самые простые способы: -

data ['new_col'] = list_of_values

данные.loc [ :, 'new_col'] = list_of_values