Добавление нового столбца в существующий 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
- Сначала создайте список python
list_of_e
с соответствующими данными. - Используйте это:
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