Pandas:使用部分字符串匹配聚合不同数据帧的几列

Posted

技术标签:

【中文标题】Pandas:使用部分字符串匹配聚合不同数据帧的几列【英文标题】:Pandas: aggregate several columns of different dataframes using partial string matching 【发布时间】:2021-10-16 22:26:41 【问题描述】:

编辑:我现在已经能够解决它

我想使用第二个数据帧聚合来自一个数据帧的观察结果,并且需要考虑权重和部分字符串匹配。

我制作了一个数据框,显示某类专利在一年内的申请频率,如下所示:

    IPC_four    count_year_IPC_four
Year        
1955    A01B    9
1955    G01P    3
1955    B23D    4
1955    G01R    28
1955    B23C    1
...     ...     ...
1990    A21D    1
1990    G01F    17
1990    G06K    8
1990    F21P    0
1990    H05K    23

23868 rows × 2 columns

(我也有一个包含所有单个专利的数据框,但我使用pd.crosstab()pd.unstack() 以稍微不雅的方式汇总了该专利)

我想使用我的第二个表来聚合这些,这是一个从 IPC 类到行业分支的对应矩阵。 重要的是,由于某些 IPC 类别对应于多个经济部门,因此有一个“因素”列,需要将第一列的实例与该列相乘。

df2:
        Branch  Code    Factor
0       20      E21     1.0
1       21      E01     1.0
2       21      E02     1.0
3       2       A21     1.0
4       2       A22     1.0
...     ...     ...     ...
210     7       G10     1.0
211     7       A47B    1.0
212     7       A47C    1.0
213     7       A47D    1.0
214     7       A47F    1.0

215 rows × 3 columns

新的数据框应该是我来自 df1 的数字与相应的年份和行业分支的加权和。它应该如下所示:

        Branch  count_Branch_IPC_four
Year        
1955    2       9
1955    3       3
1955    4       4
1955    5       28
1955    6       1
...     ...     ...
1990    2       1
1990    3       17
1990    4       8
1990    5       0
1990    6       23

576 rows x 3 columns

在下文中,我已经说明了我的思考过程,我认为这些步骤是填充我最终想要获得的数据框的一行所必需的:

    由于所有内容都需要按年份汇总,因此我需要首先获得 df1[Year] 的值(我认为?)

    我想查看我的第二个数据帧中的df2[Branch],并考虑df2[Code] 的哪些值对应于该分支。

    然后我想取df2[Code] 中与df2[Branch] 的值相对应的每个值,并检查df1[IPC_four] 的哪些值(在给定的年份)适合这个。

    代码有时是三位数,有时是四位数。如果它只有三位数,我需要使用某种部分字符串匹配来检查它。

    由于代码表示类和子类,与较长的代码相比,较短的代码只会选择更大的所有子类集合。因此,为什么我想做部分字符串匹配。如果这让我失望了,我也可以考虑添加额外的行,可能会有四位数的字符串排列,但这也不容易

    对于每个合适的df1[IPC_four],(可能不止一个)我想创建所有相应df1[count_year_IPC_four] 值的总和。 count_year_IPC_four 的总和必须乘以 Factor,这对应于我们一直在使用的 Code。 (来自 DF2)。

当然,迭代有点令人困惑,但我最困惑的是如何通过本质上使用来自不同数据帧的行值来完成这些复杂的操作。

我尝试通过创建数据框字典,按年份和分支拆分数据框。

df = pd.read_pickle("count_year_IPCfour.pkl")
b = pd.read_pickle("branchlist.pkl")

branches = (2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 20, 21)
year_range = range(1955, 1990)
year_collection = 
branch_collection = 


for year in year_range:
    new_df = df.loc[df['Year'] == year ]
    year_collection[year] = pd.DataFrame(new_df, columns = [ "Branch", "IPC_four", "Code", "count_year_IPC_four", "Factor"])

for branch in branches:
    new_df = b.loc[b['Branch'] == branch ]
    branch_collection[branch] = pd.DataFrame(new_df, columns = [ "Branch", "IPC_four", "Code", "count_year_IPC_four", "Factor"])

但现在我仍然不知道该怎么做,因为从根本上说,我无法理解在沿行、跨列和跨数据帧移动方面所需的操作。

注意:由于重复(即一些IPC_four 代码属于多个Branch)和部分字符串匹配问题(一些Codes 是三位数,有些是四位数)我不能只是执行pd.join() 操作 - 我尝试过,但重复项意味着 df 增长了很多。

我应该如何实现这个?如果有人已经回答了这个问题,或者这可以分成多个部分,我很乐意看看这些。 非常感谢。

【问题讨论】:

【参考方案1】:

我设法解决了。

自从我在 Jupyter 中完成我的工作以来,我通常使用非常短的代码 sn-ps 来完成一些小任务,这就是为什么您会在这里看到这种非常奇怪的代码结构。关于这一点,我想问:继续使用您的数据框名称是一种好习惯吗?还是应该覆盖之前的?

    创建一个变量 = 1,您可以使用 .groupby(Year, Branch).sum() 使用
