如何获取多索引数据帧的前两个索引的字典

Posted

技术标签:

【中文标题】如何获取多索引数据帧的前两个索引的字典【英文标题】:How to get dict of first two indexes for multi index data frame 【发布时间】:2018-12-10 18:22:17 【问题描述】:

我有一个如下所示的数据框

我想知道是否存在一种最快的方法来在 pandas 中创建一个可以保存如下数据的 python dict

table = 2: [4, 5, 6, 7, 8 ...], 4: [1, 2, 3, 4, ...]

这里的键是用户 ID,值是唯一的日期列表。

这可以在核心 python 的早期完成,但想知道是否有基于 pandas 或 numpy 的方法来快速计算。我需要一个快速的解决方案,当这个数据框变大时可以很好地扩展。

编辑 1:表演

所用时间:每个循环 14.3 毫秒 ± 134 微秒(7 次运行的平均值 ± 标准偏差,每次 100 次循环)

levels = pd.DataFrame(k: df.index.get_level_values(k) for k in range(2))

table = levels.drop_duplicates()\
              .groupby(0)[1].apply(list)\
              .to_dict()

print(table)

所用时间:每个循环 17.4 毫秒 ± 105 微秒(7 次运行的平均值 ± 标准偏差,每次 100 次循环)

res.reset_index().drop_duplicates(['user_id','date']).groupby('user_id')['date'].apply(list).to_dict()

所用时间:每个循环 294 毫秒 ± 12.8 毫秒(7 次运行的平均值 ± 标准偏差,每次 1 个循环)

a = k: list(pd.unique(list(zip(*g))[1])) 
     for k, g in groupby(df.index.values.tolist(), itemgetter(0))
print (a)

所用时间:每个循环 15 毫秒 ± 187 微秒(7 次运行的平均值 ± 标准偏差,每次 100 次循环)

pd.Series(res.index.get_level_values(1), index=res.index.get_level_values(0)).groupby(level=0).apply(set).to_dict()

编辑 2:再次进行基准测试

错误的结果

idx = df.index.droplevel(-1).drop_duplicates()
l1, l2 = idx.levels
mapping = defaultdict(list)
for i, j in zip(l1, l2):
    mapping[i].append(j)

改进的时序:每个循环 14.6 ms ± 58.8 µs(平均值 ± 标准偏差,7 次运行,每次 100 个循环)

a = k: list(set(list(zip(*g))[1])) 
     for k, g in groupby(res.index.values.tolist(), itemgetter(0))

【问题讨论】:

是否可以为纯 python 解决方案添加计时? 我认为Brad Solomon 解决方案和我的第一个,我真的很好奇是否更快。谢谢。 @jezrael 已在问题中更新。 【参考方案1】:

我认为如果需要更好的性能,请使用itertools.groupbyunique 来获得与原始数据相同顺序的返回列表。如果顺序不重要,请使用set:

df = pd.DataFrame('A':list('abcdef'),
                   'B':[4,5,4,5,5,4],
                   'C':[7,8,9,4,2,3],
                   'D':[1,3,5,7,1,0],
                   'E':[5,3,6,9,2,4],
                   'F':list('aaabbb')).set_index(['F','B', 'A'])

print (df)
       C  D  E
F B A         
a 4 a  7  1  5
  5 b  8  3  3
  4 c  9  5  6
b 5 d  4  7  9
    e  2  1  2
  4 f  3  0  4

from  itertools import groupby
from operator import itemgetter

a = k: list(set(list(zip(*g))[1])) 
     for k, g in groupby(df.index.values.tolist(), itemgetter(0))
print (a)
'a': [4, 5], 'b': [5, 4]

另一个熊猫解决方案:

d = df.reset_index().drop_duplicates(['F','B']).groupby('F')['B'].apply(list).to_dict()
print (d)
'a': [4, 5], 'b': [5, 4]

【讨论】:

set 的结果比pd.unique 好得多。你应该把它改回来 @MayukhSarkar - 谢谢。【参考方案2】:

来自Jz的数据

pd.Series(df.index.get_level_values(0),index=df.index.get_level_values(1)).groupby(level=0).apply(set).to_dict()
Out[92]: 4: 'a', 'b', 5: 'a', 'b'

如果只需要list,可以加apply(list) PS:个人认为不需要这一步

pd.Series(df.index.get_level_values(0),index=df.index.get_level_values(1)).groupby(level=0).apply(set).apply(list).to_dict()
Out[93]: 4: ['b', 'a'], 5: ['b', 'a']

【讨论】:

文,需要列表,而不是集合 @jezrael 已修复 ;-) 只有套装订购有问题,所以不确定是否有好的解决方案。 @jezrael 更改了顺序。 @jezrael 是的,订购并不重要。【参考方案3】:

这是使用drop_duplicates + groupby 的一种解决方案。

levels = pd.DataFrame(k: df.index.get_level_values(k) for k in range(2))

table = levels.drop_duplicates()\
              .groupby(0)[1].apply(list)\
              .to_dict()

print(table)

1: [2, 3], 2: [8, 9]

设置

df = pd.DataFrame([[1, 2, 0, 3], [1, 2, 1, 4], [1, 3, 1, 5],
                   [2, 8, 1, 3], [2, 8, 1, 4], [2, 9, 2, 5]],
                  columns=['col1', 'col2', 'col3', 'col4'])

df = df.set_index(['col1', 'col2', 'col3'])

print(df)

                col4
col1 col2 col3      
1    2    0        3
          1        4
     3    1        5
2    8    1        3
          1        4
     9    2        5

【讨论】:

以上是关于如何获取多索引数据帧的前两个索引的字典的主要内容,如果未能解决你的问题,请参考以下文章

将多索引数据帧的索引值提取为python中的简单列表

Pandas:根据条件为多索引数据帧的子集设置值的正确方法

从多索引数据帧中获取一个索引

如何在嵌套字典中按元素访问熊猫多索引?

删除每个索引的多索引熊猫数据帧的最低五个值

为啥我在多索引的索引中有空项目