【发布时间】:2020-04-26 05:41:36
【问题描述】:
第一部分
假设我有一个数据集 df,如下所示:
x | y
----|--------
foo | 1.foo-ya
bar | 2.bar-ga
baz | 3.ha-baz
qux | None
我想过滤 y 正好在中间包含 x 的行(不是开头也不是结尾,即匹配模式 '^.+\w+.+$',命中第 1 行和第 2 行),不包括 None/NaN:
x | y
----|-----
foo | 1.foo-ya
bar | 2.bar-ga
这是一个典型的成对字符比较,在 SQL 中很容易:
select x, y from df where y like concat('^.+', x, '.+%');
或在 R 中:
library(dplyr)
library(stringr)
library(glue)
df %>% filter(str_detect(y, glue('^.+{x}.+$')))
但是由于我不是pandas专家,看来pandas中没有类似的简单“矢量化”正则表达式匹配方法?我应用了 lambda 方法:
import pandas as pd
import re
df.loc[df.apply(lambda row: bool(re.search(
'^.+' + row.x + '.+$', row.y))
if row.x and row.y else False, axis=1), :]
pandas 中有没有更优雅的方法来完成它?
第二部分
此外,我想提取第一部分产生的匹配记录中的前导数字(1、2、...):
x | y | z
----|----------|---
foo | 1.foo-ya | 1
bar | 2.bar-ga | 2
在 R 中,我可以直接进行管道争吵:
df %>%
filter(str_detect(y, glue('^.+{x}.+$'))) %>%
mutate(z=str_replace(y, glue('^(\\d+)\\.{x}.+$'), '\\1') %>%
as.numeric)
但在 pandas 中,我只知道 lambda 方法。有没有比它“更好”的方法?
a = df.loc[df.apply(lambda row: bool(
re.search('^.+' + row.x + '.+$', row.y))
if row.x and row.y else False, axis=1),
['x', 'y']]
a['z'] = a.apply(lambda row: re.sub(
r'^(\d+)\.' + row.x + '.+$', r'\1', row.y), axis=1).astype('int')
a
顺便说一句,assign 方法不起作用。
df.loc[df.apply(lambda row: bool(re.search(
'^.+' + row.x + '.+$', row.y))
if row.x and row.y else False, axis=1),
['x', 'y']].assign(z=lambda row: re.sub(
r'^(\d+)\.' + row.x + '.+$', r'\1', row.y))
谢谢!
【问题讨论】:
-
您需要
df[df['x'].eq(df['y'].str.split('\.|-').str[1])]吗?您可以根据需要更改拆分的正则表达式 -
尝试
df.assign(foo=df['y'].str.extract('\d\.(\w+)-')).query('x == foo').drop( 'foo',axis=1).assign( num=df['y'].str.extract('(^\d)'))获得一次性解决方案。 -
@Datanovice,谢谢。 “正好在中间”仅仅意味着匹配模式 r'^.+{column x}.+$'.
-
上述方法有效吗?
-
@Datanovice,是的,两者都有效。谢谢。但是,如果模式在整个数据集中不一致怎么办?有没有一种矢量化的方法来首先识别列匹配?
标签: r regex pandas dplyr vectorization