我正在将数据帧转换为方阵。数据框有一个索引,只有一列有浮点数。我需要做的是计算所有索引对,并为每对索取两个相关列值的平均值。因此,通常的枢轴功能只是解决方案的一部分。
目前,该函数具有估计的复杂度O(n ^ 2),这是不好的,因为我必须使用具有一次数百行的数据帧的较大输入。我可以采取另一种更快的方法吗?
示例输入(为简单起见,这里使用整数):
df = pd.DataFrame([3, 4, 5])
更新:转换逻辑
对于示例中的输入数据框:
0
0 3
1 4
2 5
我做了以下事情(并没有声称这是最好的方式):
代码位于turn_table_into_square_matrix()。
期望的输出:
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倍)。有没有更快的解决方案来解决这个问题?
答案 0 :(得分:2)
我不认为有可能比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. ]]
答案 1 :(得分:1)
我认为你有很多来自熊猫的开销(即original_dataframe[original_dataframe.index.isin(list(pair))]
看起来实在太贵了)。我还没有对它进行过测试,但我认为当你使用numpy数组时可以节省大量的执行时间。如果需要,您仍然可以在最后将它提供给pandas.DataFrame。
像(仅仅是为了描绘我的意思):
original_array = original_dataframe.as_matrix().ravel()
n = len(original_array)
final_matrix = np.zeros((n,n))
for pair in pairs:
final_matrix[pair[0], pair[1]] = 0.5*(original_array[pair[0]]+original_array[pair[1]])
答案 2 :(得分:0)
如何?
df.pivot(index='i', columns = 'j', values = 'empty')
为此,您需要通过添加新的索引列(两次)来欺骗标准pivot
,因为它不允许在数据透视表中重复两次相同的参数并为值添加空列:
df['i']=df.index
df['j']=df.index
df['empty']=None
就是这样。