pandas.dataframe_如何简单地更快地对Pandas DataFrame进行操作

在本文中,我将介绍和评估不同的python方法,以便运行相同的函数并在pandas中创建新的列。 这些方法将在数字和非数字操作中进行检查。

4e0125c425d555a323c33c42e057098b.png

> Photo by Marc Sendra Martorell on Unsplash

如果您以前拥有足够大的数据集,那么您已经知道有时简单的操作需要大量时间。 有很多方法可以使对Pandas DataFrame的操作更快。 您可以使用多处理,modin [ray],cuDF,Dask,Spark之类的库来完成工作。 另外,您可以修改算法以更快地执行任务。 但是,在寻求解决方案之前,让我们看看是否有任何简单的方法可以提高速度并节省时间!

至于我的数据集,我正在使用其中的一部分。 它有2个数字列和1个文本列,由" HelpfulnessNumerator"列中的一些0和" ProfileName"列中的一些NaN组成。 (数据集和所有笔记本均已上传到此Github存储库中。)

46f492a49c7ee5ee287e1d856002a302.png

数值运算

我想简单地检查出不同的方法,而主要功能没有任何变化。 数字部分中的函数是除法函数。

def divide(t,h):    if h == 0:        return np.nan    return t/h

我学习Pandas数据框架的第一件事是df.iterrows()

%%timeititerrows_divide = []for index,row in df.iterrows():    Time = row['Time']    HelpfulnessNumerator = row['HelpfulnessNumerator']    iterrows_divide.append(divide(Time,HelpfulnessNumerator))5min 12s ± 31.1 s per loop (mean ± std. dev. of 7 runs, 1 loop each)

apply()方法是另一种选择:

%timeit df.apply(lambda row: divide(row['Time'],row['HelpfulnessNumerator']),axis = 1)1min 25s ± 4.88 s per loop (mean ± std. dev. of 7 runs, 1 loop each)

Pandas行操作中最好的选择是itertuples()方法:

%%timeititertuples_divide = []for _ in df.itertuples():    Time = getattr(_,'Time')    HelpfulnessNumerator = getattr(_,'HelpfulnessNumerator')    itertuples_divide.append(divide(Time,HelpfulnessNumerator))4 s ± 238 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

在大多数情况下,强制使用逐行操作。 但是,如果您必须这样做,则df.itertuples()是最快的方法。

让我们尝试一下map()方法:

%timeit list(map(divide,df['Time'],df['HelpfulnessNumerator']))861 ms ± 22.9 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

让我们做一些(假的)numpy vectorizer()方法:

%timeit np.vectorize(divide)(df['Time'],df['HelpfulnessNumerator'])636 ms ± 24.6 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

我们可以将numpy where()方法用于(实际)numpy矢量化程序:

%timeit np.where(df['HelpfulnessNumerator'] == 0 , 0 , df['Time']/df['HelpfulnessNumerator'])21.6 ms ± 329 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

如您所见,我们不能在所有地方都使用np.where()方法。 但这确实很快。 让我们稍微改变一下功能:

def modified_divide(t,h):    return t/h

现在我们可以在np.where()中使用此函数:

%timeit np.where(df['HelpfulnessNumerator'] == 0 , 0 , modified_divide(df['Time'],df['HelpfulnessNumerator']))21.4 ms ± 200 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

另外,我们可以直接在pandas操作中使用此功能:

%timeit modified_divide(df['Time'],df['HelpfulnessNumerator'])8.75 ms ± 114 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

还有其他选择吗? 绝对没错。 如果可以使用Python编写代码,则可以使用Fortran编写代码。 我在Jupyter Notebook中为Fortran魔术制作了一个笔记本。 仅作为示例,请看以下示例:

%%fortransubroutine fortran_divide(x, y, z)    real, intent(in) :: x,y    real, intent(out) :: z        z = x/y    IF ( 0 .EQ. Y ) THEN          Z = 0    END IF        end subroutine fortran_divide%timeit np.vectorize(fortran_divide)(df['Time'],df['HelpfulnessNumerator'])669 ms ± 22.8 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

