为何numpy.fill_diagonal()无法修改DataFrame的对角线值?
为什么numpy.fill_diagonal()无法修改DataFrame的对角线值?
我尝试用numpy.fill_diagonal()将DataFrame的对角线值修改为20000,但运行代码后对角线值仍为0。当前使用numpy版本2.1.0、pandas版本2.2.2,不需要提供for循环修改的方案,只想知道失败的原因。
复现代码
import numpy as np import pandas as pd nodes = ['A','B','C','D','E','F','G','H'] df_cap = pd.DataFrame(None, index=nodes, columns=nodes) edges = { ('A', 'B'): 809, ('A', 'C'): 184, ('A', 'D'): 440, ('B', 'C'): 134, ('B', 'E'): 277, ('B', 'F'): 138, ('C', 'D'): 194, ('C', 'F'): 144, ('C', 'G'): 139, ('D', 'E'): 190, ('D', 'G'): 284, ('E', 'F'): 100, ('E', 'H'): 281,('F', 'H'): 922,('G', 'F'): 123,('G', 'H'): 232 } for i in nodes: for j in nodes: if (i,j) in edges: df_cap.loc[i,j] = edges[(i,j)] nodes = nodes + ["Dummy"] df_cap = df_cap.astype(float).fillna(0) df_cap.loc["Dummy", :] = 0 df_cap.loc[:, "Dummy"] = 0 np.fill_diagonal(df_cap.values, 20000) df_cap.loc["A", "Dummy"] = 20000 df_cap.loc["Dummy", "H"] = 20000 df_cap
问题原因
核心问题是添加Dummy行/列后,df_cap.values返回的是DataFrame底层数据的副本,而非引用:
- 当执行
df_cap.loc["Dummy", :] = 0和df_cap.loc[:, "Dummy"] = 0时,pandas需要扩展原DataFrame的维度,此时会重新分配内存空间,原有的连续数组被打散,df_cap.values不再指向DataFrame的底层内存,而是返回一个临时副本。 np.fill_diagonal()修改的是这个副本,修改完成后副本被丢弃,原DataFrame的数据完全没变化,所以对角线还是0。- 可以通过打印
df_cap.values.flags.c_contiguous验证:添加Dummy后这个值会变成False,说明数组不是连续内存块,此时values返回的就是副本。
可行的修正方案(无需for循环)
方案1:确保操作连续数组
先将数组转换为连续内存块,再调用fill_diagonal:
# 替换原代码中的np.fill_diagonal行 arr = np.ascontiguousarray(df_cap.values) np.fill_diagonal(arr, 20000) df_cap[:] = arr
方案2:直接通过索引赋值
利用numpy的索引直接修改底层数据,不受内存连续性影响:
# 替换原代码中的np.fill_diagonal行 idx = np.arange(len(df_cap)) df_cap.values[idx, idx] = 20000
内容的提问来源于stack exchange,提问作者Joe
相关产品推荐
相关产品推荐

