在 Pandas 中将字典转换为 symmetric/distance 矩阵的最有效方法
Most efficient way to turn dictionary into symmetric/distance matrix in Pandas
我正在为具有奇怪距离度量的东西做成对距离。我有一本像 {(key_A, key_B):distance_value}
这样的字典,我想制作一个像距离矩阵一样的对称 pd.DataFrame
。
最有效的方法是什么?我找到了一种方法,但它似乎不是最好的方法。 NumPy
或 Pandas
中是否有执行此类操作的内容?或者只是一种更快的方法?我的方法是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
您可以使用 scipy.spatial.distance.squareform
,它将距离计算向量(即 [d(A,B), d(A,C), ..., d(C,D)]
)转换为您要查找的距离矩阵。
方法 1:存储在列表中的距离
如果你按顺序计算你的距离,就像在你的示例代码和我的示例距离向量中一样,我会避免使用字典并只将结果存储在列表中,然后执行如下操作:
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
which has a 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
给定键(单个字符)和距离的字典,这里是基于 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
我正在为具有奇怪距离度量的东西做成对距离。我有一本像 {(key_A, key_B):distance_value}
这样的字典,我想制作一个像距离矩阵一样的对称 pd.DataFrame
。
最有效的方法是什么?我找到了一种方法,但它似乎不是最好的方法。 NumPy
或 Pandas
中是否有执行此类操作的内容?或者只是一种更快的方法?我的方法是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
您可以使用 scipy.spatial.distance.squareform
,它将距离计算向量(即 [d(A,B), d(A,C), ..., d(C,D)]
)转换为您要查找的距离矩阵。
方法 1:存储在列表中的距离
如果你按顺序计算你的距离,就像在你的示例代码和我的示例距离向量中一样,我会避免使用字典并只将结果存储在列表中,然后执行如下操作:
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
which has a 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
给定键(单个字符)和距离的字典,这里是基于 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