【问题标题】:How to merge by a column of collection using Python Pandas?如何使用 Python Pandas 按集合列合并?
【发布时间】:2017-07-11 01:40:05
【问题描述】:

我有 2 个 Stack Overflow 问题列表,A 组和 B 组。两者都有两列,Id 和 Tag。例如:

|Id        |Tag
| -------- | --------------------------------------------
|2         |c#,winforms,type-conversion,decimal,opacity

对于 A 组中的每个问题,我需要在 B 组中找到与 A 组中的问题至少有一个重叠标签的所有匹配问题,而与标签的位置无关。例如,这些问题都应该是匹配问题:

|Id        |Tag
|----------|---------------------------
|3         |c#
|4         |winforms,type-conversion
|5         |winforms,c#

我的第一个想法是将变量 Tag 转换为 set 变量并使用 Pandas 合并,因为 set 忽略了位置。但是,Pandas 似乎不允许将集合变量作为关键变量。所以我现在使用 for 循环来搜索 B 组。但它非常慢,因为我在 B 组中有 1300 万个观察值。

我的问题是: 1. Python中有没有其他方法可以通过集合的列进行合并,并且可以知道重叠标签的数量? 2、如何提高for循环搜索的效率?

【问题讨论】:

  • 你是如何得到 1300 万个堆栈溢出问题的? API 永远不会让您做得足够快。
  • 我从 Stack Exchange 数据转储 @COLDSPEED 下载
  • 好的。下一个问题。 Tag 是一串逗号分隔的标签类型还是标签列表?
  • 标签是一串括号分隔的标签。但我可以使用正则表达式将其转换为标签列表。我现在正在阅读您的答案,这似乎很有希望。非常感谢!

标签: python pandas dataframe merge set


【解决方案1】:

这可以使用df.joindf.groupby 来实现。

这是我正在使用的设置:

df1 = pd.DataFrame({ 'Id' : [2], 'Tag' : [['c#', 'winforms', 'type-conversion', 'decimal', 'opacity']]}) 

   Id                                                Tag
0   2  [c#, winforms, type-conversion, decimal, opacity]

df2 = pd.DataFrame({ 'Id' : [3, 4, 5], 'Tag' : [['c#'], ['winforms', 'type-conversion'], ['winforms', 'c#']]})  

   Id                          Tag
0   3                         [c#]
1   4  [winforms, type-conversion]
2   5               [winforms, c#]

让我们展平两个数据框中的右列。 This 帮助:

In [2331]: from itertools import chain

In [2332]: def flatten(df):
      ...:     return pd.DataFrame({"Id": np.repeat(df.Id.values, df.Tag.str.len()),
      ...:                          "Tag": list(chain.from_iterable(df.Tag))})
      ...: 

In [2333]: df1 = flatten(df1)

In [2334]: df2 = flatten(df2)

In [2335]: df1.head()
Out[2335]: 
   Id              Tag
0   2               c#
1   2         winforms
2   2  type-conversion
3   2          decimal
4   2          opacity

df2 也是如此,它也是扁平的。

现在是魔法。我们将在Tag 列上执行join,然后在加入IDs 上的groupby 以查找重叠标签的数量。

In [2337]: df1.merge(df2, on='Tag').groupby(['Id_x', 'Id_y']).count().reset_index()
Out[2337]: 
   Id_x  Id_y  Tag
0     2     3    1
1     2     4    2
2     2     5    2

输出显示每对标签以及重叠标签的数量。没有重叠的对被groupby 过滤掉。

df.count 计算重叠标签,df.reset_index 只是美化输出,因为groupby 将分组列指定为索引,所以我们重置它。

要查看匹配的标签,您将对以上内容稍作修改:

In [2359]: df1.merge(df2, on='Tag').groupby(['Id_x', 'Id_y'])['Tag'].apply(list).reset_index()
Out[2359]: 
   Id_x  Id_y                          Tag
0     2     3                         [c#]
1     2     4  [winforms, type-conversion]
2     2     5               [c#, winforms]

要过滤掉 1 次重叠,请将 df.query 调用链接到第一个表达式:

In [2367]: df1.merge(df2, on='Tag').groupby(['Id_x', 'Id_y']).count().reset_index().query('Tag > 1')
Out[2367]: 
   Id_x  Id_y  Tag
1     2     4    2
2     2     5    2 

【讨论】:

  • 有什么方法可以同时显示重叠标签?
  • @COLDSPEED 对不起,我之前忘了提。加入时可以限制重叠标签的数量吗?因为一两个重叠标签的问题太多,会使输出表太长。所以我想限制只有 3 个或更多重叠标签的问题。谢谢!
  • @Xiaomeng 使用.query,因为我在我的帖子中进行了编辑。从这里开始,请不要在 cmets 中堆积请求和详细信息。它阻碍了未来读者的可读性。请一次查询一个。如果这解决了您的问题,请将其标记为已接受并提出新问题。我不会再回答 cmets 中的任何问题了。
  • @COLDSPEED 我无法展平 df2,它给了我错误“根据规则‘安全’将数组数据从 dtype('float64') 转换为 dtype('int64')”。从 csv 文件导入 df2 时,我尝试使用 dtype=int64。但我仍然遇到同样的错误。但是,使用您的函数将 df1 展平没有问题。 df1 和 df2 之间的唯一区别是 df2 的观测值比 df1 多得多。并且 df2.Id 列可能有更长的 int。
  • @Xiaomeng 肯定会问一个新问题。我想我不知道如何解决内存错误!
【解决方案2】:
  • 第一步列出所有标签
  • 第二步创建每个标签的二进制表示,即用bit 1或0来表示是否有标签
  • 第 3 步要找到任何 ID 共享相同的标签,您可以调用一个简单的应用函数来解码您的二进制表示。

就处理速度而言,应该没问题。但是,如果标签数量过多,则可能存在内存问题。如果您只需要为一个 Id 查找具有相同标签的问题,我建议您编写一个简单的函数并调用 df.apply。如果您需要检查大量 ID 并查找具有相同标签的问题,我会说上面的方法会更好。

(打算留下评论,但没有足够的声誉......叹息)

【讨论】:

  • 标签至少有一万个。如果我使用二进制表示,我将有数千列来指示问题的标签。这会有效吗?
  • 如果超过1万个标签,我认为你不应该使用我上面提出的方法。我认为Coldspeed的方法会好很多。
猜你喜欢
  • 1970-01-01
  • 2020-07-11
  • 1970-01-01
  • 2019-03-15
  • 2018-09-07
  • 1970-01-01
  • 2017-11-27
  • 2013-08-01
相关资源
最近更新 更多