在 Pandas 中将字典转换为对称/距离矩阵的最有效方法

Posted

技术标签:

【中文标题】在 Pandas 中将字典转换为对称/距离矩阵的最有效方法【英文标题】:Most efficient way to turn dictionary into symmetric/distance matrix in Pandas 【发布时间】:2016-12-24 13:19:43 【问题描述】:

我正在对具有奇怪距离度量的事物进行成对距离。我有一本像(key_A, key_B):distance_value 这样的字典,我想制作一个对称的pd.DataFrame,就像一个距离矩阵。

最有效的方法是什么?我找到了一种方法,但这似乎不是最好的方法。 NumPyPandas 中是否有执行此类操作的内容?或者只是一种更快的方法?我的方式是1.46 ms per loop

np.random.seed(0)
D_pair_value = dict()
for pair in itertools.combinations(list("ABCD"),2):
    D_pair_value[pair] = np.random.randint(0,5)
D_pair_value
# ('A', 'B'): 4,
#  ('A', 'C'): 0,
#  ('A', 'D'): 3,
#  ('B', 'C'): 3,
#  ('B', 'D'): 3,
#  ('C', 'D'): 1
D_nested_dict = defaultdict(dict)
for (p,q), value in D_pair_value.items():
    D_nested_dict[p][q] = value
    D_nested_dict[q][p] = value

# Fill diagonal with zeros
DF = pd.DataFrame(D_nested_dict)
np.fill_diagonal(DF.values, 0)
DF

【问题讨论】:

【参考方案1】:

您可以使用scipy.spatial.distance.squareform,它将距离计算向量(即[d(A,B), d(A,C), ..., d(C,D)])转换为您要查找的距离矩阵。

方法一:存储在列表中的距离

如果您按顺序计算距离,例如在您的示例代码和我的示例距离向量中,我会避​​免使用字典并将结果存储在列表中,然后执行以下操作:

from scipy.spatial.distance import squareform

df = pd.DataFrame(squareform(dist_list), index=list('ABCD'), columns=list('ABCD'))

方法2:存储在字典中的距离

如果您正在无序地计算事物并且需要字典,您只需要获取一个正确排序的距离向量:

from scipy.spatial.distance import squareform

dist_list = [dist[1] for dist in sorted(D_pair_value.items())]
df = pd.DataFrame(squareform(dist_list), index=list('ABCD'), columns=list('ABCD'))

方法3:存储在排序字典中的距离

如果需要字典,请注意有一个名为 sortedcontainers 的包,其中有一个 SortedDict,它基本上可以为您解决排序问题。要使用它,您只需将D_pair_value 初始化为SortedDict() 而不是dict。使用您的示例设置:

from scipy.spatial.distance import squareform
from sortedcontainers import SortedDict

np.random.seed(0)
D_pair_value = SortedDict()
for pair in itertools.combinations(list("ABCD"),2):
    D_pair_value[pair] = np.random.randint(0,5)

df = pd.DataFrame(squareform(D_pair_value.values()), index=list('ABCD'), columns=list('ABCD'))

上述任何方法的结果输出:

     A    B    C    D
A  0.0  4.0  0.0  3.0
B  4.0  0.0  3.0  3.0
C  0.0  3.0  0.0  1.0
D  3.0  3.0  1.0  0.0

【讨论】:

谢谢!我今天学到了一些新东西:scipy.spatial.distance.squareform 这里是一个很好的解决方案。谢谢,@MaxU【参考方案2】:

给定一个键(单个字符)和距离的字典,这是一个基于 NumPy 的方法 -

def dict2frame(D_pair_value):
    # Extract keys and values
    k = np.array(D_pair_value.keys())
    v = np.array(D_pair_value.values())

    # Get row, col indices from keys
    idx = (np.fromstring(k.tobytes(),dtype=np.uint8)-65).reshape(-1,2)

    # Setup output array and using row,col indices set values from v
    N = idx.max()+1
    out = np.zeros((N,N),dtype=v.dtype)
    out[idx[:,0],idx[:,1]] = v
    out[idx[:,1],idx[:,0]] = v

    header = list("".join([chr(item) for item in np.arange(N)+65]))
    return pd.DataFrame(out,index=header, columns=header)

示例运行 -

In [166]: D_pair_value
Out[166]: 
('A', 'B'): 4,
 ('A', 'C'): 0,
 ('A', 'D'): 3,
 ('B', 'C'): 3,
 ('B', 'D'): 3,
 ('C', 'D'): 1

In [167]: dict2frame(D_pair_value)
Out[167]: 
   A  B  C  D
A  0  4  0  3
B  4  0  3  3
C  0  3  0  1
D  3  3  1  0

【讨论】:

以上是关于在 Pandas 中将字典转换为对称/距离矩阵的最有效方法的主要内容,如果未能解决你的问题,请参考以下文章

晶胞中的距离矩阵(考虑对称性)

将上三角矩阵转换为 R 中的对称矩阵

在 Python 中将年份转换为 12/31/xxxx 格式

在 Pandas 数据框中将 float64 数组中的所有单元格展平为 int

在 PySpark 中将 Python Dict 转换为稀疏 RDD 或 DF

在 DataFrame 中将 Pandas 系列转换为 DateTime