繁体   English   中英

加快python应用行明智函数

[英]speed up python apply row wise functions

我正在做一个数据清理项目,因此我必须清理熊猫数据框的多个字段。 通常,我正在编写正则表达式和简单函数。 下面的例子

def func1(s):
    s = str(s)
    s = s.replace(' ', '')
    if len(s) > 0 and s != '0':
        if s.isalpha() and len(s) < 2:
            return s

def func2(s):
    s = str(s)
    s = s.replace(' ', '')
    s = s.strip(whitespace+','+'-'+'/'+'\\')
    if s != '0':
        if s.isalnum() or s.isdigit():
            return s

def func3(s):
    s = str(s)
    if s.isdigit() and s != '0':
        return s
    else:
        return None

def func4(s):
    if str(s['j']).isalpha() and str(s['k']).isdigit() and s['l'] is none:
        return s['k']

并这样称呼他们。

x['a'] = x['b'].apply(lambda x: func1(x) if pd.notnull(x) else x)
x['c'] = x['d'].apply(lambda x: func2(x) if pd.notnull(x) else x)
x['e'] = x['f'].apply(lambda x: func3(x) if pd.notnull(x) else x)
x['g'] = x.apply(lambda x: func4(x), axis = 1)

这里一切都很好,但是我已经写了将近50个这样的函数,并且我的数据集有超过1000万条记录。 脚本运行了几个小时,如果我的理解是正确的,则将这些函数按行调用,因此每个函数的调用次数与行的调用次数相同,并且需要花费很长时间来处理。 有没有一种方法可以对此进行优化? 我怎样才能更好地解决这个问题? 可能不是通过套用功能? 谢谢。

样本数据集:-

        Name                               f    j    b
339043  Moir Point RD                      3    0   
21880   Fisher-Point Drive Freemans Ba     6    0   
457170  Whakamoenga Point                 29    0   
318399  Motukaraka Point RD                0    0   
274047  Apirana Avenue Point England     360    0   366
207588  Hobsonville Point RD             127    0   
747136  Dog Point RD                     130    0   
325704  Aroha Road Te Arai Point          36    0   
291888  One Tree Point RD                960    0   
207954  Hobsonville Point RD             160    0   205D
248410  Huia Road Point Chevalier        106    0   

通常,应避免在.apply上调用DataFrame 这确实是让您受益的地方。 DataFrame ,它正在为DataFrame每一行创建一个新的Series ,并将其发送到传递给.apply的函数。 不用说,这是每行相当多的开销,因此.apply在完整的DataFrame很慢。

在下面的示例中,由于示例数据受到限制,因此我重命名了函数调用中的某些列。

import sys
import time
import contextlib
import pandas as pd

@contextlib.contextmanager
def timethis(label):
    '''A context manager to time a bit of code.'''
    print('Timing', label, end=': ')
    sys.stdout.flush()
    start = time.time()
    yield
    print('{:.4g} seconds'.format(time.time() - start))

... func1, func2, and func3 definitions...

def func4(s):
    if str(s['j']).isalpha() and str(s['f']).isdigit() and s['b'] is none:
        return s['f']

x = pd.DataFrame({'f': [3, 6, 29, 0, 360, 127, 130, 36, 960, 160, 106],
                  'j': 0,
                  'b': [None, None, None, None, 366, None, None, None, None, '205D', None]})
x = pd.concat(x for _ in range(100000))
y = x.copy()

x['a'] = x['b'].apply(lambda x: func1(x) if pd.notnull(x) else x)
x['c'] = x['j'].apply(lambda x: func2(x) if pd.notnull(x) else x)
x['e'] = x['f'].apply(lambda x: func3(x) if pd.notnull(x) else x)
with timethis('func4'):
    x['g'] = x.apply(func4, axis = 1)  # The lambda in your example was not needed

...

def vectorized_func4(df):
    '''Accept the whole DataFrame and not just a single row.'''
    j_isalpha = df['j'].astype(str).str.isalpha()
    f_isdigit = df['f'].astype(str).str.isdigit()
    b_None = df['b'].isnull()
    ret_col = df['f'].copy()
    keep_rows = j_isalpha & f_isdigit & b_None
    ret_col[~keep_rows] = None
    return ret_col

y['a'] = vectorized_func1(y['b'])
y['c'] = vectorized_func2(y['j'])
y['e'] = vectorized_func3(y['f'])
with timethis('vectorized_func4'):
    y['g'] = vectorized_func4(y)

输出:

Timing func4: 115.9 seconds
Timing vectorized_func4: 25.09 seconds

事实证明,对于func1func2func3 ,与矢量化方法相比,它在性能上是一种func3 .apply (和.map为此事)对Series因为每个单元没有额外的开销也不是那么慢。 然而,这并不意味着你应该只使用.apply ,当你有一个Series ,而不是调查的矢量内置方法Series -往往不是你可能能够做的比更好的apply

这是您重写func3进行矢量化的方法(我添加了时序语句,以便我们看到花费最多的时间)。

def vectorized_func3(col):
    with timethis('fillna'):
        col = col.fillna('')
    with timethis('astype'):
        col = col.astype(str)
    with timethis('rest'):
        is_digit_string = col.str.isdigit()
        not_0_string = col != '0'
        keep_rows = is_digit_string & not_0_string
        col[~keep_rows] = None
    return col

这是与func3比较的时间:

Timing func3: 8.302 seconds
Timing fillna: 0.006584 seconds
Timing astype: 9.445 seconds
Timing rest: 1.65 seconds

仅更改Seriesdtype会花费很长时间,因为必须创建一个新的Series ,然后每个元素都将被强制转换。 其他一切都在燃烧 如果你可以改变你的算法不要求改变数据类型为str ,或者可以简单地存储为str摆在首位,然后向量化的方法是快得多(尤其是vectorized_func4 )。

带走

  • 不要使用.apply于一个完整的DataFrame ,除非你绝对必须。 如果您认为必须这样做,那就去喝杯咖啡,然后思考十分钟,然后尝试思考一种不用.apply
  • 尽量不要在Series上使用.apply ,您可能可以做得更好,但是不会像在完整的DataFrame上那样糟糕。
  • 尝试提出不需要不断转换dtype的算法。

除了使用多个条件之外,还可以在一个函数中使用if..elif。 只是一个想法!

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM