Установить значение для конкретной ячейки в панде DataFrame, используя индекс
Я создал панду DataFrame
df=DataFrame(index=['A','B','C'], columns=['x','y'])
и получил это
ху NaN NaN B NaN NaN C NaN NaN
Затем я хочу присвоить значение определенной ячейке, например, для строки "C" и столбца "x". Я ожидал получить такой результат:
ху NaN NaN B NaN NaN C 10 NaN
с этим кодом:
df.xs('C')['x']=10
но содержание df не изменилось. Это снова только Нэн в кадре данных.
Какие-либо предложения?
25 ответов
Ответ RukTech, df.set_value('C', 'x', 10)
, намного быстрее, чем варианты, которые я предложил ниже. Однако, это было намечено для обесценивания.
В дальнейшем рекомендуемый метод.iat/.at
,
Зачем df.xs('C')['x']=10
не работает:
df.xs('C')
по умолчанию возвращает новый фрейм данных с копией данных, поэтому
df.xs('C')['x']=10
изменяет только этот новый фрейм данных.
df['x']
возвращает вид наdf
датафрейм, так
df['x']['C'] = 10
модифицируетdf
сам.
Предупреждение: иногда трудно предсказать, вернет ли операция копию или представление. По этой причине в документах рекомендуется избегать назначений с "цепной индексацией".
Таким образом, рекомендуемая альтернатива
df.at['C', 'x'] = 10
который модифицирует df
,
In [18]: %timeit df.set_value('C', 'x', 10)
100000 loops, best of 3: 2.9 µs per loop
In [20]: %timeit df['x']['C'] = 10
100000 loops, best of 3: 6.31 µs per loop
In [81]: %timeit df.at['C', 'x'] = 10
100000 loops, best of 3: 9.2 µs per loop
Вы также можете использовать условный поиск, используя .loc
как видно здесь:
df.loc[df[<some_column_name>] == <condition>, <another_column_name>] = <value_to_add>
где <some_column_name
это столбец, который вы хотите проверить <condition>
переменная против и <another_column_name>
это столбец, который вы хотите добавить (может быть новым или уже существующим). <value_to_add>
это значение, которое вы хотите добавить в этот столбец / строку.
Этот пример не работает точно с рассматриваемым вопросом, но он может быть полезен для тех, кто хочет добавить определенное значение на основе условия.
Попробуйте использовать df.loc[row_index,col_indexer] = value
Рекомендованный способ (по словам сопровождающих) установить значение:
df.ix['x','C']=10
Использование "цепной индексации" (df['x']['C']
) может привести к проблемам.
Увидеть:
Чтобы установить значения, используйте:
df.at[0, 'clm1'] = 0
- Самый быстрый рекомендуемый метод для установки переменных.
set_value
,ix
были объявлены устаревшими- В отличие от предупреждения
iloc
а такжеloc
.iat/.at
это хорошее решение. Предположим, у вас есть этот простой data_frame:
A B C
0 1 8 4
1 3 9 6
2 22 33 52
если мы хотим изменить значение ячейки [0,"A"]
Вы можете использовать одно из этих решений:
df.iat[0,0] = 2
df.at[0,'A'] = 2
И вот полный пример, как использовать iat
чтобы получить и установить значение ячейки:
def prepossessing(df):
for index in range(0,len(df)):
df.iat[index,0] = df.iat[index,0] * 2
return df
y_train до:
0
0 54
1 15
2 15
3 8
4 31
5 63
6 11
y_train после вызова prepossessing функции, которая iat
изменить, чтобы умножить значение каждой ячейки на 2:
0
0 108
1 30
2 30
3 16
4 62
5 126
6 22
Я бы посоветовал:
df.loc[index_position, "column_name"] = some_value
Избегайте присвоения с помощью цепной индексации
Вы имеете дело с назначением с цепной индексацией, которое приведет к SettingWithCopy
предупреждение . Этого следует избегать во что бы то ни стало.
В вашем задании придется прибегнуть к одному единственному
.loc[]
или
.iloc[]
slice, как описано здесь . Следовательно, в вашем случае:
df.loc['C', 'x'] = 10
В моем примере я просто изменить его в выбранной ячейке
for index, row in result.iterrows():
if np.isnan(row['weight']):
result.at[index, 'weight'] = 0.0
'result' - это поле данных со столбцом 'weight'
Вот сводка действительных решений, предоставленных всеми пользователями, для фреймов данных, индексированных по целому числу и строке.
df.iloc, df.loc и df.at работают для обоих типов фреймов данных, df.iloc работает только с целочисленными индексами строк / столбцов, df.loc и df.at поддерживают установку значений с использованием имен столбцов и / или целочисленных индексов,
Если указанный индекс не существует, и df.loc, и df.at добавят вновь вставленные строки / столбцы к существующему фрейму данных, но df.iloc вызовет "IndexError: позиционные индексаторы выходят за пределы". Рабочий пример, протестированный в Python 2.7 и 3.7, выглядит следующим образом:
import numpy as np, pandas as pd
df1 = pd.DataFrame(index=np.arange(3), columns=['x','y','z'])
df1['x'] = ['A','B','C']
df1.at[2,'y'] = 400
# rows/columns specified does not exist, appends new rows/columns to existing data frame
df1.at['D','w'] = 9000
df1.loc['E','q'] = 499
# using df[<some_column_name>] == <condition> to retrieve target rows
df1.at[df1['x']=='B', 'y'] = 10000
df1.loc[df1['x']=='B', ['z','w']] = 10000
# using a list of index to setup values
df1.iloc[[1,2,4], 2] = 9999
df1.loc[[0,'D','E'],'w'] = 7500
df1.at[[0,2,"D"],'x'] = 10
df1.at[:, ['y', 'w']] = 8000
df1
>>> df1
x y z w q
0 10 8000 NaN 8000 NaN
1 B 8000 9999 8000 NaN
2 10 8000 9999 8000 NaN
D 10 8000 NaN 8000 NaN
E NaN 8000 9999 8000 499.0
set_value()
устарела.
Начиная с версии 0.23.4, Pandas "объявляет о будущем"...
>>> df
Cars Prices (U$)
0 Audi TT 120.0
1 Lamborghini Aventador 245.0
2 Chevrolet Malibu 190.0
>>> df.set_value(2, 'Prices (U$)', 240.0)
__main__:1: FutureWarning: set_value is deprecated and will be removed in a future release.
Please use .at[] or .iat[] accessors instead
Cars Prices (U$)
0 Audi TT 120.0
1 Lamborghini Aventador 245.0
2 Chevrolet Malibu 240.0
Учитывая этот совет, вот демонстрация того, как их использовать:
- по целым позициям строки / столбца
>>> df.iat[1, 1] = 260.0
>>> df
Cars Prices (U$)
0 Audi TT 120.0
1 Lamborghini Aventador 260.0
2 Chevrolet Malibu 240.0
- по меткам строк / столбцов
>>> df.at[2, "Cars"] = "Chevrolet Corvette"
>>> df
Cars Prices (U$)
0 Audi TT 120.0
1 Lamborghini Aventador 260.0
2 Chevrolet Corvette 240.0
Рекомендации:
Один из способов использования индекса с условием - сначала получить индекс всех строк, которые удовлетворяют вашему условию, а затем просто использовать эти индексы строк несколькими способами.
conditional_index = df.loc[ df['col name'] <condition> ].index
Пример условия похож на
==5, >10 , =="Any string", >= DateTime
Затем вы можете использовать эти индексы строк различными способами, например
- Заменить значение одного столбца на conditional_index
df.loc[conditional_index , [col name]]= <new value>
- Заменить значение нескольких столбцов на conditional_index
df.loc[conditional_index, [col1,col2]]= <new value>
- Одним из преимуществ сохранения conditional_index является то, что вы можете присвоить значение одного столбца другому столбцу с тем же индексом строки.
df.loc[conditional_index, [col1,col2]]= df.loc[conditional_index,'col name']
Все это возможно, потому что.index возвращает массив индекса, который.loc может использовать с прямой адресацией, чтобы избежать повторных обходов снова и снова.
Я проверил и вывод df.set_value
немного быстрее, но официальный метод df.at
Похоже, самый быстрый, не осуждаемый способ сделать это.
import numpy as np
import pandas as pd
df = pd.DataFrame(np.random.rand(100, 100))
%timeit df.iat[50,50]=50 # ✓
%timeit df.at[50,50]=50 # ✔
%timeit df.set_value(50,50,50) # will deprecate
%timeit df.iloc[50,50]=50
%timeit df.loc[50,50]=50
7.06 µs ± 118 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
5.52 µs ± 64.2 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
3.68 µs ± 80.8 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
98.7 µs ± 1.07 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
109 µs ± 1.42 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
Обратите внимание, что это установка значения для одной ячейки. Для векторов loc
а также iloc
должны быть лучшие варианты, так как они векторизованы.
Если кто-то хочет изменить ячейку в позиции (0,0) df на строку, такую как
'"236"76"'
, следующие параметры сделают работу:
df[0][0] = '"236"76"'
# %timeit df[0][0] = '"236"76"'
# 938 µs ± 83.4 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
Или используя
df.at[0, 0] = '"236"76"'
# %timeit df.at[0, 0] = '"236"76"'
#15 µs ± 2.09 µs per loop (mean ± std. dev. of 7 runs, 100000 loops each)
Или используя pandas.DataFrame.iat
df.iat[0, 0] = '"236"76"'
# %timeit df.iat[0, 0] = '"236"76"'
# 41.1 µs ± 3.09 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
Или используя pandas.DataFrame.loc
df.loc[0, 0] = '"236"76"'
# %timeit df.loc[0, 0] = '"236"76"'
# 5.21 ms ± 401 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
Или используя pandas.DataFrame.iloc
df.iloc[0, 0] = '"236"76"'
# %timeit df.iloc[0, 0] = '"236"76"'
# 5.12 ms ± 300 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
Если время имеет значение, используя pandas.DataFrame.at
это самый быстрый подход.
Были разочарованы множеством ответов, данных с помощью loc и .iloc, таких как
df.loc[index_position, "column_name"] = some_value
потому что при попытке этого они продолжают выдавать ошибки или предупреждения, такие как
Must have equal len keys and value when setting with an iterable
и
SettingWithCopyWarning: A value is trying to be set on a copy of a slice from a DataFrame
**однако я хотел бы разъяснить другим, у кого возникла эта проблема, что **
df.at[2, "Cars"] = mystuff
устранил предупреждение для меня и работает как шарм!
Су, ваш вопрос о преобразовании NaN в ['x',C] в значение 10
ответ..
df['x'].loc['C':]=10
df
альтернативный код
df.loc['C':'x']=10
df
df.loc['c','x']=10
Это изменит значение c- й строки и x- го столбца.
В дополнение к ответам, приведенным выше, ниже приведен сравнительный анализ различных способов добавления строк данных в уже существующий фрейм данных. Это показывает, что использование at или set-value является наиболее эффективным способом для больших фреймов данных (по крайней мере, для этих условий тестирования).
- Создайте новый фрейм данных для каждой строки и...
- ... добавить его (13,0 с)
- ... объединить его (13, 1 с)
- Сначала сохраните все новые строки в другом контейнере, один раз преобразуйте в новый фрейм данных и добавьте...
- контейнер = списки списков (2,0 с)
- контейнер = словарь списков (1,9 с)
- Предварительно распределите весь фрейм данных, переберите новые строки и все столбцы и заполните, используя
- ... в (0,6 с)
- ... set_value (0,4 с)
Для теста использовался существующий фрейм данных, состоящий из 100 000 строк и 1000 столбцов и случайных числовых значений. К этому фрейму данных было добавлено 100 новых строк.
Код смотри ниже:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Wed Nov 21 16:38:46 2018
@author: gebbissimo
"""
import pandas as pd
import numpy as np
import time
NUM_ROWS = 100000
NUM_COLS = 1000
data = np.random.rand(NUM_ROWS,NUM_COLS)
df = pd.DataFrame(data)
NUM_ROWS_NEW = 100
data_tot = np.random.rand(NUM_ROWS + NUM_ROWS_NEW,NUM_COLS)
df_tot = pd.DataFrame(data_tot)
DATA_NEW = np.random.rand(1,NUM_COLS)
#%% FUNCTIONS
# create and append
def create_and_append(df):
for i in range(NUM_ROWS_NEW):
df_new = pd.DataFrame(DATA_NEW)
df = df.append(df_new)
return df
# create and concatenate
def create_and_concat(df):
for i in range(NUM_ROWS_NEW):
df_new = pd.DataFrame(DATA_NEW)
df = pd.concat((df, df_new))
return df
# store as dict and
def store_as_list(df):
lst = [[] for i in range(NUM_ROWS_NEW)]
for i in range(NUM_ROWS_NEW):
for j in range(NUM_COLS):
lst[i].append(DATA_NEW[0,j])
df_new = pd.DataFrame(lst)
df_tot = df.append(df_new)
return df_tot
# store as dict and
def store_as_dict(df):
dct = {}
for j in range(NUM_COLS):
dct[j] = []
for i in range(NUM_ROWS_NEW):
dct[j].append(DATA_NEW[0,j])
df_new = pd.DataFrame(dct)
df_tot = df.append(df_new)
return df_tot
# preallocate and fill using .at
def fill_using_at(df):
for i in range(NUM_ROWS_NEW):
for j in range(NUM_COLS):
#print("i,j={},{}".format(i,j))
df.at[NUM_ROWS+i,j] = DATA_NEW[0,j]
return df
# preallocate and fill using .at
def fill_using_set(df):
for i in range(NUM_ROWS_NEW):
for j in range(NUM_COLS):
#print("i,j={},{}".format(i,j))
df.set_value(NUM_ROWS+i,j,DATA_NEW[0,j])
return df
#%% TESTS
t0 = time.time()
create_and_append(df)
t1 = time.time()
print('Needed {} seconds'.format(t1-t0))
t0 = time.time()
create_and_concat(df)
t1 = time.time()
print('Needed {} seconds'.format(t1-t0))
t0 = time.time()
store_as_list(df)
t1 = time.time()
print('Needed {} seconds'.format(t1-t0))
t0 = time.time()
store_as_dict(df)
t1 = time.time()
print('Needed {} seconds'.format(t1-t0))
t0 = time.time()
fill_using_at(df_tot)
t1 = time.time()
print('Needed {} seconds'.format(t1-t0))
t0 = time.time()
fill_using_set(df_tot)
t1 = time.time()
print('Needed {} seconds'.format(t1-t0))
Вы также можете изменить базовый массив ячейки.
values
/to_numpy()
возвращает представление базового массива DataFrame, поэтому, если определенное значение в массиве изменяется, это изменение также отражается на DataFrame.
df = pd.DataFrame(index=['A','B','C'], columns=['x','y'])
# change the last value in the first column
df.values[-1, 0] = 10
df.to_numpy()[-1, 0] = 10
x y
A NaN NaN
B NaN NaN
C 10 NaN
Вы также можете выбрать столбец, просмотреть его базовый массив и изменить его по индексу. Этот метод работает, даже если dtype является Extension Dtype.
# change the last value in column 'x'
df['x'].values[-1] = 100
Изменение представления DataFrame является самым быстрым способом (в 5 раз быстрее, чем следующий самый быстрый метод) для установки значения в ячейке, что становится актуальным, если это делается в цикле.
df = pd.DataFrame(index=['A', 'B', 'C'], columns=['x', 'y'])
%timeit df.values[-1, 0] = 10 # 1.89 µs ± 85.1 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)
%timeit df.iat[-1, 0] = 10 # 10.9 µs ± 380 ns per loop (mean ± std. dev. of 7 runs, 100,000 loops each)
%timeit df.at['C', 'x'] = 10 # 13 µs ± 307 ns per loop (mean ± std. dev. of 7 runs, 100,000 loops each)
%timeit df.loc['C', 'x'] = 10 # 55.4 µs ± 6.16 µs per loop (mean ± std. dev. of 7 runs, 10,000 loops each)
%timeit df.iloc[-1, 0] = 10 # 39.7 µs ± 1.85 µs per loop (mean ± std. dev. of 7 runs, 10,000 loops each)
С версии 0.21.1 вы также можете использовать .at
метод. Есть некоторые различия по сравнению с .loc
как упомянуто здесь - панды.at против.loc, но это быстрее при замене одного значения
Если вы хотите изменить значения не для всей строки, а только для некоторых столбцов:
x = pd.DataFrame({'A': [1, 2, 3], 'B': [4, 5, 6]})
x.iloc[1] = dict(A=10, B=-10)
Я тоже искал эту тему, и я собрал способ перебирать DataFrame и обновлять его поисковыми значениями из второго DataFrame. Вот мой код
src_df = pd.read_sql_query(src_sql,src_connection)
for index1, row1 in src_df.iterrows():
for index, row in vertical_df.iterrows():
src_df.set_value(index=index1,col=u'etl_load_key',value=etl_load_key)
if (row1[u'src_id'] == row['SRC_ID']) is True:
src_df.set_value(index=index1,col=u'vertical',value=row['VERTICAL'])