我想将节点和距离矩阵转换为 table,其中包含节点 1、节点 2 的列以及它们之间的距离

I want to convert a matrix of nodes and distances into a table with columns for node1, node2 and the distance between them

我正在处理 csv 文件,其中包含每个节点的 header 和每个节点的对应行,每个数据字段对应于两个节点之间的差异,如下所示:

Blank 4 38 71 90 94 ... 8545
4 0 1280 1762 1406 1589 ... 1017
38 1280 0 681 202 385 ... 1433
71 1762 681 0 503 0 ... 0
90 1406 202 503 0 0 ... 1559
94 1589 385 0 0 0 ... 1742
.. .. ... ... ... ... 0 ...
8545 1017 1433 0 1559 1742 ... 0

(在此上下文中 0 表示没有连接,因为它们是相同的节点或它们未连接)

我想更改布局,使其类似于:

Node1 Node2 Distance
4 4 0
4 38 1280
4 71 1762

以此类推,包括所有节点及其连接

是否有 pythonic 方式来执行此操作?

我想要的是使用 Pandas stack,例如

import numpy as np
import pandas as pd

A = np.abs(1 - np.corrcoef( np.random.random( size=(8,8) ))) * 100

A 只是有一些随机的或多或少相似的数据。

我们现在用

创建一个 df
df = pd.DataFrame(A, columns = [ "node_" + str(i) for i in range(A.shape[0])])
         node_0      node_1      node_2      node_3      node_4      node_5  \
0  1.110223e-14  177.640823   94.528739  102.894884   75.716800   16.959874   
1  1.776408e+02    0.000000  100.594166   69.032398  106.857164  180.845071   
2  9.452874e+01  100.594166    0.000000  143.128873  154.447302  114.711230   
3  1.028949e+02   69.032398  143.128873    0.000000   25.162778  129.170641   
4  7.571680e+01  106.857164  154.447302   25.162778    0.000000   74.135700   
5  1.695987e+01  180.845071  114.711230  129.170641   74.135700    0.000000   
6  1.424585e+02   74.925019   70.156519   70.914395  125.433304  171.546764   
7  8.862326e+01   67.220779  139.488270   61.680713   58.115007   98.620041   

你可以df.stack()

df_stacked = df.stack()

您可以使用 pd.DataFrame( df_stacked )

将其设为数据框

在这里使用 DataFrame.stack 以获得更好的性能,例如 pd.concat 和列表理解:

df2 = df.stack().rename_axis(['Node1','Node2']).reset_index(name='Distance')
print (df2.head(15))
    Node1 Node2  Distance
0       4     4         0
1       4    38      1280
2       4    71      1762
3       4    90      1406
4       4    94      1589
5       4  8545      1017
6      38     4      1280
7      38    38         0
8      38    71       681
9      38    90       202
10     38    94       385
11     38  8545      1433
12     71     4      1762
13     71    38       681
14     71    71         0

另一个想法是使用 numpy:

c = np.tile(df.columns, len(df))
i = np.repeat(df.index, len(df.columns))
v = np.ravel(df)

df2 = pd.DataFrame({'Node1':i, 'Node2':c, 'Distance':v})

性能:

#6k rows
df = pd.concat([df] * 1000, ignore_index=True)
# print(df)

In [37]: %timeit df2 = pd.DataFrame({'Node1':np.repeat(df.index, len(df.columns)), 'Node2':np.tile(df.columns, len(df)), 'Distance':np.ravel(df)})
1.77 ms ± 42.2 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

In [38]: %timeit df.stack().rename_axis(['Node1','Node2']).reset_index(name='Distance')
4.39 ms ± 475 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

In [39]: %timeit pd.concat([pd.DataFrame([[i, j, df.at[i, j]]], columns=['Node1', 'Node2', 'Distance']) for i in df.index   for j in df.columns], ignore_index=True)
31.6 s ± 1.22 s per loop (mean ± std. dev. of 7 runs, 1 loop each)

对于大多数图形操作,如果您使用图形库,您会发现巨大的优势。在这种情况下,您可以轻松地从数据框创建一个图形并将这些距离添加为 edge weights.

然后对于所需的输出,您只需要在这种情况下检索图边的权重,这可以使用 NetworkX 轻松完成:

import networkx as nx

G = nx.from_pandas_adjacency(df)
weights = nx.get_edge_attributes(G, 'weight')
print(weights)
{(94, 4): 1589,
 (94, 38): 385,
 (4, 38): 1280,
 (4, 71): 1762,
 (4, 90): 1406,
 (38, 71): 681,
 (38, 90): 202,
 (71, 90): 503}

或者,如果您希望将结果作为数据框:

(pd.DataFrame(weights.keys(), columns=['node1', 'node2'])
    .assign(Distance=weights.values()))

   node1  node2  Distance
0     94      4      1589
1     94     38       385
2      4     38      1280
3      4     71      1762
4      4     90      1406
5     38     71       681
6     38     90       202
7     71     90       503