【问题标题】:Calculating number of unique co-authors in a dataframe计算数据框中唯一共同作者的数量
【发布时间】:2019-02-12 09:52:48
【问题描述】:

我有一个非常大的数据框,其中包含书籍 ID 和共同创作每本书的人的姓名。我想计算每位作者在其整个职业生涯中曾与多少“独特”的合著者合作。

例如:

BookID   Author
  1         John
  1         Alex
  1         Jenna
  2         John
  2         Alex
  3         John
  4         Alex
  4         Mary
  4         Max

预期的结果是(括号之间的内容不是必需的,但我把它放在那里是为了更好地证明,我只想要数字):

Author     Num_Unique_CoAuthors
 John           2                    (Alex and Jenna)
 Alex           4                    (John, Jenna, Mary and Max)
 Jenna          2                    (Alex and John)
 Mary           2                    (Alex and Max)
 Max            2                    (Alex and Mary)

【问题讨论】:

    标签: python pandas python-2.7 dataframe


    【解决方案1】:

    首先为每个组创建sets 到新列,然后获取与Author 列的差异,通过boolean indexing 删除空集,最后将值展平到新集以获得唯一的最后获取长度:

    df = df.join(df.groupby('BookID')['Author'].apply(set).rename('new'), 'BookID')
    
    df['new'] = [b - set([a]) for a, b in zip(df['Author'], df['new'])]
    
    df = (df[df['new'].astype(bool)].groupby('Author')['new']
              .apply(lambda x: tuple(set([z for y in x for z in y])))
              .to_frame())
    
    df.insert(0, 'Num_Unique_CoAuthors', df['new'].str.len())
    print (df)
            Num_Unique_CoAuthors                       new
    Author                                                
    Alex                       4  (Max, John, Jenna, Mary)
    Jenna                      2              (John, Alex)
    John                       2             (Jenna, Alex)
    Mary                       2               (Max, Alex)
    Max                        2              (Mary, Alex)
    

    【讨论】:

    • 完美运行!
    • 如何编辑它以包含零共同作者的作者?在这种情况下,我想包含空集。
    • @BKS - 尝试删除将 df[df['new'].astype(bool)] 更改为 df
    【解决方案2】:

    另一种方法

    第一组BookIDlist all authors per book (i.e. list all authors per group)

    combos = df.groupby('BookID').agg(lambda x: list(x)).reset_index(drop=False)
    print(combos)
       BookID               Author
    0       1  [John, Alex, Jenna]
    1       2         [John, Alex]
    2       3               [John]
    3       4    [Alex, Mary, Max]
    

    接下来,与 BookID 上的主要数据合并,以获取每个作者的所有作者

    merged = combos.merge(df, how='inner', on='BookID')
    print(merged)
       BookID             Author_x Author_y
    0       1  [John, Alex, Jenna]     John
    1       1  [John, Alex, Jenna]     Alex
    2       1  [John, Alex, Jenna]    Jenna
    3       2         [John, Alex]     John
    4       2         [John, Alex]     Alex
    5       3               [John]     John
    6       4    [Alex, Mary, Max]     Alex
    7       4    [Alex, Mary, Max]     Mary
    8       4    [Alex, Mary, Max]      Max
    

    Author_x 是完整的作者列表,包括Author_y。现在可以使用以下方法将完整作者列表 (Author_x) 与每个个人/唯一作者 (Author_y) 进行比较

    1. Create dict whose keys are unique Author_y values(即唯一作者)和值是空白列表
    2. 遍历dict中的每个键值对
    3. 使用Author_y 列从上述步骤中分割合并的数据帧;这会在 dict 键中为作者提供所有作者
    4. 从切片中,获取所有作者的列表 (Author_x) 作为扁平列表
    5. extend blank listdifference between flattened list (all authors) and dict key
    d = {auth:[] for auth in df['Author'].unique()}
    for k,v in d.items():
        all_auths = merged[merged['Author_y']==k]['Author_x'].values.tolist()
        auths = [coauths for nested in all_auths for coauths in nested]
        v.extend(list(set(auths) - set([k])))
    

    最后,放入DataFrame,统计每行的非空值

    cnames = ['coauth'+str(k) for k in range(1,len(d))]
    df_summary = pd.DataFrame.from_dict(d, orient='index', columns=cnames)
    df_summary['Num_Unique_CoAuthors'] = df_summary.shape[1] - df_summary.isna().sum(axis=1)
    print(df_summary)
      author coauth1 coauth2 coauth3 coauth4  Num_Unique_CoAuthors
    0   John    Alex   Jenna    None    None                     2
    1   Alex     Max    John    Mary   Jenna                     4
    2  Jenna    John    Alex    None    None                     2
    3   Mary     Max    Alex    None    None                     2
    4    Max    Alex    Mary    None    None                     2
    

    扩展数据案例

    如果主要数据包含单个作者(即没有任何共同作者),则此方法为该行打印零

    这是一个添加到数据中的虚拟行,只有一个作者

    print(df)
       BookID Author
    0       1   John
    1       1   Alex
    2       1  Jenna
    3       2   John
    4       2   Alex
    5       3   John
    6       4   Alex
    7       4   Mary
    8       4    Max
    9       5    Tom
    

    这是输出

      author coauth1 coauth2 coauth3 coauth4  Num_Unique_CoAuthors
    0   John   Jenna    Alex    None    None                     2
    1   Alex    Mary    John   Jenna     Max                     4
    2  Jenna    John    Alex    None    None                     2
    3   Mary     Max    Alex    None    None                     2
    4    Max    Mary    Alex    None    None                     2
    5    Tom    None    None    None    None                     0
    

    初步回答

    您是否尝试过 groupbysum 聚合

    df.groupby(['Author'])['BookID'].sum()
    

    【讨论】:

    • 这不是告诉我们他写了多少本书吗?不是他有多少合著者?
    • Book ID 下的数字也只是 ID,也可以是字符串。不应将其视为整数,因此使用 sum 没有意义。 :)
    • @BKS,感谢您的 cmets。我已经更新了这个答案。
    【解决方案3】:

    我有一个替代解决方案。

    1. 加入BookID
    2. 使用crosstab 创建邻接矩阵
    3. 沿行计算计数,不包括行的作者。

    >>> df_merge = df.merge(df, on='BookID')
    >>> ctdf = pd.crosstab(df_merge.Author_x, df_merge.Author_y, aggfunc='max', values=[1] * len(df_merge)).fillna(0)
    >>> ctdf
    Author_y  Alex  Jenna  John  Mary  Max
    Author_x
    Alex       1.0    1.0   1.0   1.0  1.0
    Jenna      1.0    1.0   1.0   0.0  0.0
    John       1.0    1.0   1.0   0.0  0.0
    Mary       1.0    0.0   0.0   1.0  1.0
    Max        1.0    0.0   0.0   1.0  1.0
    >>> ctdf.apply(lambda x: sum([*x]) - 1)
    Author_y
    Alex     4.0
    Jenna    2.0
    John     2.0
    Mary     2.0
    Max      2.0
    dtype: float64
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-10-02
      • 2023-04-06
      • 1970-01-01
      • 1970-01-01
      • 2018-03-11
      相关资源
      最近更新 更多