将dict构造函数转换为Pandas多索引datafram

2024-10-03 02:47:03 发布

您现在位置:Python中文网/ 问答频道 /正文

我有很多数据,我想在Pandas数据框中构建。但是,我需要一个多索引格式。熊猫的多索引功能一直让我困惑,这次我也不知道该怎么办。在

我按照我想要的方式构建了这个结构,但是因为我的实际数据要大得多,所以我想用Pandas来代替。下面的代码是dict变量。请注意,原始数据有更多的标签和更多的行。在

其思想是原始数据包含索引为Task_n的任务的行,该任务由索引为Participant_n的参与者执行。每个都是一个段。即使原始数据没有这种区别,我还是想把它添加到我的数据帧中。换句话说:

Participant_n | Task_n | val | dur
----------------------------------
            1 |      1 |  12 |   2
            1 |      1 |   3 |   4
            1 |      1 |   4 |  12
            1 |      2 |  11 |  11
            1 |      2 |  34 |   4

上面的例子包含一个参与者,两个任务,分别有三个两个段(行)。在

在Python中,使用dict结构,如下所示:

^{pr2}$

在最后的结果中,我有一些额外的变量,比如:task_sum(参与者任务的总和)、all_sum(参与者所有动作的总和),还有prof,这是一个任意的布尔标志。结果dict看起来像这样(不是为了节省空间而美化的)。如果要查看,请在文本编辑器中以JSON或Python dict的形式打开并美化):

{'participant1': {'prof': 0, 'all_sum': {'val': 220, 'dur': 1240}, 'task1': {'seg1': {'val': 25, 'dur': 83}, 'task_sum': {'val': 38, 'dur': 1138}, 'seg2': {'val': 4, 'dur': 68}, 'seg3': {'val': 9, 'dur': 987}}, 'task2': {'seg1': {'val': 98, 'dur': 98}, 'task_sum': {'val': 182, 'dur': 102}, 'seg2': {'val': 84, 'dur': 4}}}, 'participant2': {'prof': 0, 'all_sum': {'val': 235, 'dur': 49}, 'task1': {'seg1': {'val': 9, 'dur': 21}, 'task_sum': {'val': 9, 'dur': 21}}, 'task2': {'seg1': {'val': 15, 'dur': 6}, 'task_sum': {'val': 218, 'dur': 16}, 'seg2': {'val': 185, 'dur': 6}, 'seg3': {'val': 18, 'dur': 4}}, 'task3': {'seg1': {'val': 8, 'dur': 12}, 'task_sum': {'val': 8, 'dur': 12}}}, 'participant3': {'prof': 0, 'all_sum': {'val': 31, 'dur': 214}, 'task1': {'seg1': {'val': 7, 'dur': 78}, 'task_sum': {'val': 19, 'dur': 166}, 'seg2': {'val': 12, 'dur': 88}}, 'task2': {'seg1': {'val': 12, 'dur': 48}, 'task_sum': {'val': 12, 'dur': 48}}}}

我不希望使用字典,而是使用一个pd.DataFrame作为结尾,其中包含多个索引,类似于下面的表示法或类似的索引。(为了简单起见,我只使用了索引,而不是task1或{})

Participant   Prof all_sum      Task    Task_sum     Seg   val   dur
                   val    dur           val    dur
====================================================================
participant1  0    220   1240      1     38   1138     1    25    83
                                                       2     4    68
                                                       3     9   987
                                   2    182    102     1    98    98
                                                       2    84     4
--------------------------------------------------------------------
participant2  0    235     49      1      9     21     1     9    21
                                   2    218     16     1    15     6
                                                       2   185     6
                                                       3    18     4
                                   3      8     12     1     8    12
--------------------------------------------------------------------
participant3  0     31    214      1     19    166     1     7    78
                                                       2    12    88
                                   2     12     48     1    12    48

这是熊猫可能的结构吗?如果没有,有哪些合理的替代方案?

我必须再次强调,实际上有更多的数据,可能还有更多的子层次。因此,解决方案必须灵活,有效。如果这使事情简单得多,我愿意只在一个轴上有多个索引,并将标题更改为:

Participant  Prof  all_sum_val  all_sum_dur  Task  Task_sum_val  Task_sum_dur  Seg   

我遇到的主要问题是,如果事先不知道维度,我不知道如何构建多索引df。我事先不知道会有多少任务或部分。所以我很确定我可以从我最初的dict方法中保留循环结构,我想接下来我必须将/concat附加到一个初始的空数据帧中,但是问题是这个结构必须是什么样子的。它不可能是一个简单的序列,因为它没有考虑多个索引。那怎么办?在

