Добавление нового столбца в существующий DataFrame в Python pandas

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

          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' в существующий фрейм данных и не хочу ничего менять в кадре данных (т.е. новый столбец всегда имеет ту же длину, что и DataFrame).

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

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

Ответ 1

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

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

Редактировать 2015
Некоторые сообщили о получении SettingWithCopyWarning с этим кодом.
Тем не менее, код по-прежнему отлично работает с текущей версией панды 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'] = pd.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] = value

>>> df1.loc[:,'f'] = pd.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, в настоящее время лучшим способом добавления значений Series в качестве нового столбца DataFrame может быть использование assign:

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

Ответ 2

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

Ответ 3

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

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

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

df['e'] = e.values

назначить (Панды 0,16. 0+)

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

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

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

Ответ 4

Выполнение этого непосредственно через NumPy будет наиболее эффективным:

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

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

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

Ответ 5

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

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

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

Ответ 6

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

A pandas dataframe реализуется как упорядоченный dict столбцов.

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

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

    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 и попробуйте назначить его в dataframe, где индексы отключены, вы столкнулись с проблемой. Пример:

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. И метод pandas [] = пытается быть "умным"

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

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

Боковое примечание

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

Решение проблемы

Если у вас есть 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 тогда". Но кто знает, как эта функция изменится в будущем для поддержки новых аргументов. Возможно, ваше имя столбца будет аргументом в новом обновлении pandas, что вызовет проблемы с обновлением.

Ответ 7

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

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

Ответ 8

Я получил страшный SettingWithCopyWarning, и он не был исправлен с использованием синтаксиса iloc. My DataFrame был создан 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 Хорошо работает для типа потока данных, где вы не перезаписываете свои промежуточные значения.

Ответ 9

Простейшие способы: -

data['new_col'] = list_of_values

data.loc[ : , 'new_col'] = list_of_values

Ответ 10

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

Ответ 11

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

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

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

Ответ 12

Защищенный:

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

Ответ 13

e = [ -0.335485, -1.166658, -0.385571]

Простой и легкий способ

df['e'] = e

Ответ 14

Если в кадре данных и объекте Series есть тот же индекс, здесь также работает 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)

Ответ 15

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

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

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

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

Ответ 16

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

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])

Ответ 17

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

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

Ответ 18

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

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

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

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

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

Ответ 19

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

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

Ответ 20

Для полноты - еще одно решение с использованием Метод DataFrame.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

Ответ 21

Следующее - это то, что я сделал... Но я довольно новичок в pandas и действительно Python вообще, поэтому no promises.

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)

Ответ 22

Если вы получаете SettingWithCopyWarning, легко исправить это, чтобы скопировать DataFrame, который вы пытаетесь добавить в столбец.

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

Ответ 23

чтобы вставить новый столбец в заданном месте (0 <= loc <= количество столбцов) во фрейме данных, просто используйте Dataframe.insert:

DataFrame.insert(loc, column, value)

Поэтому, если вы хотите добавить столбец e в конце фрейма данных с именем df, вы можете использовать:

e = [-0.335485, -1.166658, -0.385571]    
DataFrame.insert(loc=len(df.columns), column='e', value=e)

значение может быть Series, целым числом (в этом случае все ячейки заполняются этим одним значением) или структурой, подобной массиву

https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.insert.html