【问题标题】:Count instances of matched strings and cumulative total values计算匹配字符串的实例和累计总值
【发布时间】:2020-05-04 14:07:27
【问题描述】:

很难在标题上描述这一点,但考虑到这两个 DataFrame:

import pandas as pd
import numpy as np
import re


df1 = pd.DataFrame({
'url': [
  'http://google.com/car', 
  'http://google.com/moto', 
  'http://google.com/moto-bike'
], 'value': [3, 4, 6]})

url                           value
http://google.com/car         3
http://google.com/moto        4
http://google.com/moto-bike   6

df2 = pd.DataFrame({'name': ['car','moto','bus']})

  name
0 car
1 moto
2 bus

我想看看df2 上的名字出现在url 上对于df1 的次数,并通过以下方式进行管理:

df2['instances'] = pd.Series([df1.url.str.contains(fr'\D{w}\D', regex=True) \
.sum() for w in df2.name.tolist()])

由于某种原因,汽车的实例为零,因为只有一个。

   name  instances
0   car          0
1  moto          2
2   bus          0

我希望能够有另一列将df1 的所有匹配项的value 列相加,所以它看起来像这样:

   name  instances  value_total
0   car          1           3
1  moto          2          10
2   bus          0           0

任何有关正确方向的帮助将不胜感激,谢谢!

【问题讨论】:

  • 所有的 url 是否都来自同一个域,或者至少是相同的格式?如果是这样,您可以在 url 列上使用正则表达式来删除您想要的部分,然后使用 pandas 内置插件在两个数据帧之间进行比较会很容易。
  • 是的,相同的域但不一定相同的路径,我拥有的正则表达式似乎足以识别想要的行
  • 为什么\D在字前后都做?我理解正则表达式的含义,只是想知道 url 查找中的原因?
  • 我想删除可能出现在 url 上的 name 值之前和之后的任何数字或字符,因此 \car 或 -car 匹配 car 但 carousel 或 motocar 不匹配
  • 我认为如果这是你想要的,那么你需要一个重复限定符。 *(0个或更多)或? (0 或 1)取决于可能有​​多少特殊字符。

标签: python pandas numpy


【解决方案1】:

尝试使用str.extract,然后合并groupbynamed aggregationpandas 0.25+ 中的新功能):

pat = '|'.join(df2['name']) #'car|moto|bus'
m = df2.merge(df1.assign(name=df1['url']
            .str.extract('('+ pat + ')', expand=False)),on='name',how='left')
m = m.groupby('name',sort=False).agg(instances=('value','count')
                 ,value_total=('value','sum')).reset_index()

print(m)

   name  instances  value_total
0   car          1          3.0
1  moto          2         10.0
2   bus          0          0.0

【讨论】:

  • 我收到TypeError: aggregate() missing 1 required positional argument: 'arg'
  • @Álvaro 命名聚合在pandas 版本0.25 中引入,可以upgradepandas 版本吗?
  • 我已经升级了,现在可以正常使用了,非常感谢,您认为这可以很好地处理大量数据吗?
  • 一如既往的好,你可以使用f-strings代替字符串连接=)
  • @Datanovice 如您的回答所示。是的,我们可以做到:)谢谢:)
【解决方案2】:

这是使用.locgroupbymerge 的anky 答案的类似版本

pat = '|'.join(df2['name'])
df1.loc[df1['url'].str.contains(f'({pat})'),'name'] = df1['url'].str.extract(f'({pat})')[0]

vals = (
    df1.groupby("name")
    .agg({"name": "count", "value": "sum"})
    .rename(columns={"name": "instance"})
    .reset_index()
)

new_df = pd.merge(df2,vals,on='name',how='left').fillna(0)

print(new_df)
   name  instance  value
0   car       1.0    3.0
1  moto       2.0   10.0
2   bus       0.0    0.0

编辑,如果您需要精确匹配 car,那么我们可以添加单词边界:

pat = r'|'.join(np.where(df2['name'].str.contains('car'),
                     r'\b' + df2['name'] + r'\b', df2['name']))
print(df1)
                          url  value 
0       http://google.com/car      3   
1     http://google.com/motor      4  
2  http://google.com/carousel      6  
3       http://google.com/bus      8 

df1.loc[df1['url'].str.contains(f'{pat}'),'name'] = df1['url'].str.extract(f'({pat})')[0]
print(df1)
                          url  value  name
0       http://google.com/car      3   car
1     http://google.com/motor      4  moto
2  http://google.com/carousel      6   NaN
3       http://google.com/bus      8   bus

如果你想要完全匹配,那么只需将单词边界添加到模式中:

pat = '|'.join(r'\b' + df2['name'] + r'\b')
#'\\bcar\\b|\\bmoto\\b|\\bbus\\b'

【讨论】:

  • 如果网址显示google.com/carousel,则再次使用这个,它会将其视为汽车但不是,这就是我在 str.contains 上执行正则表达式 \D{w}\D 的原因
  • @Álvaro 你可以添加一个单词边界,但是motor 会被排除,可以吗?
  • 它可以工作,但是您手动添加了单词 car,如何使其动态化?需要精确匹配 df2 上的所有字符串,而不仅仅是单词 car
  • @Álvaro 我们可以为 all 变量使其动态化,但正如我上面所说,它会将电机排除在查找之外,因此我的where 声明是否会感觉?
  • @Álvaro 已为您修复 - Anky_91 的答案比我的要好,所以请保留正确的答案 - 唯一的区别是次要的正则表达式
猜你喜欢
  • 1970-01-01
  • 2020-01-02
  • 1970-01-01
  • 2016-07-19
  • 1970-01-01
  • 2020-05-02
  • 2014-04-20
  • 2023-02-22
  • 1970-01-01
相关资源
最近更新 更多