【问题标题】:Set values on the diagonal of pandas.DataFrame在 pandas.DataFrame 的对角线上设置值
【发布时间】:2014-08-19 22:29:47
【问题描述】:

我有一个 pandas 数据框,我想将对角线设为 0

import numpy
import pandas

df = pandas.DataFrame(numpy.random.rand(5,5))
df

Out[6]:
     0           1           2           3               4
0    0.536596    0.674319    0.032815    0.908086    0.215334
1    0.735022    0.954506    0.889162    0.711610    0.415118
2    0.119985    0.979056    0.901891    0.687829    0.947549
3    0.186921    0.899178    0.296294    0.521104    0.638924
4    0.354053    0.060022    0.275224    0.635054    0.075738
5 rows × 5 columns

现在我想将对角线设置为 0:

for i in range(len(df.index)):
    for j in range(len(df.columns)):
        if i==j:
            df.loc[i,j] = 0
df
Out[9]:
     0           1           2           3           4
0    0.000000    0.674319    0.032815    0.908086    0.215334
1    0.735022    0.000000    0.889162    0.711610    0.415118
2    0.119985    0.979056    0.000000    0.687829    0.947549
3    0.186921    0.899178    0.296294    0.000000    0.638924
4    0.354053    0.060022    0.275224    0.635054    0.000000
5 rows × 5 columns

但一定有比这更 Pythonic 的方式!?

【问题讨论】:

    标签: python numpy pandas


    【解决方案1】:
    In [21]: df.values[[np.arange(df.shape[0])]*2] = 0
    
    In [22]: df
    Out[22]: 
              0         1         2         3         4
    0  0.000000  0.931374  0.604412  0.863842  0.280339
    1  0.531528  0.000000  0.641094  0.204686  0.997020
    2  0.137725  0.037867  0.000000  0.983432  0.458053
    3  0.594542  0.943542  0.826738  0.000000  0.753240
    4  0.357736  0.689262  0.014773  0.446046  0.000000
    

    请注意,这仅适用于 df 的行数与列数相同的情况。另一种适用于任意形状的方法是使用np.fill_diagonal

    In [36]: np.fill_diagonal(df.values, 0)
    

    【讨论】:

    • 这个解决方案确实适用于我建议的玩具数据,但我自己的更大矩阵失败了。
    • @PhilippSchwarz np.fill_diagonal 的一个好处是它可以在任何可以放入 RAM 中的 DataFrame 或矩阵上工作,而无需使用更多内存。 “填充”分配有效地在内存中“就地”发生,并保持 indexcolumns 以及 DataFrame 的其他属性完好无损。
    • 第一个解决方案现在抛出 FutureWarning,因为不推荐使用用于多维索引的非元组序列。
    【解决方案2】:

    unutbu's answer 中的两种方法都假定标签是不相关的(它们对基础值进行操作)。

    OP 代码与.loc 一起使用,因此是基于标签的(即在具有相同标签的行列中的单元格上放置 0,而不是在位于对角线上的单元格中 - 诚然,这在具体示例中无关紧要给定,其中标签只是位置)。

    由于需要“基于标签”的对角填充(使用描述不完整邻接矩阵的 DataFrame),我能想到的最简单的方法是:

    def pd_fill_diagonal(df, value):
        idces = df.index.intersection(df.columns)
        stacked = df.stack(dropna=False)
        stacked.update(pd.Series(value,
                                 index=pd.MultiIndex.from_arrays([idces,
                                                                  idces])))
        df.loc[:, :] = stacked.unstack()
    

    【讨论】:

      【解决方案3】:

      这是一个对我有用的技巧:

      def set_diag(self, values): 
          n = min(len(self.index), len(self.columns))
          self.values[[np.arange(n)] * 2] = values
      pd.DataFrame.set_diag = set_diag
      
      x = pd.DataFrame(np.random.randn(10, 5))
      x.set_diag(0)
      

      【讨论】:

      • 这与numpy.fill_diagonal 有何不同?此外,向现有库类型添加字段也不是一件好事。
      【解决方案4】:

      这个解决方案是矢量化的并且非常快,除非其他建议的解决方案适用于任何列名和 df 矩阵的大小。

      def pd_fill_diagonal(df_matrix, value=0): 
          mat = df_matrix.values
          n = mat.shape[0]
          mat[range(n), range(n)] = value
          return pd.DataFrame(mat)
      

      在 507 列和行的 Dataframe 上的性能

      % timeit pd_fill_diagonal(df, 0)
      

      1000 次循环,3 次中的最佳:每个循环 145 µs

      【讨论】:

        【解决方案5】:

        使用np.fill_diagonal(df.values, 1) 是最简单的,但您需要确保您的列都具有相同的数据类型,我混合了 np.float64 和 python 浮点数,它只会影响 numpy 值。要修复,您必须将所有内容都转换为 numpy。

        【讨论】:

          【解决方案6】:

          所有依赖于修改DataFrame.values 的答案都取决于未记录的行为。允许values 属性返回数据的副本,但修改values 的解决方案假设它返回一个视图。有时它确实会返回一个视图,但 pandas 文档并不能保证它何时会返回。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2022-12-17
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2019-02-23
            • 2017-01-04
            • 2014-12-23
            相关资源
            最近更新 更多