【问题标题】:Set value for particular cell in pandas DataFrame using index使用索引为 pandas DataFrame 中的特定单元格设置值
【发布时间】:2012-11-30 07:04:17
【问题描述】:

我创建了一个 Pandas 数据框

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

得到了这个

xy 南南 B 南南 C 南 南

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

xy 南南 B 南南 C 10 NaN

使用此代码:

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

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

有什么建议吗?

【问题讨论】:

  • 不要使用“链式索引”(df['x']['C']),使用df.ix['x','C']
  • 索引访问的顺序需要是:dataframe[column (series)] [row (Series index)],而很多人(包括我自己)更习惯dataframe[row][column]的顺序。作为一名 Matlab 和 R 程序员,后者对我来说感觉更直观,但这显然不是 Pandas 的工作方式..
  • 我试过了,但我最终添加了另一个行名 x 和另一个列名 C。你必须先做行然后列。所以 df.ix['C','x']=10
  • 致@Yariv 的评论。警告:从 0.20.0 开始,不推荐使用 .ix 索引器,取而代之的是更严格的 .iloc 和 .loc 索引器。 pandas.pydata.org/pandas-docs/stable/generated/… 。 df.at 看起来好像还在附近。
  • 如果您需要使用索引值更改整个数据帧子集的值,请务必检查(并支持取消挖掘)Atta Jutt 的回答。

标签: python pandas dataframe cell nan


【解决方案1】:

这里总结了所有用户提供的有效解决方案,针对整数和字符串索引的数据帧。

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

