根据字符串长度过滤字符串数据

130

我喜欢筛选出字符串长度不等于10的数据。

如果我想要筛选出任何一行,其列AB的字符串长度不等于10,我会尝试这样做。

df=pd.read_csv('filex.csv')
df.A=df.A.apply(lambda x: x if len(x)== 10 else np.nan)
df.B=df.B.apply(lambda x: x if len(x)== 10 else np.nan)
df=df.dropna(subset=['A','B'], how='any')

这个工作速度较慢,但是正在运行。

然而,当A中的数据不是字符串而是数字(在read_csv读取输入文件时被解释为数字)时,有时会出现错误:

File "<stdin>", line 1, in <lambda>
TypeError: object of type 'float' has no len()

我相信应该有更加高效和优美的代码替代这段代码。


基于以下回答和评论,我找到了最简单的解决方案:

df=df[df.A.apply(lambda x: len(str(x))==10]
df=df[df.B.apply(lambda x: len(str(x))==10]
或者
df=df[(df.A.apply(lambda x: len(str(x))==10) & (df.B.apply(lambda x: len(str(x))==10)]
或者
df=df[(df.A.astype(str).str.len()==10) & (df.B.astype(str).str.len()==10)]
9个回答

200
import pandas as pd

df = pd.read_csv('filex.csv')
df['A'] = df['A'].astype('str')
df['B'] = df['B'].astype('str')
mask = (df['A'].str.len() == 10) & (df['B'].str.len() == 10)
df = df.loc[mask]
print(df)

应用于filex.csv:

A,B
123,abc
1234,abcd
1234567890,abcdefghij

上面的代码打印输出

            A           B
2  1234567890  abcdefghij

astype('str') 是关键点。在更改任何列数据类型之前,确保使用它。要检查数据框中的数据类型,可以使用 print(df.dtypes) - agfe2

37

一种更Pythonic的过滤行的方式是基于其他列及其值的给定条件:

假设一个df如下:

data = {
    "names": ["Alice", "Zac", "Anna", "O"],
    "cars": ["Civic", "BMW", "Mitsubishi", "Benz"],
    "age": ["1", "4", "2", "0"],
}

df=pd.DataFrame(data)
df:
  age        cars  names
0   1       Civic  Alice
1   4         BMW    Zac
2   2  Mitsubishi   Anna
3   0        Benz      O

那么:

df[
    df["names"].apply(lambda x: len(x) > 1)
    & df["cars"].apply(lambda x: "i" in x)
    & df["age"].apply(lambda x: int(x) < 2)
]

我们将拥有:

  age   cars  names
0   1  Civic  Alice

在上述条件下,我们首先查看字符串的长度,然后检查字符串中是否存在字母"i",最后检查第一列中整数的值。


19

我个人发现这种方式是最容易的:

df['column_name'] = df[df['column_name'].str.len()!=10]

1
虽然一开始看起来这种方法更方便,但在处理大数据集时比使用apply要慢。 - Smittie

6

你也可以使用query

df.query('A.str.len() == 10 & B.str.len() == 10')

2

对于这样的字符串操作,使用内置方法(不使用lambda)的普通Python比apply()str.len()要快得多。

通过将len映射到列表推导式中的每个字符串来构建布尔掩码,大约比apply()str.len()分别快40-70%。

对于多列,zip()允许同时评估来自不同列的值。

col_A_len = map(len, df['A'].astype(str))
col_B_len = map(len, df['B'].astype(str))
m = [a==3 and b==3 for a,b in zip(col_A_len, col_B_len)]
df1 = df[m]

对于单列,不使用zip(),而是循环遍历该列并检查长度是否等于3:

df2 = df[[a==3 for a in map(len, df['A'].astype(str))]]

使用 Series.map() 方法可以稍微简洁地编写此代码(但由于 pandas 的开销,速度可能比列表推导式略慢):

df2 = df[df['A'].astype(str).map(len)==3]

1
如果您的行中有数字,则它们将转换为浮点数。
从csv导入后将所有行转换为字符串。为了更好的性能,将那些lambda分割成多个线程。

1
你可以使用df.apply(len)。它会给你结果。

1

从A列和B列中过滤出长度不为10的值,这里我传递lambda表达式给map()函数。map()函数总是应用于Series对象。

 df = df[df['A'].map(lambda x: len(str(x)) == 10)]
 df = df[df['B'].map(lambda x: len(str(x)) == 10)]

0
你可以使用 applymap 方法一次性过滤所有需要的列,然后使用 .all() 方法仅保留两列都为 True 的行。
#The *mask* variable is a dataframe of booleans, giving you True or False for the selected condition
mask = df[['A','B']].applymap(lambda x: len(str(x)) == 10)

#Here you can just use the mask to filter your rows, using the method *.all()* to filter only rows that are all True, but you could also use the *.any()* method for other needs
df = df[mask.all(axis=1)]

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接