【问题标题】:Sum values of parent nodes in a dataframe数据框中父节点的总和值
【发布时间】:2019-06-16 04:49:51
【问题描述】:

我有一个 df(下面的一小部分)。我正在尝试为当前行及其所有子行添加额外的 tw0 列 rolled_doc_cntrolled_doc_cnt_all ,其中包含 doc_cnt/doc_cnt_all 的总和。在下面非常有限的行中,df.at[0,'rolled_doc_cnt'] = 1317 & df.at[0,'rolled_doc_cnt_all'] = 3540

    SYMBOL  level   not-allocatable additional-only doc_cnt doc_cnt_all parent
0   A   2   True    False   0   0   
1   A01 4   True    False   0   0   A
2   A01B    5   True    False   0   0   A01
3   A01B   1/00 7   False   False   198 244 A01B
4   A01B   1/02 8   False   False   230 538 A01B   1/00
5   A01B   1/022    9   False   False   83  238 A01B   1/02
6   A01B   1/024    9   False   False   28  63  A01B   1/02
7   A01B   1/026    9   False   False   100 120 A01B   1/02
8   A01B   1/028    9   False   False   27  82  A01B   1/02
9   A01B   1/04 9   False   False   29  54  A01B   1/02
10  A01B   1/06 8   False   False   78  508 A01B   1/00
11  A01B   1/065    9   False   False   118 150 A01B   1/06
12  A01B   1/08 9   False   False   71  326 A01B   1/06
13  A01B   1/10 9   False   False   14  30  A01B   1/06
14  A01B   1/12 9   False   False   24  86  A01B   1/06
15  A01B   1/14 9   False   False   44  131 A01B   1/06
16  A01B   1/16 8   False   False   159 518 A01B   1/00
17  A01B   1/165    9   False   False   50  114 A01B   1/16
18  A01B   1/18 9   False   False   64  338 A01B   1/16

我在创建parenthere 时得到了一些帮助。

