【问题标题】:Pandas : how to merge 2 dataframes on key1.str.endswith(key2)熊猫:如何在 key1.str.endswith(key2) 上合并 2 个数据帧
【发布时间】:2015-05-24 00:38:09
【问题描述】:

我想找到在 key1.str.endswith(key2) 上合并 2 个数据帧的最佳方法,一个例子有时比文字更好:

 i want to merge df1 and df2  on product.str.endswith(color)

 df1:
    index product
    1     a208-BLACK 
    2     a2008-WHITE
    3     x307-PEARL-WHITE
    4     aa-b307-WHITE

 df2:
    index color       code
    1     BLACK       X1001
    2     WHITE       X7005
    3     PEARL-WHITE X7055

得到:

 df:
    index product            code
    1     a208-BLACK         X1001
    2     a2008-WHITE        X7005
    3     x307-PEARL-WHITE   X7055
    4     aa-b307-WHITE      X7005

有什么想法吗?

【问题讨论】:

  • 计算机如何知道PEARL-WHITE 是一种颜色而b307-WHITE 不是?大写是区分因素吗?
  • 我猜是这样,大写可以区分颜色。

标签: python pandas merge


【解决方案1】:

我不是正则表达式专家,最后一个是最难处理的,但以下工作:

In [402]:

df['code'] = df['product'].str.split('-').str[1:].str.join('-').str.findall(r'[A-Z]+').str.join('-').map(df1.set_index('color')['code'])
df
Out[402]:
                product   code
index                         
1            a208-BLACK  X1001
2           a2008-WHITE  X7005
3      x307-PEARL-WHITE  X7055
4         aa-b307-WHITE  X7005

基本上我在- 上拆分产品代码,并将所有元素放在第一个破折号的右侧。

剩下这个:

In [403]:

df['product'].str.split('-').str[1:]
Out[403]:
index
1               [BLACK]
2               [WHITE]
3        [PEARL, WHITE]
4         [b307, WHITE]
Name: product, dtype: object

然后我把破折号放回去,使用正则表达式只查找大写字母字符,这处理最后一个,再次重新加入。

最后一点是在颜色列上设置索引后在另一个df上调用map,这将对df中的颜色值进行查找并返回相应的代码。

正则表达式并非万无一失,但它适用于您的数据集。

编辑

我现在意识到我们不需要那么多连接:

In [409]:

df['code'] = df['product'].str.findall(r'[A-Z]+').str.join('-').map(df1.set_index('color')['code'])
df
Out[409]:
                product   code
index                         
1            a208-BLACK  X1001
2           a2008-WHITE  X7005
3      x307-PEARL-WHITE  X7055
4         aa-b307-WHITE  X7005

时间

In [414]:


%%timeit 
import re
df['color'] = df['product'].apply(lambda x: re.sub('^[^ALPHA:]*-(.*)', '\\1', x))

pd.merge(df, df1, on='color')
1 loops, best of 3: 4.09 ms per loop
In [416]:

%%timeit
df['code'] = df['product'].str.findall(r'[A-Z]+').str.join('-').map(df1.set_index('color')['code'])

100 loops, best of 3: 1.63 ms per loop

str 方法比使用 lambda 快 2 倍以上,这可能并不令人惊讶,因为 str 方法是矢量化的,就像调用 map 一样。

更新时间

In [7]:

%%timeit
df1['color'] = df1['product'].str.extract(r'-([A-Z-]+)$')
pd.merge(df1, df2)
100 loops, best of 3: 4.51 ms per loop
In [9]:

%%timeit
df1['code'] = df1['product'].str.findall(r'[A-Z]+').str.join('-').map(df2.set_index('color')['code'])
100 loops, best of 3: 3.87 ms per loop
In [10]:

%%timeit 
import re
df1['color'] = df1['product'].apply(lambda x: re.sub('^[^ALPHA:]*-(.*)', '\\1', x))

pd.merge(df1, df2, on='color')
100 loops, best of 3: 4.79 ms per loop

所以@unutbu 的回答比@colonel beaveau 的回答要快一些,但在这里使用地图还是更快。

事实上,如果我们将 @unutbu 的正则表达式 str 方法与 map 结合起来,我们会比我原来的方法更快:

In [12]:

%%timeit
df1['product'].str.extract(r'-([A-Z-]+)$').map(df2.set_index('color')['code'])
100 loops, best of 3: 2.17 ms per loop

所以在这里使用map 比合并快近 2 倍

【讨论】:

    【解决方案2】:

    一些简洁的解决方案:

    import pandas as pd
    
    df1['color'] = df1['product'].apply(lambda x: re.sub('^[^ALPHA:]*-(.*)', '\\1', x))
    
    pd.merge(df1, df2, on='color')
    
    #            product        color   code
    #0        a208-BLACK        BLACK  X1001
    #1       a2008-WHITE        WHITE  X7005
    #2  x307-PEARL-WHITE  PEARL-WHITE  X7055
    #3     aa-b307-WHITE        WHITE  X7005
    

    【讨论】:

    • 第 3 行的颜色错误,您使用的是 WHITE 而不是 PEARL-WHITE
    • 是的,刚刚注意到!只会更新正则表达式。这个想法只是使用合并而不是很长的一行;)
    • 我的方法快了 2 倍以上,考虑到数据集的大小和我正在执行更多操作的事实,这令人惊讶
    • 一致性与速度,取决于 OP 的选择!我很高兴仍然找到了一种可能的正则表达式!
    【解决方案3】:

    您可以使用vectorized string methodstr.extract 和正则表达式模式r'-([A-Z-]+)$' 来查找颜色。

    df1['color'] = df1['product'].str.findall(r'-([A-Z-]+)$').str[0]
    

    然后pd.merge(df1, df2) 将合并到公共列上,在这种情况下是color 列:

    result = pd.merge(df1, df2)
    

    例如,

    import io
    import pandas as pd
    
    df1 = '''\
    index product
    1     a208-BLACK 
    2     a2008-WHITE
    3     x307-PEARL-WHITE
    4     aa-b307-WHITE'''
    df1 = pd.read_table(io.BytesIO(df1), sep='\s+', index_col=0)
    
    df2 = '''\
    index color       code
    1     BLACK       X1001
    2     WHITE       X7005
    3     PEARL-WHITE X7055'''
    df2 = pd.read_table(io.BytesIO(df2), sep='\s+', index_col=0)
    df1['color'] = df1['product'].str.extract(r'-([A-Z-]+)$')
    print(pd.merge(df1, df2))
    

    产量

                product        color   code
    0        a208-BLACK        BLACK  X1001
    1       a2008-WHITE        WHITE  X7005
    2     aa-b307-WHITE        WHITE  X7005
    3  x307-PEARL-WHITE  PEARL-WHITE  X7055
    

    正则表达式模式r'-([A-Z-]+)$' 表示

    -              # match a literal hyphen
    (              # followed by a group 
     [A-Z-]+       # of 1-or-more capital letters or hyphens
    )              # end of group 
    $              # followed by end of line
    

    【讨论】:

      猜你喜欢
      • 2018-10-31
      • 1970-01-01
      • 2017-10-21
      • 2023-04-05
      • 2014-12-04
      • 2017-04-21
      • 2019-07-20
      • 1970-01-01
      • 2018-11-04
      相关资源
      最近更新 更多