【讨论】:

    【解决方案2】:

    避免使用链式索引进行赋值

    您正在处理assignment with chained indexing,这将导致SettingWithCopy warning。应该尽量避免这种情况。

    您的作业必须使用一个 .loc[].iloc[] 切片,如 explained here。因此,在您的情况下:

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

    【讨论】:

      【解决方案3】:

      如果想将 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 是最快的方法。

      【讨论】:

        【解决方案4】:

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

        答案是……

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

        替代代码是

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

        【讨论】:

          【解决方案5】:

          我建议:

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

          【讨论】:

            【解决方案6】:

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

            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. 替换条件索引的多列值
            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 可以将其用于直接寻址,因此它避免了一次又一次的遍历。

            【讨论】:

            • 换行怎么样?
            • 随便用,df.loc[conditional_index, ]= 它将在满足条件的行的所有列中替换新值
            【解决方案7】:

            更新:.set_value 方法将是 deprecated.iat/.at 是很好的替代品,不幸的是 pandas 提供的文档很少


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

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

            【讨论】:

            • df['x']['C'] = 10 还要好。
            • 1000 次循环,最好的 3:195 µs 每个循环 "df['x']['C'] = 10" 1000 次循环,最好的 3:310 µs 每个循环 "df.ix['C','x'] = 10" 1000 个循环,最好的 3 个:每个循环 189 µs "df.xs('C ', copy=False)['x'] = 10" 1000 次循环,3 次中最好的:7.22 µs 每个循环 "df.set_value('C', 'x', 10)"跨度>
            • 这是否也适用于向数据框添加新行/列?
            • 是的(对于 pandas 0.16.2)
            • 是否可以使用它来将值设置为df=df.append(df.sum(numeric_only=True),ignore_index=True)
            【解决方案8】:

            我测试过,输出是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 应该是更好的选择,因为它们是向量化的。

            【讨论】:

              【解决方案9】:

              要设置值,请使用:

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

              【讨论】:

              【解决方案10】:

              .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 来获取和设置单元格的值:

              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
              

              调用iat的前置函数后的y_train将每个单元格的值乘以2:

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

              【讨论】:

                【解决方案11】:

                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
                

                参考资料:

                【讨论】:

                  【解决方案12】:

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

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

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

                  此示例不适用于手头的问题,但对于想要根据条件添加特定值的人可能很有用。

                  【讨论】:

                  • 第二列需要放在括号中,否则所有列都会被值覆盖。像这样:df.loc[df['age']==3, ['age-group']] = 'toddler'
                  • 是我的索引(例如 unixtime 索引)并且我正在尝试添加一个尚未退出的时间戳(即新的时间戳读数)时,我无法让它工作.有什么想法吗?
                  • 是否可以根据索引和单元格值更改值?
                  • @BND 我不确定,但是您可以绕过这个明显的陷阱,而只需将索引列与具有相同值的另一列复制?简短的回答是我不知道。
                  • @yeliabsalohcin 见上述答案。
                  【解决方案13】:

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

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

                  对于测试,使用了包含 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))
                  

                  【讨论】:

                    【解决方案14】:

                    df.loc['c','x']=10 这将改变第 c 行的值,并且 第 x 列。

                    【讨论】:

                      【解决方案15】:

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

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

                      'result' 是一个带有 'weight' 列的 dataField

                      【讨论】:

                        【解决方案16】:

                        从 0.21.1 版本开始,您还可以使用 .at 方法。与这里提到的.loc 相比存在一些差异 - pandas .at versus .loc,但在单值替换时更快

                        【讨论】:

                          【解决方案17】:

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

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

                          【讨论】:

                            【解决方案18】:

                            你可以使用.iloc

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

                            【讨论】:

                            • 此方法似乎不支持多个值,例如df.iloc[[2:8], [0]] = [2,3,4,5,6,7] 方法 df.loc() 本机执行的操作。
                            【解决方案19】:

                            RukTech's answerdf.set_value('C', 'x', 10) 远远快于我在下面建议的选项。不过一直是slated for deprecation

                            接下来,recommended method is .iat/.at


                            为什么df.xs('C')['x']=10 不起作用:

                            df.xs('C')默认返回一个新的dataframewith a copy的数据,所以

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

                            只修改这个新的数据框。

                            df['x'] 返回df 数据框的视图,所以

                            df['x']['C'] = 10
                            

                            修改df 本身。

                            警告:有时很难预测操作是否返回副本或视图。出于这个原因docs recommend avoiding assignments with "chained indexing"


                            所以推荐的替代方案是

                            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
                            

                            【讨论】:

                            • API 中没有 df.x 这样的东西。你什么意思?
                            • @smci: 'x'df 中的列名。 df.x 返回一个Series,其值在x 列中。我将其更改为df['x'],因为此表示法适用于任何列名(与点表示法不同),我认为更清晰。
                            • 我知道,我还以为你在说df.x 是与df.xs, df.ix 并列的一些未知的新方法
                            • 根据维护者的说法,这不是设置值的推荐方式。请参阅stackoverflow.com/a/21287235/1579844 和我的回答。
                            • 在我的情况下,我有一个混合,即索引位置和列标签。我找到了让它工作的方法:df_temp.iat[0, df_temp.columns.get_loc('Cash')] = df_temp['Cash'].iloc[0] + start_val
                            【解决方案20】:

                            这是唯一对我有用的东西!

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

                            详细了解.lochere

                            【讨论】:

                            • 是否将.loc 替换为.iat/.at
                            • at 类似于loc,两者都提供基于标签的查找。如果您只需要在 DataFrame 或 Series 中获取或设置单个值,请使用 at。来自padas doc
                            • 当我的索引元素是数字时,这对我有用。
                            • 这不适用于数字和字符串索引的混合。
                            【解决方案21】:

                            我也在搜索这个主题,我整理了一种方法来遍历 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'])
                            

                            【讨论】:

                              【解决方案22】:

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

                              【讨论】:

                              • 欢迎来到 Stack Overflow!请考虑编辑您的帖子,以添加更多关于您的代码的作用以及它为什么会解决问题的解释。一个大部分只包含代码的答案(即使它正在工作)通常不会帮助 OP 理解他们的问题。如果只是猜测,还建议您不要发布答案。一个好的答案将有一个合理的理由说明它为什么可以解决 OP 的问题。
                              • 此代码不起作用(或不再起作用?),因为它没有替换值。
                              【解决方案23】:

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

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

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

                              见:

                              【讨论】:

                              猜你喜欢
                              • 2016-11-30
                              • 2015-10-12
                              • 1970-01-01
                              • 1970-01-01
                              • 2022-08-19
                              • 2020-08-07
                              • 2018-08-02
                              • 2017-12-22
                              • 2019-05-01
                              相关资源
                              最近更新 更多