在 Pandas 中对包含 Python `range` 或类似列表的列执行合并

Posted

技术标签:

【中文标题】在 Pandas 中对包含 Python `range` 或类似列表的列执行合并【英文标题】:Performing a merge in Pandas on a column containing a Python `range` or list-like 【发布时间】:2021-02-10 04:19:16 【问题描述】:

我的问题是几年前提出的this 的扩展。

我正在尝试左连接,但我想要连接的列之一需要是范围值。它必须是一个范围,因为扩展它意味着数百万个新的(和不必要的)行。直观地说,使用 Python 的 in 运算符(因为 x in range(y, z) 非常常见)似乎是可能的,但会涉及一个讨厌的 for 循环和 if/else 块。必须有更好的方法。

这是我的数据的一个简单版本:

# These are in any order
sample = pd.DataFrame(
    'col1': ['1b', '1a', '1a', '1b'],
    'col2': ['2b', '2b', '2a', '2a'],
    'col3': [42, 3, 21, 7]
)

# The 'look-up' table
look_up = pd.DataFrame(
    'col1': ['1a', '1a', '1a', '1a', '1b', '1b', '1b', '1b'],
    'col2': ['2a', '2a', '2b', '2b', '2a', '2a', '2b', '2b'],
    'col3': [range(0,10), range(10,101), range(0,10), range(10,101), range(0,10), range(10,101), range(0,10), range(10,101)],
    'col4': ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']
)

我最初尝试合并以查看 pandas 是否能够理解,但出现类型不匹配错误。

sample.merge(
    look_up,
    how='left',
    left_on=['col1', 'col2', 'col3'],
    right_on=['col1', 'col2', 'col3']
)
# ValueError: You are trying to merge on int64 and object columns. If you wish to proceed you should use pd.concat

查看pd.concat 的文档似乎也不会给我想要的结果。而不是追加,我仍在尝试获得类似merge 的结果。我试图按照我在开始时链接的问题的答案进行操作,但这也不起作用。 完全可能我误解了如何使用np.where,但我也希望有一个不那么老套的解决方案。

这是我使用np.where的尝试:

s1 = sample['col1'].values
s2 = sample['col2'].values
s3 = sample['col3'].values

l1 = look_up['col1'].values
l2 = look_up['col2'].values
l3 = look_up['col3'].values

i, j = np.where((s3[:, None] in l3) & (s2[:, None] == l2) & (s1[:, None] == l1))
result = pd.DataFrame(
    np.column_stack([sample.values[i], look_up.values[j]]), 
    columns=sample.columns.append(look_up.columns)
)

len(result)  # returns 0

我想要的结果应该是这样的:

col1  col2 col3 col4
'1b'  '2b'   42  'h'
'1a'  '2b'    3  'c'
'1a'  '2a'   21  'b'
'1b'  '2a'    7  'e'

【问题讨论】:

我不知道你的框架有多大或最大范围(所以它们可能不是最好的选择)但你可以分解和合并:sample.merge(look_up.explode('col3'), on=['col1', 'col2', 'col3'], how='left') 我应该添加当前的长度约为 30,000 行。最大的范围是 1,000 到 100,000,最常见的可能是 150 到 10,000。我认为使用 df.explode 可能是最简单的暴力选项。谢谢! @dlindsay df.explode 是一种快速的操作,虽然非常占用内存,但除非遇到内存问题,否则它确实是最简单的解决方案。 【参考方案1】:

由于看起来范围非常大,并且您正在使用整数值,因此您可以计算最小值、最大值:

columns = look_up.columns

look_up['minval'] = look_up['col3'].apply(min)
look_up['maxval'] = look_up['col3'].apply(max)
    
(sample.merge(look_up, on=['col1','col2'], how='left',
              suffixes=['','_'])
       .query('minval <= col3 <= maxval')
       [columns]
)

输出:

  col1 col2  col3 col4
1   1b   2b    42    h
2   1a   2b     3    c
5   1a   2a    21    b
6   1b   2a     7    e

【讨论】:

您对df.query 的使用正是我所缺少的。谢谢!

以上是关于在 Pandas 中对包含 Python `range` 或类似列表的列执行合并的主要内容,如果未能解决你的问题,请参考以下文章

在 pandas / python 中对条件值进行分组和计数

在 Python 中对 pandas 中的数据框进行分箱 [重复]

如何使用 Pandas 在 Python 中对字典中的数据进行排序

如何在 python pandas 循环中对数据帧执行操作

在 pandas 中对函数进行矢量化

如何在 FOR 循环中对 Python Pandas 列表中的元素执行字符串更改