首页 > 解决方案 > 在 pandas groupby 对象上重新运行 agg 会修改原始数据框

问题描述

我正在尝试聚合一堆字典,将字符串键和二进制数列表作为值,存储在熊猫数据框中。像这样:

发生此问题的示例数据框:

df4 
subject task_nr probe_dict
0   2   1   {'1_3': [0, 0, 1, 1]}
1   2   0   {'1_3': [0, 0, 1, 1]}
2   2   0   {'1_3': [0, 0, 1, 1]}

对于聚合,我只想在数据帧不同行中的字典中的键相等时将列表连接在一起。在聚合之前,我运行一个 groupby 操作,主题和 task_nr 作为“groupers”。我的聚合函数有一个参数,让我只从加入列表中获取最后 n 个条目。我想用不同的参数重新运行它。但是,当我多次在从数据帧派生的 groupby 对象上重新运行 .agg 时,原始数据帧会发生变化。下面是我的 groupby 和聚合代码。我已经包含了一些深拷贝来试图避免这个问题,但它似乎没有帮助。原因似乎是原始数据框正在被修改。有没有办法避免这种情况?为什么会这样?

grouped= cp.deepcopy(df4).groupby(['subject','task_nr'])  #group results by task and subject
mptd_only30 = partial(merge_probe_trial_dicts,only_last=30)  #consider only last 30 probe trials
mergag = cp.deepcopy(grouped['probe_dict']).agg(mptd_only30)  #aggregate data across sessions using function above

聚合函数。

def merge_probe_trial_dicts(x,only_last=None):
    """ 
    Function passed as argument to result of pandas groupby in
    order to merge the dicts by subject and task
    
    Arguments:
    =========================
    x: what is passed by agg
    
    only_last (int): only look at the last n probe trials for each transition
    """
    
    out = {}
    for d in x.dropna():
        for k,v in d.items():
            if k not in out.keys():
                out[k] = v
            else:
                out[k].extend(v)
                
    if only_last:
        for k,v in out.items():
            out[k] = v[-only_last:]
    return out

标签: pythonpandaspandas-groupby

解决方案


问题是它会merge_probe_trial_dicts改变原来的列表df4而不是创建一个新列表。

只需添加.copy()如下,你应该很好。

def merge_probe_trial_dicts(x, only_last=None):
    out = {}
    for d in x.dropna():
        for k, v in d.items():
            if k not in out.keys():
                out[k] = v.copy()  # This is the trick
            else:
                out[k].extend(v)
                
    if only_last:
        for k,v in out.items():
            out[k] = v[-only_last:]
    return out

附加技巧:将额外的参数agg传递给聚合函数。所以你可以直接这样做:

df4.groupby(["subject", "task_nr"]).agg(merge_probe_trial_dicts, only_last=30)

代替:

mptd_only30 = partial(merge_probe_trial_dicts, only_last=30)
df4.groupby(["subject", "task_nr"]).agg(mptd_only30)

另一个:if k not in out.keys()效率很低,因为您out.keys()每次都重新计算。我建议defaultdict改用更简单、更高效的代码:

from collections import defaultdict

def merge_probe_trial_dicts(x, only_last=None):
    out = defaultdict(list)
    for d in x.dropna():
        for key, value in d.items():
            out[key] += value
    if only_last is not None:
        for key, value in out.items():
            out[key] = value[-only_last:]
    return out

推荐阅读