【问题标题】:Construct data frame using groupby使用 groupby 构造数据框
【发布时间】:2020-08-14 13:14:27
【问题描述】:

我的数据框如下所示:

                date    id     pct_change
12355258    2010-07-28  60059   0.210210
12355265    2010-07-28  60060   0.592000
12355282    2010-07-29  60059   0.300273
12355307    2010-07-29  60060   0.481982
12355330    2010-07-28  60076   0.400729

我想用列“目标”、“源”、“权重”来编写它,其中: 'target' 和 'source' 都是 'id','weights' 计算 'target' 和 'source' 同时改变价格的天数。所以它看起来像:

target  source  weights
60059   60060   2
60059   60076   1   
60060   60076   1

我的目标是使用此数据框制作 networkx 图。

我尝试过使用 groupby

df.groupby(['date','id'])['id'].unique().value_counts()
df.groupby(['date','id'])['id'].count()

和 for 循环(很糟糕)。

我觉得我在 groupby 中错过了一小步,但不知道错过了什么。

感谢您的帮助。

【问题讨论】:

  • 12,177,535 行,24,800 个单独的 'id's
  • 如果你想要 2 个 id 的所有组合,那么有这么多单个 id 的问题,你会得到大约 3 亿行,我觉得 pandas 执行这样的操作会很麻烦,甚至存储“常规”计算机内存的结果可能太多
  • 我在数据集中有一个替代变量,它有 26 个类别而不是 24800 个。这可行吗?
  • 抱歉又打扰了...您知道为 'id' 做这件事的方法吗?使用“类别”变量,网络已完全连接,我无法进行任何进一步分析。
  • 只有 26 个类别而不是 25K 个 ID 才有意义,尤其是在有这么多日期的情况下,您将获得一个全连接的网络。老实说,我不确定计算机上有这么多数据是否可行。对不起

标签: python pandas dataframe graph networkx


【解决方案1】:

这个想法是首先使用pivto_table 来获取 True 如果 id 对每个日期都有 pct_change

#first pivot to get True if any value of id for a date
df_ = df.pivot_table(index='id', columns='date', values='pct_change', 
                     aggfunc=any, fill_value=False)
print(df_)
date  2010-07-28 2010-07-29
id                         
60059       True       True
60060       True       True
60076       True      False

然后您可以使用itertools 中的combination 创建所有可能的对,使用它们来选择行并使用& 运算符查看两者在同一日期的位置为True,沿列求和(获取权重列)。将此列分配给从两个组合列表创建的 Dataframe。

# get all combinations of ids
from itertools import combinations
a, b = map(list, zip(*combinations(df_.index, 2)))

res = (pd.DataFrame({'target':a, 'source':b})
         .assign(weigths=(df_.loc[a].to_numpy()
                          &df_.loc[b].to_numpy()
                         ).sum(axis=1))
      )
print(res)
   target  source  weigths
0   60059   60060        2
1   60059   60076        1
2   60060   60076        1

注意:不要忘记将pivot_table中的index='id'改成你的分类列名,否则你的电脑很有可能无法处理以下操作而崩溃

【讨论】:

    【解决方案2】:

    试试这个

    import pandas as pd, numpy as np
    
    ids = df.id.unique()
    WeightDf = pd.DataFrame(index=ids, columns=ids)
    WeightDf.loc[:, :] = 0
    
    def weigh(ID):
        IdDates =  set(df.loc[df.id==ID].date.to_list())
        for i in ids:
            WeightDf.at[ID, i] = len(set.intersection(set(df.loc[df.id==i].date.to_list()), IdDates))
            
    pd.Series(ids).apply(weigh)
    print(WeightDf)
    
    import itertools as itt
    result = pd.DataFrame(columns=['Id1', 'Id2', 'Weight'])
    for i1, i2 in itt.combinations(ids, 2):
        result = pd.concat([result, pd.DataFrame(data=[{'Id1':i1, 'Id2':i2,'Weight':WeightDf.loc[i1, i2]}])])
    
    print(result)
    
    

    【讨论】:

    • 这会创建一个矩阵,而不是真正的列目标、源、权重。
    • 谢谢!我想知道是否可以使用具有 24800 个观察值的“id”变量来做到这一点?我不知道这么大的数字是否可以使用组合。你觉得可能吗?
    • 这将是可能的,因为您可能有 24800 个观察值,但是,您将减少 unique IdS
    【解决方案3】:

    看到了这个用例的很多变化 - 生成组合

    import itertools
    
    df = pd.read_csv(io.StringIO("""                date    id     pct_change
    12355258    2010-07-28  60059   0.210210
    12355265    2010-07-28  60060   0.592000
    12355282    2010-07-29  60059   0.300273
    12355307    2010-07-29  60060   0.481982
    12355330    2010-07-28  60076   0.400729"""), sep="\s+")
    
    # generate combinations of two... edge case when a group has only one member
    # tuple of itself to itself
    dfx = (df.groupby('date').agg({"id": lambda s: list(itertools.combinations(list(s), 2))
                                   if len(list(s))>1 else [tuple(list(s)*2)]})
        .explode("id")
         .groupby("id").agg({"id":"count"})
         .rename(columns={"id":"weights"})
         .reset_index()
         .assign(target=lambda dfa: dfa["id"].apply(lambda s: s[0]),
               source=lambda dfa: dfa["id"].apply(lambda s: s[1]))
         .drop(columns="id")
    )
    
    print(dfx.to_string(index=False))
    

    输出

     weights  target  source
           2   60059   60060
           1   60059   60076
           1   60060   60076
    

    【讨论】:

      【解决方案4】:

      This SO link 最终对我的问题有了更快的回答,它适用于大量 id。它更接近我之前尝试使用的 groupby + value_counts。

      这里是代码,方便人们在未来使用:

      from itertools import combinations
      
      def combine(batch):
          """Combine all products within one batch into pairs"""
          return pd.Series(list(combinations(set(batch), 2)))
      
      edges = df.groupby('date')['id'].apply(combine).value_counts()
      
      c = ['source', 'target']
      L = edges.index.values.tolist()
      edges = pd.DataFrame(L, columns=c).join(edges.reset_index(drop=True))
      
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2019-10-31
        • 1970-01-01
        • 2021-12-19
        • 2018-12-08
        • 2019-11-08
        • 1970-01-01
        相关资源
        最近更新 更多