Python 测试值的最有效方法是在表中的列表中

Python 测试值的最有效方法是在表中的列表中,python,pandas,dataframe,apply,Python,Pandas,Dataframe,Apply,我有一个来自csv的数据框架,我正在测试它的各个方面。所有这些似乎都是沿着这一行进行的,要么像这个正则表达式一样是这一列,要么就是这个列表中的这一列 所以我的数据帧有点像这样: import pandas as pd df = pd.DataFrame({'full_name': ['Mickey Mouse', 'M Mouse', 'Mickey RudeWord Mouse'], 'nationality': ['Mouseland', 'United States', 'Canada'

我有一个来自csv的数据框架,我正在测试它的各个方面。所有这些似乎都是沿着这一行进行的,要么像这个正则表达式一样是这一列,要么就是这个列表中的这一列

所以我的数据帧有点像这样:

import pandas as pd
df  = pd.DataFrame({'full_name': ['Mickey Mouse', 'M Mouse', 'Mickey RudeWord Mouse'], 'nationality': ['Mouseland', 'United States', 'Canada']})
我正在基于该内容生成新专栏,如下所示:

def full_name_metrics(full_name):
    lst_rude_words = ['RUDEWORD', 'ANOTHERRUDEWORD', 'YOUGETTHEIDEA']
    # metric of whether full name has less than two distinct elements
    full_name_less_than_2_parts = len(full_name.split(' '))<2
    # metric of whether full_name contains an initial
    full_name_with_initial = 1 in [len(x) for x in full_name.split(' ')]
    # metric of whether name matches an offensive word
    full_name_with_offensive_word = any(item in full_name.upper().split(' ') for item in lst_rude_words)
    return pd.Series([full_name_less_than_2_parts, full_name_with_initial, full_name_with_offensive_word])

df[['full_name_less_than_2_parts', 'full_name_with_initial', 'full_name_with_offensive_word']] = df.apply(lambda x: full_name_metrics(x['full_name']), axis=1)
def full_name_度量(全名):
lst_rude_words=['RUDEWORD','ANOTHERRUDEWORD','yougetheidea']
#全名是否少于两个不同元素的度量

full_name_less_than_2_parts=len(full_name.split(“”))我将逐一回答

所有操作都依赖于在空白处拆分全名列,因此只需执行一次:

>>> stuff = df.full_name.str.split()
对于少于两部分的名称:

>>> df['full_name_less_than_2_parts'] = stuff.agg(len) < 2
>>> df
               full_name    nationality  full_name_less_than_2_parts
0           Mickey Mouse      Mouseland                        False
1                M Mouse  United States                        False
2  Mickey RudeWord Mouse         Canada                        False

检查不需要的单词

从不需要的单词列表中创建正则表达式模式,并将其用作
.str.contains
方法的参数

>>> rude_words =r'|'.join( ['RUDEWORD', 'ANOTHERRUDEWORD', 'YOUGETTHEIDEA'])
>>> df['rude'] = df.full_name.str.upper().str.contains(rude_words,regex=True)
>>> df
               full_name    nationality  full_name_less_than_2_parts  full_name_with_initial   rude
0           Mickey Mouse      Mouseland                        False                   False  False
1                M Mouse  United States                        False                    True  False
2  Mickey RudeWord Mouse         Canada                        False                   False   True

将它们放入一个返回三个序列的函数中(主要用于执行定时测试)

import pandas as pd
from timer import Timer
df = pd.DataFrame(
    {
        "full_name": ["Mickey Mouse", "M Mouse", "Mickey RudeWord Mouse"]*8000,
        "nationality": ["Mouseland", "United States", "Canada"]*8000,
    }
)
rude_words = r'|'.join(['RUDEWORD', 'ANOTHERRUDEWORD', 'YOUGETTHEIDEA'])
def f(df):
    rude_words = r'|'.join(['RUDEWORD', 'ANOTHERRUDEWORD', 'YOUGETTHEIDEA'])
    stuff = df.full_name.str.split()
    s1 = stuff.agg(len) < 2
    stuff = (stuff.explode().agg(len) == 1)
    s2 = stuff.groupby(stuff.index).agg('any')
    s3 = df.full_name.str.upper().str.contains(rude_words,regex=True)
    return s1,s2,s3

t = Timer('f(df)','from __main__ import pd,df,f')
print(t.timeit(1))    # <--- 0.12 seconds on my computer
x,y,z = f(df)
df.loc[:,'full_name_less_than_2_parts'] = x
df.loc[:,'full_name_with_initial'] = y
df.loc[:,'rude'] = z
# print(df.head(100))
将熊猫作为pd导入
从计时器导入计时器
df=pd.DataFrame(
{
“全名”:[“米老鼠”、“米老鼠”、“米老鼠”]*8000,
“国籍”:[“穆斯兰”、“美国”、“加拿大”]*8000,
}
)
粗鲁的单词=r'|'。加入(['RUDEWORD','ANOTHERRUDEWORD','yougetheidea'])
def f(df):
粗鲁的单词=r'|'。加入(['RUDEWORD','ANOTHERRUDEWORD','yougetheidea'])
stuff=df.full_name.str.split()
s1=stuff.agg(len)<2
stuff=(stuff.explode().agg(len)==1)
s2=stuff.groupby(stuff.index).agg('any'))
s3=df.full_name.str.upper().str.contains(粗鲁的单词,regex=True)
返回s1、s2、s3
t=计时器('f(df'),'from'uuuuuuu main'uuuuuuuuuu'import pd,df,f')

print(t.timeit(1))#如果您希望加快列表检查,那么
Series.str.contains
方法可能会有所帮助-

lst_rude_words_as_str = '|'.join(lst_rude_words)
df['full_name_with_offensive_word'] = df['full_name'].str.upper().str.contains(lst_rude_words_as_str, regex=True)
以下是
%timeit
对我的看法:

def func_in_list(full_name):
'''Your function - just removed the other two columns.'''
    lst_rude_words = ['RUDEWORD', 'ANOTHERRUDEWORD', 'YOUGETTHEIDEA']
    full_name_with_offensive_word = any(item in full_name.upper().split(' ') for item in lst_rude_words)


%timeit df.apply(lambda x: func_in_list(x['full_name']), axis=1) #3.15 ms
%timeit df['full_name'].str.upper().str.contains(lst_rude_words_as_str, regex=True) #505 µs
编辑

我添加了之前漏掉的另外两列-下面是完整的代码

import pandas as pd    
df = pd.DataFrame({'full_name': ['Mickey Mouse', 'M Mouse', 'Mickey Rudeword Mouse']})
def df_metrics(input_df):
    input_df['full_name_less_than_2_parts'] = input_df['full_name'].str.split().map(len) < 2
    input_df['full_name_with_initial'] = input_df['full_name'].str.split(expand=True)[0].map(len) == 1
    lst_rude_words = ['RUDEWORD', 'ANOTHERRUDEWORD', 'YOUGETTHEIDEA']
    lst_rude_words_as_str = '|'.join(lst_rude_words)
    input_df['full_name_with_offensive_word'] = input_df['full_name'].str.upper().str.contains(lst_rude_words_as_str, regex=True)
    return input_df
但是当我增加数据帧的大小时,就会有一些速度

df_big = pd.concat([df] * 10000)

%timeit df_metrics(df_big)
#135 ms ± 7.03 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

 %timeit df_big[['full_name_less_than_2_parts', 'full_name_with_initial', 'full_name_with_offensive_word']] = df_big.apply(lambda x: full_name_metrics(x['full_name']), axis=1) 
#11.5 s ± 173 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

顺便说一句,我还有其他不依赖于拆分的测试,如果这会造成差异的话
full\u name\u with\u invalid\u character\u format=not(bool(re.match('^[a-zA-Z\s\]+$,full\u name))
所以你认为时间问题可能与拆分有关,我原以为主要是针对列表进行测试,很明显,它们的长度超过3项。不一定是拆分。如果您/我能够弄清楚如何将系列作为一个整体进行操作,而不是按行应用操作,则会有所改进。
%timeit df_metrics(df)
#3.5 ms ± 67.7 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

%timeit df[['full_name_less_than_2_parts', 'full_name_with_initial', 'full_name_with_offensive_word']] = df.apply(lambda x: full_name_metrics(x['full_name']), axis=1)
#3.7 ms ± 59.6 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
df_big = pd.concat([df] * 10000)

%timeit df_metrics(df_big)
#135 ms ± 7.03 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

 %timeit df_big[['full_name_less_than_2_parts', 'full_name_with_initial', 'full_name_with_offensive_word']] = df_big.apply(lambda x: full_name_metrics(x['full_name']), axis=1) 
#11.5 s ± 173 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)