在pandas.DataFrame的对角线上设置值

时间:2014-06-29 10:16:04

标签: python numpy pandas

我有一个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方式!?

6 个答案:

答案 0 :(得分:41)

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)

答案 1 :(得分:3)

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()

答案 2 :(得分:1)

此解决方案是矢量化且速度非常快,除非其他建议的解决方案适用于任何列名称和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

答案 3 :(得分:0)

这是一个对我有用的黑客:

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)

答案 4 :(得分:0)

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

答案 5 :(得分:0)

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