代码之家  ›  专栏  ›  技术社区  ›  BKS

计算数据帧中唯一合作作者的数量

  •  2
  • BKS  · 技术社区  · 5 年前

    我有一个非常大的数据框架,其中包含图书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)
    
    3 回复  |  直到 5 年前
        1
  •  4
  •   jezrael    5 年前

    第一次创造 set s每个组到新列,然后得到不同于 Author 列,删除空集依据 boolean indexing 最后将值展平到新的集合,以获得唯一的最后一个get长度:

    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)
    
        2
  •  1
  •   edesz    5 年前

    另一种方法

    第一组 BookID list 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]
    

    下一步,与主数据合并,打开 书呆子 ,获取每个作者的所有作者

    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 . 现在可以比较完整的作者列表( 作者X )每个独立/唯一作者( 委托人 ,方法如下

    1. Create dict whose keys are unique Author_y values (即唯一作者)和值是空白列表
    2. 循环访问dict中的每个键值对
    3. 使用从上面的步骤切片合并的数据帧 委托人 列;这将在dict键中为作者提供所有作者。
    4. 从slice获取所有作者的列表( 作者X )作为扁平列表
    5. extend blank list 具有 difference 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
    

    初始答案

    你试过了吗? groupby 具有 sum 聚集

    df.groupby(['Author'])['BookID'].sum()
    
        3
  •  0
  •   Scratch'N'Purr    5 年前

    我有另一个解决方案。

    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