Python Pandas-使用索引中的成对组合将数据帧转换为方形矩阵

Python Pandas-使用索引中的成对组合将数据帧转换为方形矩阵,python,pandas,optimization,Python,Pandas,Optimization,我正在把一个数据帧转换成一个方阵。数据框有一个索引,只有一列带有浮点数。我需要做的是计算所有索引对,每对取两个相关列值的平均值。因此,通常的枢轴函数只是解决方案的一部分 目前,该函数的估计复杂度为O(n^2),这并不好,因为我必须处理较大的输入,每次处理几百行的数据帧。还有其他更快的方法吗 输入示例(为简单起见,此处使用整数): 更新:转换逻辑 对于示例中的输入数据帧: 0 0 3 1 4 2 5 我做了以下几件事(但不是说这是最好的方式): 获取所有索引对:(0,1)、(1,

我正在把一个数据帧转换成一个方阵。数据框有一个索引,只有一列带有浮点数。我需要做的是计算所有索引对,每对取两个相关列值的平均值。因此,通常的枢轴函数只是解决方案的一部分

目前,该函数的估计复杂度为O(n^2),这并不好,因为我必须处理较大的输入,每次处理几百行的数据帧。还有其他更快的方法吗

输入示例(为简单起见,此处使用整数):

更新:转换逻辑

对于示例中的输入数据帧:

   0

0  3
1  4
2  5
我做了以下几件事(但不是说这是最好的方式):

  • 获取所有索引对:(0,1)、(1,2)、(0,2)
  • 对于每一对,计算其值的平均值:(0,1):3.5,(1,2):4.5,(0,2):4.0
  • 使用每对中的索引作为列和行标识符,在对角线上使用零(如所需输出所示),构建一个平方对称矩阵
代码位于将表转换为矩阵()中

期望输出:

    0   1   2

0   0.0 3.5 4.0
1   3.5 0.0 4.5
2   4.0 4.5 0.0
目前的执行情况:

import pandas as pd
from itertools import combinations 
import time
import string
import random


def turn_table_into_square_matrix(original_dataframe):

    # get all pairs of indices 
    index_pairs = list(combinations(list(original_dataframe.index),2))

    rows_for_final_dataframe = []

    # collect new data frame row by row - the time consuming part
    for pair in index_pairs:
        subset_original_dataframe = original_dataframe[original_dataframe.index.isin(list(pair))]
        rows_for_final_dataframe.append([pair[0], pair[1], subset_original_dataframe[0].mean()])
        rows_for_final_dataframe.append([pair[1], pair[0], subset_original_dataframe[0].mean()])

    final_dataframe = pd.DataFrame(rows_for_final_dataframe)

    final_dataframe.columns = ["from", "to", "weight"]
    final_dataframe_pivot = final_dataframe.pivot(index="from", columns="to", values="weight")
    final_dataframe_pivot = final_dataframe_pivot.fillna(0)

    return final_dataframe_pivot
为性能计时的代码:

for size in range(50, 600, 100):

    index = range(size)
    values = random.sample(range(0, 1000), size)
    example = pd.DataFrame(values, index)

    print ("dataframe size", example.shape)

    start_time = time.time()
    turn_table_into_square_matrix(example)
    print ("conversion time:", time.time()-start_time)
计时结果:

dataframe size (50, 1)
conversion time: 0.5455281734466553

dataframe size (150, 1)
conversion time: 5.001590013504028

dataframe size (250, 1)
conversion time: 14.562285900115967

dataframe size (350, 1)
conversion time: 31.168692111968994

dataframe size (450, 1)
conversion time: 49.07127499580383

dataframe size (550, 1)
conversion time: 78.73740792274475

因此,一个包含50行的数据帧转换只需半秒,而包含550行的数据帧(长11倍)转换需要79秒(长11^2倍)。这个问题有更快的解决方案吗?

我认为pandas带来了很多开销(例如,
original\u dataframe[original\u dataframe.index.isin(list(pair))]
对于它的实际功能来说似乎太贵了)。我还没有对它进行测试,但我认为当您使用numpy阵列时,可以节省大量的执行时间。如果需要,您仍然可以在末尾将其馈送到pandas.DataFrame

比如(我只是简单地描述一下我的意思):


我认为在这种计算上不可能比O(n^2)做得更好。正如@piiipmatz所建议的,您应该尝试使用numpy做所有事情,然后将结果放入
pd.DataFrame
。您的问题听起来像是
numpy.add.at
的一个很好的用例

下面是一个简单的例子

import numpy as np
import itertools

# your original array
x = np.array([1, 4, 8, 99, 77, 23, 4, 45])
n = len(x)
# all pairs of indices in x
a, b = zip(*list(itertools.product(range(n), range(n))))
a, b = np.array(a), np.array(b)
# resulting matrix
result = np.zeros(shape=(n, n))

np.add.at(result, [a, b], (x[a] + x[b]) / 2.0)

print(result)
# [[  1.    2.5   4.5  50.   39.   12.    2.5  23. ]
# [  2.5   4.    6.   51.5  40.5  13.5   4.   24.5]
# [  4.5   6.    8.   53.5  42.5  15.5   6.   26.5]
# [ 50.   51.5  53.5  99.   88.   61.   51.5  72. ]
# [ 39.   40.5  42.5  88.   77.   50.   40.5  61. ]
# [ 12.   13.5  15.5  61.   50.   23.   13.5  34. ]
# [  2.5   4.    6.   51.5  40.5  13.5   4.   24.5]
# [ 23.   24.5  26.5  72.   61.   34.   24.5  45. ]]
这个怎么样:

df.pivot(index='i', columns = 'j', values = 'empty')
为此,您需要通过添加新的索引列(两次)来稍微欺骗标准的
pivot
,因为它不允许在pivot中两次使用相同的参数,并为值添加空列:

df['i']=df.index
df['j']=df.index
df['empty']=None

就这样。

您能解释一下示例输入是如何转换为输出的吗?当然,谢谢您的建议-它使示例更加清晰。我已经更新了postoh哇,这是如此快!输入550个条目只需0.35秒!谢谢:)这是numpy非常强大的功能!非常欢迎:)这段代码几乎和@valentis的一样快——550个条目只需0.4秒。非常感谢。
df.pivot(index='i', columns = 'j', values = 'empty')
df['i']=df.index
df['j']=df.index
df['empty']=None