使用索引为熊猫DataFrame中的特定单元格设置值

我创建了一个熊猫数据帧

df = DataFrame(index=['A','B','C'], columns=['x','y'])

并得到了这个

x    y
A  NaN  NaN
B  NaN  NaN
C  NaN  NaN

现在,我想为特定单元格分配一个值,例如行C和列x。 我希望得到这个结果:

x    y
A  NaN  NaN
B  NaN  NaN
C  10  NaN

使用此代码:

df.xs('C')['x'] = 10

但是,df的内容没有改变。数据框再次只包含NaN

有什么建议吗?

1500026 次浏览

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

推荐的设置值的方法(根据维护者)是:

df.ix['x','C']=10

使用“链式索引”(df['x']['C'])可能会导致问题。

见:

更新:.set_value方法将是已弃用.iat/.at是很好的替代品,不幸的是熊猫提供的留档很少


最快的方法是使用set_value。此方法比.ix方法快约100倍。例如:

df.set_value('C', 'x', 10)

尝试使用df.loc[row_index,col_indexer] = value

这是唯一为我工作的东西!

df.loc['C', 'x'] = 10

了解有关.loc这里的更多信息。

我也在搜索这个主题,我整理了一种迭代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'])

您还可以使用.loc使用条件查找,如下所示:

df.loc[df[<some_column_name>] == <condition>, [<another_column_name>]] = <value_to_add>

其中<some_column_name是要检查<condition>变量的列,<another_column_name>是要添加到的列(可以是新列或已存在的列)。<value_to_add>是要添加到该列/行的值。

这个例子并不完全适用于手头的问题,但对于想要根据条件添加特定值的人来说,它可能很有用。

您可以使用.iloc

df.iloc[[2], [0]] = 10

如果您不想更改整行的值,而只想更改某些列的值:

x = pd.DataFrame({'A': [1, 2, 3], 'B': [4, 5, 6]})
x.iloc[1] = dict(A=10, B=-10)

从0.21.1版开始,您还可以使用.at方法。与此处提到的.loc-Pandas. at与. loc相比有一些差异,但它在单值替换上更快

在我的例子中,我只是在选定的单元格中更改它

    for index, row in result.iterrows():
if np.isnan(row['weight']):
result.at[index, 'weight'] = 0.0

“结果”是具有“权重”列的dataField

df.loc['c','x']=10 这将更改cth行的值 x第一列。

除了上面的答案之外,这里是一个基准,比较了将数据行添加到已经存在的数据框的不同方法。它表明使用at或set-value是大型数据框的最有效方法(至少对于这些测试条件)。

  • 为每一行创建新的数据框并…
    • …追加它(13.0 s)
    • …连接它(13.1 s)
  • 首先将所有新行存储在另一个容器中,转换为新数据帧一次并附加…
    • 容器=列表列表(2.0 s)
    • 容器=列表字典(1.9秒)
  • 预分配整个数据帧,遍历新行和所有列并使用
    • …在(0.6秒)
    • …set_value(0.4s)

对于测试,使用了包含100,000行和1,000列以及随机numpy值的现有数据框。为此数据框添加了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))

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

参考文献:

以下是所有用户提供的有效解决方案的摘要,用于按整数和字符串索引的数据帧。

df.ilocdf.locdf.at适用于两种类型的数据帧,df.iloc仅适用于行/列整数索引,df.locdf.at支持使用列名和/或整数索引设置值。

当指定的索引不存在时,df.locdf.at都会将新插入的行/列附加到存量数据帧中,但df.iloc会提升"IndexError: positional indexers are out-of-bounds"。在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

.iat/.at是一个很好的解决方案。 假设你有这个简单的data_frame:

   A   B   C
0  1   8   4
1  3   9   6
2  22 33  52

如果我们想修改单元格[0,"A"]的值,可以使用以下解决方案之一:

  1. df.iat[0,0] = 2
  2. df.at[0,'A'] = 2

下面是一个完整的例子,如何使用iat来获取和设置cell的值:

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调用iat后,将每个单元格的值乘以2:

     0
0   108
1   30
2   30
3   16
4   62
5   126
6   22

要设置值,请使用:

df.at[0, 'clm1'] = 0
  • 设置变量的最快推荐方法。
  • set_valueix已被弃用。
  • 没有警告,不像ilocloc

我测试了输出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)

请注意,这是为单个单元格设置值。对于向量lociloc应该是更好的选择,因为它们是矢量化的。

使用带条件索引的一种方法是首先获取满足条件的所有行的索引,然后简单地以多种方式使用这些行索引

conditional_index = df.loc[ df['col name'] <condition> ].index

示例条件如下

==5, >10 , =="Any string", >= DateTime

然后,您可以以各种方式使用这些行索引,例如

  1. 将一列的值替换为conditional_index
df.loc[conditional_index , [col name]]= <new value>
  1. 替换conditional_index的多列值
df.loc[conditional_index, [col1,col2]]= <new value>
  1. 保存conditional_index的一个好处是,您可以将一列的值分配给具有相同行索引的另一列
df.loc[conditional_index, [col1,col2]]= df.loc[conditional_index,'col name']

这一切都是可能的,因为. index返回一个索引数组,. loc可以将其用于直接寻址,因此它避免了一次又一次的遍历。

Soo,你的问题是将NaN在['x', C]转换为值10

答案是…

df['x'].loc['C':]=10
df

替代代码是

df.loc['C', 'x']=10
df

我建议:

df.loc[index_position, "column_name"] = some_value

要同时修改多个单元格:

df.loc[start_idx_pos: End_idx_pos, "column_name"] = some_value

如果要将df的位置(0,0)中的单元格更改为'"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)

或使用pandas.DataFrame.at

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是最快的方法。

避免使用链式索引分配

你正在处理一个链式索引赋值,这将导致一个SettingWithCopy警告。这应该通过一切手段来避免。

你的作业将不得不求助于一个.loc[].iloc[]切片,作为这里解释。因此,在你的情况下:

df.loc['C', 'x'] = 10