def GetParent():
    # level      0  1  2  3  4  5  6  7  8  9  10 11  12  13  14  15  16  17  18  19, 20
    hierarchy = [0, 0, 0, 0, 2, 4, 0, 5, 7, 8, 9,
                 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
    parent = ['']*len(hierarchy)

    def func(row):
        # print(row)
        symbol, level = row[['SYMBOL', 'level']]

        parent_level = hierarchy[level]
        parent_symbol = parent[parent_level]

        parent[level] = symbol

        return pd.Series([parent_symbol], index=['parent'])

    return func


# create a column with the parents
st = time()
parents = dfa.apply(GetParent(), axis=1)
dfa = pd.concat([dfa, parents], axis=1)
print((time()-st)/60, 'minutes elapsed')

我尝试在 spyder 中调试此代码,以便在推进 df 行时看到列表 parent 的变化,但我无法弄清楚如何在不跳转到 pandas 的情况下跳转到函数 GetParent()函数apply()。跳入apply() 最终导致我出现递归错误。

我尝试对GetParents() 进行一些修改,以跟踪每个级别的每个符号的文档计数,但后来我意识到我正在跟踪父节点的文档计数,但不是孩子们。那么,使用上面的 df,我如何能够创建类似于以下 df 的内容?

    SYMBOL  level   not-allocatable additional-only doc_cnt doc_cnt_all parent  rolled_doc_cnt  rolled_doc_cnt_all
0   A   2   TRUE    FALSE   0   0       1317    3540
1   A01 4   TRUE    FALSE   0   0   A   1317    3540
2   A01B    5   TRUE    FALSE   0   0   A01 1317    3540
3   A01B   1/00 7   FALSE   FALSE   198 244 A01B    1317    3540
4   A01B   1/02 8   FALSE   FALSE   230 538 A01B   1/00 497 1095
5   A01B   1/022    9   FALSE   FALSE   83  238 A01B   1/02 83  238
6   A01B   1/024    9   FALSE   FALSE   28  63  A01B   1/02 28  63
7   A01B   1/026    9   FALSE   FALSE   100 120 A01B   1/02 100 120
8   A01B   1/028    9   FALSE   FALSE   27  82  A01B   1/02 27  82
9   A01B   1/04 9   FALSE   FALSE   29  54  A01B   1/02 29  54
10  A01B   1/06 8   FALSE   FALSE   78  508 A01B   1/00 349 1231
11  A01B   1/065    9   FALSE   FALSE   118 150 A01B   1/06 118 150
12  A01B   1/08 9   FALSE   FALSE   71  326 A01B   1/06 71  326
13  A01B   1/10 9   FALSE   FALSE   14  30  A01B   1/06 14  30
14  A01B   1/12 9   FALSE   FALSE   24  86  A01B   1/06 24  86
15  A01B   1/14 9   FALSE   FALSE   44  131 A01B   1/06 44  131
16  A01B   1/16 8   FALSE   FALSE   159 518 A01B   1/00 273 970
17  A01B   1/165    9   FALSE   FALSE   50  114 A01B   1/16 50  114
18  A01B   1/18 9   FALSE   FALSE   64  338 A01B   1/16 64  338

也请随时告诉我,我尝试这样做的方式不是最佳的,并建议另一种方式

【问题讨论】:

    标签: python-3.x pandas


    【解决方案1】:

    获取父符号的函数(GetParent)可以简化一点:

    def GetParent():
        hierarchy = [0, 0, 0, 0, 2, 4, 0, 5, 7, 8, 9, 10,
                     11, 12, 13, 14, 15, 16, 17, 18, 19]
        parent = ['']*len(hierarchy)
        def func(row):
            symbol, level = row[['SYMBOL', 'level']]
            parent_level = hierarchy[level]
            parent_symbol = parent[parent_level]
            parent[level] = symbol
            return parent_symbol
        return func
    

    即它只返回parent_symbol(不是Series),然后它可以是 应用直接创建目标列:

    dfa['parent'] = dfa.apply(GetParent(), axis=1)
    

    我在您的示例数据上尝试了此功能,结果没有错误

    请注意,GetParent 能够处理多达 20 个级别的层次结构, 所以也许你的错误的根源是你的完整数据有更多 层级?

    您没有写任何发生此错误的地方以及错误消息是什么。 从此时开始寻找错误原因。

    另一个提示:从在您的某些初始部分运行此代码开始 数据(例如前半部分)。目标是找到源行,在其上 发生错误。然后非常彻底看看这一行(有点 之前)。这可能会给你一些关于错误原因的线索。

    编辑

    我想出了一个无论层次结构深度如何都应该有效的解决方案。

    def GetParent():
        par = {0: ''}
        def func(row):
            symbol, level = row[['SYMBOL', 'level']]
            parLevel = level - 2 if level in [2, 4, 7] else level - 1
            parSym = par[parLevel]
            par[level] = symbol
                return parSym
        return func
    

    请注意,“缺少层次结构级别”的问题已解决:

    parLevel = level - 2 if level in [2, 4, 7] else level - 1
    

    返回 level - 2 级别“就在”缺失的级别之后 level - 1 用于所有其他级别。

    父符号保存在“内部”字典中,所以没有问题 列表索引超出范围。

    同上apply这个函数,直接创建目标列。

    编辑 2

    我看到你设法计算了祖先,那么现在如何计算总和 感兴趣的列。

    Pandas中树结构的操作而言, 我在页面的 StackOverflow 中发现了一段有趣的代码:

    Hierarchical data: efficiently build a list of every descendant for each node

    从那里复制 list_ancestors 函数和其他 2 个函数 由它使用,即trace_nodesnumpy_col_inner_many_to_one_join

    这里不再重复这段代码。

    然后运行:

    links = list_ancestors(dfa[['SYMBOL', 'parent']].values)
    

    list_ancestors 函数生成一个DataFrame,带有descendantancestor 列,包括祖先和后代之间的“链接” 任何深度(到目前为止,您只有直接后代)。

    要添加 SYMBOLdoc_cntdoc_cnt_all 列,请运行:

    links2 = pd.merge(links, dfa[['SYMBOL', 'doc_cnt', 'doc_cnt_all']],
        left_on='descendant', right_on='SYMBOL', copy=False)
    

    现在开始求和:

    s1 = links2.groupby('ancestor')['doc_cnt', 'doc_cnt_all'].apply(sum)
    s2 = dfa[['SYMBOL', 'doc_cnt', 'doc_cnt_all']].set_index('SYMBOL')
    s3 = pd.concat([s1, s2]).groupby(level=0).apply(sum)
    

    一点解释:

    • s1 包含所有(感兴趣的两列的)总和 后代(任何深度),但没有“自己的”价值观 对于这些列。
    • s2 反过来只包含“缺失”的自身值。
    • s3 将这些值相加。

    剩下要做的只有两个步骤:

    • 将列名更改为目标名,
    • 加入 dfas3

    代码如下:

    s3.columns=['rolled_doc_cnt', 'rolled_doc_cnt_all']
    dfa.join(s3, on='SYMBOL')
    

    对于您的测试数据,我得到了预期的结果。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-12-26
      • 2020-05-19
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多