我是Pandas的新手,并且无法找到解决以下问题的简洁解决方案。
假设我有一系列基于对称(距离)矩阵的数据,从以下系列中删除重复项的最有效方法是什么?
from pandas import DataFrame
df = DataFrame([[0, 1, 2],
[1, 0, 3],
[2, 3, 0]],
index=['a', 'b', 'c'],
columns=['a', 'b', 'c'])
ser = df.stack()
ser
a a 0
b 1
c 2
b a 1
b 0
c 3
c a 2
b 3
c 0
我想要做的是删除重复对,因为矩阵是对称的。输出应该如下所示
a a 0
b 1
c 2
b b 0
c 3
c c 0
答案 0 :(得分:3)
以下代码的运行速度比当前接受的答案要快:
import numpy as np
def dm_to_series1(df):
df = df.astype(float)
df.values[np.triu_indices_from(df, k=1)] = np.nan
return df.unstack().dropna()
DataFrame
的类型转换为float
,以便可以使用np.nan
对元素进行清空。实际上,距离矩阵可能已经存储了浮点数,因此这一步骤可能不是绝对必要的。上三角形(不包括对角线)是无效的,并且在将DataFrame
转换为Series
后会删除这些条目。
我调整了当前接受的解决方案,以便比较运行时。请注意,我更新它以使用集合而不是列表来加快运行时间:
def dm_to_series2(df):
ser = df.stack()
seen = set()
for tup in ser.index.tolist():
if tup[::-1] in seen:
continue
seen.add(tup)
return ser[seen]
在原始示例数据集上测试两个解决方案:
import pandas as pd
df = pd.DataFrame([[0, 1, 2],
[1, 0, 3],
[2, 3, 0]],
index=['a', 'b', 'c'],
columns=['a', 'b', 'c'])
我的解决方案:
In [4]: %timeit dm_to_series1(df)
1000 loops, best of 3: 538 µs per loop
@Marius'解决方案:
In [5]: %timeit dm_to_series2(df)
1000 loops, best of 3: 816 µs per loop
我还通过使用scikit-bio的skbio.stats.distance.randdm
函数随机生成50x50矩阵并将其转换为DataFrame
来测试更大的距离矩阵:
from skbio.stats.distance import randdm
big_dm = randdm(50)
big_df = pd.DataFrame(big_dm.data, index=big_dm.ids, columns=big_dm.ids)
我的解决方案:
In [7]: %timeit dm_to_series1(big_df)
1000 loops, best of 3: 649 µs per loop
@Marius'解决方案:
In [8]: %timeit dm_to_series2(big_df)
100 loops, best of 3: 3.61 ms per loop
请注意,我的解决方案可能不像@Marius解决方案那样具有内存效率,因为我正在创建输入DataFrame
的副本并对其进行修改。如果可以修改输入DataFrame
,则可以使用就地DataFrame
操作更新代码以提高内存效率。
注意:我的解决方案受到this SO question中答案的启发。
答案 1 :(得分:1)
我不确定这是多么有效,但这有效:
seen = []
for tup in ser.index.tolist():
if tup[::-1] in seen:
continue
seen.append(tup)
ser_reduced = ser[seen]
ser_reduced
Out[9]:
a a 0
b 1
c 2
b b 0
c 3
c c 0
dtype: int64