我们可以使用numba。 Numba是一个python库,我们可以很直接地使用它。 它比np.where()快2倍,并且您在这里没有np.where()的一些限制:

from numba import njit@njit()def divide(t, h):    res = np.empty(t.shape)    for i in range(len(t)):        if h[i] != 0:            res[i] = t[i] / h[i]        else:            res[i] = 0    return res%timeit divide(df['Time'].values, df['HelpfulnessNumerator'].value9.99 ms ± 133 µs per loop (mean ± std. dev. of 7 runs, 1 loop each)

numba和Fortran都可以选择使用多处理功能。 它取决于DataFrame的大小和操作。 有时我们可以使用多重处理来提高速度,有时numba和Fortran中的多重处理对我们没有太大帮助。

ac8b6e078f6315d98aad6b21af3d07d8.png

> logarithmic time for each method.

结论是,不要对pandas DataFrame使用按行操作。 如果必须的话,可以使用df.itertuples()。 永远不要使用df.iterrows()和df.apply(…,axis = 1)。

大多数时候,您可以使用np.where()进行一些技巧。 这是最好的选择。 但是,如果不能使用它,则可以在执行数字运算时使用np.vectorize()。

还有一些其他选项,例如numba和Fortran魔术,如果我们有大量的数据集,值得花一些时间来学习和使用它们,它们可以为我们提供帮助。

此外,有时候Pandas本身也支持我们想要的东西。 最好先检查大熊猫。 :)

非数值运算

我将定义一个简单的函数来评估不同的方法。 该函数将在每个记录的开头添加一个短语。

def edit_name(profileName):    if profileName != profileName:        return np.nan    return 'Name is {}'.format(profileName)

使用df.iterrows()很简单。 但这太慢了:

%%timeititerrows_name=[]for index,row in df.iterrows():    name = edit_name(row['ProfileName'])    iterrows_name.append(name)4min 15s ± 2.45 s per loop (mean ± std. dev. of 7 runs, 1 loop each)

使用逐行套用method():

%timeit df.apply(lambda row: edit_name(row['ProfileName']),axis = 1)43.2 s ± 687 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

使用itertuples()方法:

%%timeititertuples_name=[]for _ in df.itertuples():    name = edit_name(getattr(_,'ProfileName'))    itertuples_name.append(name)3.78 s ± 128 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

另一种使用apply()方法的方法:

%timeit df['ProfileName'].apply(lambda x: edit_name(x))1.58 s ± 86.5 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

让我们使用map()方法:

%timeit list(map(edit_name,df['ProfileName']))1.41 s ± 65.5 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

糟糕,np.vectorizer的速度不如我们预期的快。 是的,因为该运算不是数字。

%timeit np.vectorize(edit_name)(df['ProfileName'])2.16 s ± 155 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

有一个pandas.Series.map可以非常快地完成有限的任务。 它本身可以处理NaN值。 例如,让我们更改函数并查看结果:

def modified_edit_name(profileName):    return 'Name is {}'.format(profileName)

我们可以在np.where()中轻松处理NaN值:

%timeit np.where(df['ProfileName'] == np.nan,np.nan, modified_edit_name(df['ProfileName']))2.69 s ± 98.3 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

Pandas系列map方法:

%timeit df['ProfileName'].map('Name is {}'.format, na_action='ignore')1.16 s ± 50.6 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
4a04ce6fdcea19cd73fef94ba0b7c1ee.png

> logarithmic time for each method.

当涉及文本时,我更喜欢使用map(),因为它速度很快。 如果必须执行逐行操作,则将使用df.itertuples()。

就像我说的那样,经过简单的优化之后,我们可以继续进行多处理并使用其他库来对pandas DataFrame进行更快的操作。

(本文翻译自Mahdi Rasouli的文章《How to simply make an operation on pandas DataFrame faster》,参考:https://towardsdatascience.com/how-to-simply-make-an-operation-on-pandas-dataframe-faster-adaea5e41e96)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值