df = pd.read_pickle("DEPATISclean002.pkl")
df["IPC_four"].astype("str")
df["empty"] = 1 #I can just sum 1 for each year and each sector
df.rename(columns="Anmeldejahr" : "Year", inplace = True)
df = df[["Year", "IPC_four", "count_inventor", "empty"]]
df = df.groupby(["Year", "IPC_four"]).sum() #now I have a df in which I have both the absolute number of patents and absolute no. of inventors - this now needs to be 
df["inv_year_IPC_four"] = df["count_inventor"]/df["empty"] #in addition I have the avg. no of Inventors per patent per IPC_four per year
df.reset_index(inplace = True)
df.to_pickle("Aggregation001.pkl")
    现在,我们需要根据要分配给分支的 IPC 代码的长度创建新的分支列表
df = pd.read_pickle("branchlist.pkl")
df["Code_len"] = df["Code"].str.len()
df = df[["Branch", "Code", "Factor", "Code_len"]]
df.to_pickle("branchlist002.pkl")
##make a len=4 one
df = df[df.Code_len == 4]
df["IPC_four"] = df["Code"].astype("str")
df = df[["Branch", "IPC_four", "Factor"]]
df.to_pickle("branchlist_len4.pkl")

##make a len=3 one
df2 = pd.read_pickle("branchlist002.pkl")
df2 = df2[["Branch", "Code", "Factor", "Code_len"]]
df2 = df2[df2.Code_len == 3]
df2["IPC_three"] = df2["Code"].astype("str")
df2 = df2[["Branch", "IPC_three", "Factor"]]
df2.to_pickle("branchlist_len3.pkl")

使用 4 位代码,计数非常容易

df1 = pd.read_pickle("Aggregation001.pkl")
df2 = pd.read_pickle("branchlist_len4.pkl")
df3 = pd.merge(df1, df2[df2.IPC_four.isin(df1.IPC_four)], how= "left", on="IPC_four")
df4 = df3[df3["Factor"].notnull()
          
#now to create a weighted list of inventors and patents by year and sector

df4["Branch_weighted"] = df4["empty"]*df4["Factor"]
df4["count_inventor_weighted"] = df4["count_inventor"]*df4["Factor"]
df5 = df4.groupby(["Year", "Branch"]).sum()
df5["inv_year_Branch"] = df5["count_inventor_weighted"]/df5["Branch_weighted"]
df5.reset_index(inplace = True)
df6 = df5[["Year", "Branch", "Branch_weighted", "inv_year_Branch", "count_inventor_weighted"]]
df6.to_pickle("Agg4.pkl")

然后我尝试了 len=3 的东西,结果在 Medium 上找到一篇关于它的文章后发现它相对简单:https://outline.com/VpFnwf

df1 = pd.read_pickle("Aggregation001.pkl")
df2 = pd.read_pickle("branchlist_len3.pkl")
df2 = df2[["IPC_three", "Branch", "Factor"]]

#making sure everything is a string
df1["IPC_four"].astype("str")
df2["IPC_three"].astype("str")

#creating a thing to join on 
df1["join"] = 1
df2["join"] = 1

#merging as a cartesian product
df3 = df1.merge(df2, on = "join").drop("join", axis = 1)
df2.drop('join', axis=1, inplace=True)
df3['match'] = df3.apply(lambda x : x.IPC_four.find(x.IPC_three), axis=1).ge(0)
df3
df4 = df3[df3.match == True]
df4

df4["Branch_weighted"] = df4["empty"]*df4["Factor"]
df4["count_inventor_weighted"] = df4["count_inventor"]*df4["Factor"]
df4.to_pickle("weighted_len3.pkl")
df5 = df4.groupby(["Year", "Branch"]).sum()
df5["inv_year_Branch"] = df5["count_inventor_weighted"]/df5["Branch_weighted"]
df5.reset_index(inplace = True)
df6 = df5[["Year", "Branch", "Branch_weighted", "inv_year_Branch", "count_inventor_weighted"]]
df6.to_pickle("Agg3.pkl")

最后只需要pd.groupby.sum()

df1 = pd.read_pickle("Agg3.pkl")
df2 = pd.read_pickle("Agg4.pkl")
df3 = df1.append(df2)
df4 = df3.groupby(["Year", "Branch"]).sum()
df4["inv_year_Branch"] = df4["count_inventor_weighted"]/df4["Branch_weighted"]
df4.reset_index(inplace = True)
df4.to_pickle("Patents_per_Branch_per_Year.pkl")

我真的希望其他人能喜欢这个答案! 另外,如果您知道一些更简单或更好的方法,请分享!我敢肯定这种转换是相当普遍的,所以如果能在 *** 上找到它就很好了。

【讨论】:

以上是关于Pandas:使用部分字符串匹配聚合不同数据帧的几列的主要内容,如果未能解决你的问题,请参考以下文章

Pandas:自定义 WMAPE 函数聚合函数到多列而没有 for 循环?

通过对不同列使用不同聚合的 pandas 数据框进行分组

使用带有 python/pandas 的 dict 理解与 str.contains 进行部分字符串匹配

如何使用 pandas 聚合大型 DataFrame 中的多个列?

使用行上的部分字符串匹配返回DataFrame项pandas python [重复]

Pandas str.contains 用于部分字符串的精确匹配