Pandas 通过子字符串匹配过滤数据框列
Pandas filter dataframe columns through substring match
我有一个包含多列的数据框,例如:
Name Age Fname
0 Alex 10 Alice
1 Bob 12 Bob
2 Clarke 13 clarke
我的过滤条件是检查Name
是否是对应Fname
.
的子串(不区分大小写)
如果是相等,那么简单如:
df[df["Name"].str.lower() == df["Fname"].str.lower()]
有效。但是,我想要子字符串匹配,所以我认为 in
可以代替 ==
。但这会产生错误,因为它将其中一个参数解释为 pd.Series
。我的第一个问题是 Why this difference in interpretation?
我尝试的另一种方法是使用 .str.contains
:
df[df["Fname"].str.contains(df["Name"], case=False)]
也将 df["Name"]
解释为 pd.Series
,当然,适用于参数中的某些常量字符串。
eg. this works:
df[df["Fname"].str.contains("a", case=False)]
我想解决这个问题,非常感谢这方面的任何帮助。
您可以使用 .apply()
和 axis=1
为每一行调用一个函数:
subset = df[df.apply(lambda x: x['Name'].lower() in x['Fname'].lower(), axis=1)]
输出:
>>> subset
Name Age Fname
1 Bob 12 Bob
2 Clarke 13 clarke
您可以遍历索引轴:
>>> df[df.apply(lambda x: x['Name'].lower() in x['Fname'].lower(), axis=1)]
Name Age Fname
1 Bob 12 Bob
2 Clarke 13 clarke
str.contains
在第一个参数 pat
中取一个常量而不是 Series
.
.str 访问器非常循环和缓慢。大多数时候最好使用列表理解。
import pandas as pd
import numpy as np
import timeit
import matplotlib.pyplot as plt
import pandas.testing as pt
def list_comprehension_lower(df):
return df[[len(set(i)) == 1 for i in (zip([x.lower() for x in df['Name']],[y.lower() for y in df['Fname']]))]]
def apply_axis_1_lower(df):
return df[df.apply(lambda x: x['Name'].lower() in x['Fname'].lower(), axis=1)]
def dot_string_lower(df):
return df[df["Name"].str.lower() == df["Fname"].str.lower()]
fig, ax = plt.subplots()
res = pd.DataFrame(
index=[1, 5, 10, 30, 50, 100, 300, 500, 700, 1000, 10000],
columns='list_comprehension_lower apply_axis_1_lower dot_string_lower'.split(),
dtype=float
)
for i in res.index:
d = pd.concat([df]*i, ignore_index=True)
for j in res.columns:
stmt = '{}(d)'.format(j)
setp = 'from __main__ import d, {}'.format(j)
res.at[i, j] = timeit.timeit(stmt, setp, number=100)
res.groupby(res.columns.str[4:-1], axis=1).plot(loglog=True, ax=ax);
输出:
现在,回到你原来的问题,你可以将 list_comprehension 与 zip
和 in
一起使用:
df.loc[2, 'Fname'] += ' Adams'
df[[x in y for x, y in zip([x.lower() for x in df['Name']],[y.lower() for y in df['Fname']])]]
输出:
Name Age Fname
1 Bob 12 Bob
2 Clarke 13 clarke Adams
这个通过列表理解的其他选项对你有用吗:
df.loc[[left.lower() in right.lower()
for left, right
in zip(df.Name, df.Fname)]
]
Name Age Fname
1 Bob 12 Bob
2 Clarke 13 clarke
我有一个包含多列的数据框,例如:
Name Age Fname
0 Alex 10 Alice
1 Bob 12 Bob
2 Clarke 13 clarke
我的过滤条件是检查Name
是否是对应Fname
.
如果是相等,那么简单如:
df[df["Name"].str.lower() == df["Fname"].str.lower()]
有效。但是,我想要子字符串匹配,所以我认为 in
可以代替 ==
。但这会产生错误,因为它将其中一个参数解释为 pd.Series
。我的第一个问题是 Why this difference in interpretation?
我尝试的另一种方法是使用 .str.contains
:
df[df["Fname"].str.contains(df["Name"], case=False)]
也将 df["Name"]
解释为 pd.Series
,当然,适用于参数中的某些常量字符串。
eg. this works:
df[df["Fname"].str.contains("a", case=False)]
我想解决这个问题,非常感谢这方面的任何帮助。
您可以使用 .apply()
和 axis=1
为每一行调用一个函数:
subset = df[df.apply(lambda x: x['Name'].lower() in x['Fname'].lower(), axis=1)]
输出:
>>> subset
Name Age Fname
1 Bob 12 Bob
2 Clarke 13 clarke
您可以遍历索引轴:
>>> df[df.apply(lambda x: x['Name'].lower() in x['Fname'].lower(), axis=1)]
Name Age Fname
1 Bob 12 Bob
2 Clarke 13 clarke
str.contains
在第一个参数 pat
中取一个常量而不是 Series
.
.str 访问器非常循环和缓慢。大多数时候最好使用列表理解。
import pandas as pd
import numpy as np
import timeit
import matplotlib.pyplot as plt
import pandas.testing as pt
def list_comprehension_lower(df):
return df[[len(set(i)) == 1 for i in (zip([x.lower() for x in df['Name']],[y.lower() for y in df['Fname']]))]]
def apply_axis_1_lower(df):
return df[df.apply(lambda x: x['Name'].lower() in x['Fname'].lower(), axis=1)]
def dot_string_lower(df):
return df[df["Name"].str.lower() == df["Fname"].str.lower()]
fig, ax = plt.subplots()
res = pd.DataFrame(
index=[1, 5, 10, 30, 50, 100, 300, 500, 700, 1000, 10000],
columns='list_comprehension_lower apply_axis_1_lower dot_string_lower'.split(),
dtype=float
)
for i in res.index:
d = pd.concat([df]*i, ignore_index=True)
for j in res.columns:
stmt = '{}(d)'.format(j)
setp = 'from __main__ import d, {}'.format(j)
res.at[i, j] = timeit.timeit(stmt, setp, number=100)
res.groupby(res.columns.str[4:-1], axis=1).plot(loglog=True, ax=ax);
输出:
现在,回到你原来的问题,你可以将 list_comprehension 与 zip
和 in
一起使用:
df.loc[2, 'Fname'] += ' Adams'
df[[x in y for x, y in zip([x.lower() for x in df['Name']],[y.lower() for y in df['Fname']])]]
输出:
Name Age Fname
1 Bob 12 Bob
2 Clarke 13 clarke Adams
这个通过列表理解的其他选项对你有用吗:
df.loc[[left.lower() in right.lower()
for left, right
in zip(df.Name, df.Fname)]
]
Name Age Fname
1 Bob 12 Bob
2 Clarke 13 clarke