对于那些已经读过这篇文章并想尝试一下的人来说,我认为我的原始代码大部分可以被重用(循环和变量赋值),但是它必须是数据帧的访问器,而不是dict。这是一个导入方面:数据应该很容易用getter/setter读取,就像普通的DataFrame一样。E、 g.应该很容易获得参与者2、任务2、段2等的持续时间值。但是,获取数据的子集(例如prof === 0)应该没有问题。在


Tags: 数据taskvalall参与者结构dictsum
2条回答

我在数据表示方面也遇到了类似的问题,并为groupby提供了下面的helper函数和小计。在

通过这个过程,可以为任意数量的groupby列生成小计,但是输出数据的格式不同。不是将小计放在它们自己的列中,而是将每个小计添加到数据帧中一个额外的行。在

对于交互式数据探索和分析,我发现这非常有帮助,因为只需几行代码就可以得到小计

def get_subtotals(frame, columns, aggvalues, subtotal_level):

    if subtotal_level == 0:
        return frame.groupby(columns, as_index=False).agg(aggvalues)

    elif subtotal_level == len(columns):
        return pd.DataFrame(frame.agg(aggvalues)).transpose().assign(
            **{c: np.nan  for i, c in enumerate(columns)}
        )

    return frame.groupby(
        columns[:subtotal_level],
        as_index=False
    ).agg(aggvalues).assign(
        **{c: np.nan for i, c in enumerate(columns[subtotal_level:])}
    )

def groupby_with_subtotals(frame, columns, aggvalues, grand_totals=False, totals_position='last'):
    gt = 1 if grand_totals else 0
    out = pd.concat(   
        [get_subtotals(df, columns, aggvalues, i)
         for i in range(len(columns)+gt)]
     ).sort_values(columns, na_position=totals_position)
    out[columns] = out[columns].fillna('total')
    return out.set_index(columns)

正在从Gabriel A's answer恢复数据帧创建代码

^{pr2}$

首先需要添加seg

df['seg'] = df.groupby(['Participant_n', 'Task_n']).cumcount() + 1

然后我们可以像这样使用groupby_with_subtotals。另外,请注意,您可以将小计放在顶部,还可以通过传入grand_totals=True, totals_position='first'来包含总计

groupby_columns = ['Participant_n', 'Task_n', 'seg']
groupby_aggs = {'val': 'sum', 'dur': 'sum'}
aggdf = groupby_with_subtotals(df, groupby_columns, groupby_aggs)
aggdf
# outputs

                             dur  val
Participant_n Task_n seg
1             1.0    1.0      83   25
                     2.0      68    4
                     3.0     987    9
                     total  1138   38
              2.0    1.0      98   98
                     2.0       4   84
                     total   102  182
              total  total  1240  220
2             1.0    1.0      21    9
                     total    21    9
              2.0    1.0       6   15
                     2.0       6  185
                     3.0       4   18
                     total    16  218
              3.0    1.0      12    8
                     total    12    8
              total  total    49  235
3             1.0    1.0      78    7
                     2.0      88   12
                     total   166   19
              2.0    1.0      48   12
                     total    48   12
              total  total   214   31

这里,小计行用total标记,最左边的total表示小计级别。在

一旦创建了聚合数据帧,就可以使用loc访问小计。示例:

aggdf.loc[1,'total','total']
# outputs:
dur    1240
val     220
Name: (1, total, total), dtype: int64

我唯一的建议是把你字典里的东西都扔掉。所有这些代码都可以在Pandas中重新编写,无需太多努力。这可能也会加快转型进程,但需要一些时间。为了帮助你在这个过程中,我重写了你提供的部分。剩下的就看你了。在

import pandas as pd

cols = ['Participant_n', 'Task_n', 'val', 'dur']

data = [[1,1,25,83],
        [1,1,4,68],
        [1,1,9,987],
        [1,2,98,98],
        [1,2,84,4],
        [2,1,9,21],
        [2,2,15,6],
        [2,2,185,6],
        [2,2,18,4],
        [2,3,8,12],
        [3,1,7,78],
        [3,1,12,88],
        [3,2,12,48]]

df = pd.DataFrame(data, columns=cols)
df["Task Sum val"] = df.groupby(["Participant_n","Task_n"])["val"].transform("sum")
df["Task Sum dur"] = df.groupby(["Participant_n","Task_n"])["dur"].transform("sum")
df["seg"] =df.groupby(["Participant_n","Task_n"]).cumcount() + 1
df["All Sum val"] = df.groupby("Participant_n")["val"].transform("sum")
df["All Sum dur"] = df.groupby("Participant_n")["dur"].transform("sum")
df = df.set_index(["Participant_n","All Sum val","All Sum dur","Task_n","Task Sum val","Task Sum dur"])[["seg","val","dur"]]
df = df.sort_index()
df

输出

^{pr2}$

试着运行这个代码,让我知道你的想法。有任何问题请发表评论。在

相关问题 更多 >