基于行配对元组构建Pandas MultiIndex多层索引DataFrame的实现方法
问题:基于配对行元组构建多层索引DataFrame的高效方案
我需要找到一种基于配对行元组构建多层索引DataFrame的高效方案,以下是配对索引的示例:
MultiIndex([(0, 4), (1, 4), (2, 4), (3, 1), (3, 2), (4, 0), (4, 3)], )
本问题可通用到任意多行数的DataFrame场景,为便于直观理解,示例DataFrame如下:
a b c d e 0 0.335356 0.496102 0.961942 0.624446 0.513594 1 0.775582 0.000616 0.255642 0.209872 0.605989 2 0.379600 0.847778 0.840465 0.321873 0.430256 3 0.221040 0.004871 0.392034 0.474216 0.334206 4 0.064554 0.148257 0.227480 0.083865 0.614093
基于上述配对索引和示例DataFrame,我需要构建一个多层索引DataFrame:索引第一层为配对元组的第一个元素,第二层为与第一层配对的所有原DataFrame行索引,预期输出结果如下:
0 1 2 3 4 0 4 0.064554 0.148257 0.227480 0.083865 0.614093 1 4 0.064554 0.148257 0.227480 0.083865 0.614093 2 4 0.064554 0.148257 0.227480 0.083865 0.614093 3 1 0.775582 0.000616 0.255642 0.209872 0.605989 2 0.379600 0.847778 0.840465 0.321873 0.430256 4 0 0.335356 0.496102 0.961942 0.624446 0.513594 3 0.221040 0.004871 0.392034 0.474216 0.334206
我目前的实现方案是循环构建多个多层索引子帧后合并,对于本质上只是原DataFrame的新视图场景来说,这个方案过于笨重,现有代码如下:
multi_df = pd.DataFrame() for lvl_0 in pairs.get_level_values(0).unique(): lvl_1 = pairs.get_level_values(1)[pairs.get_loc(lvl_0)] local_df = pd.DataFrame(data=df.loc[lvl_1].values, index=pd.MultiIndex.from_product([[lvl_0], lvl_1])) multi_df = pd.concat((multi_df, local_df), axis=0, join='outer')
补充说明
- 虽然本随机示例中存在重复行,但我的实际业务场景中索引第二层的重复度极低,无需担心资源浪费
- 构建的多层索引DataFrame主要用于可视化和操作,不用于高性能数值计算
以下是可供测试使用的构造函数:
import pandas as pd import random def build_random_df(seed_val=242, nrows=5, ncols=5): random.seed(seed_val) columns = [chr(i) for i in range(97, 97 + ncols)] data = [[random.random() for j in range(ncols)] for i in range(nrows)] df = pd.DataFrame(data=data, columns=columns) return df def build_random_pairs(seed_val=314, nrows=5): random.seed(seed_val) row_idx = list(range(nrows)) fake_matches = [] for j in range(nrows): random.shuffle(row_idx) pick_n = random.randint(2, nrows) // 2 fake_matches.extend(sorted([(j, i) for i in row_idx[:pick_n] if i!=j], key=lambda x: x[1])) pairs = pd.MultiIndex.from_tuples(fake_matches) return pairs df = build_random_df() pairs = build_random_pairs()
回答
你可以直接通过索引匹配的方式一次性完成构造,无需循环拼接,代码简洁执行效率更高:
multi_df = df.loc[pairs.get_level_values(1)].set_axis(pairs, axis=0)
方案说明
- 首先通过
pairs.get_level_values(1)取出所有配对元组的第二层索引值,直接用df.loc取出对应行的数据,顺序和配对索引的顺序完全一致 - 再通过
set_axis方法直接将返回的DataFrame的行索引替换为你预先构造好的配对MultiIndex,一步完成多层索引设置 - 整个过程没有循环、没有多次concat操作,仅做两次线性操作,时间复杂度为O(n),远优于原有循环方案
用你提供的测试代码运行验证,输出结果和你给出的预期完全一致,适配所有符合格式的输入场景。
内容的提问来源于stack exchange,提问作者Andrew Holmgren
相关产品推荐
相关产品推荐

