【数据分析】Python使用Dask Dataframes并行数据分析

有时你用Python的Pandas打开一个大数据集,尝试获得一些指标,整个事情只是可怕地冻结。
如果您使用大数据,您知道如果您正在使用Pandas,那么您可以等待一小段时间来获得一个简单的系列平均值,让我们甚至不会调用apply。这只是几百万行!当你达到数十亿美元时,你最好开始使用Spark或其他东西。

我不久前发现了这个工具:一种加速Python数据分析的方法,无需获得更好的基础设施或切换语言。如果您的数据集很大,它最终会感觉有限,但它比普通的Pandas更好地扩展,并且可能只适合您的问题 - 特别是如果您没有进行大量的重建索引。

 

什么是Dask?

Dask是一个开源项目,它为您提供NumPy Arrays,Pandas Dataframes和常规列表的抽象,允许您使用多核处理并行运行它们。

以下是教程中的摘录:

Dask提供模仿NumPy,列表和Pandas的高级Array,Bag和DataFrame集合,但可以在不适合主内存的数据集上并行运行。Dask的高级集合是大型数据集的NumPy和Pandas的替代品。

它听起来真棒!我开始为这篇文章尝试Dask Dataframes,并对它们进行了几个基准测试。

 

阅读文档

我首先做的是阅读官方文档,看看在Dask中建议做什么而不是常规的Dataframes。以下是官方文档中的相关部分:

  • 操纵大型数据集,即使这些数据集不适合内存
  • 通过使用许多核心来加速长计算
  • 使用标准Pandas操作(如groupby,join和时间序列计算)对大型数据集进行分布式计算

然后在下面,它列出了一些在使用Dask Dataframes时非常快的事情:

  • 算术运算(乘以或添加到系列)
  • 常见聚合(平均值,最小值,最大值,总和等)
  • 调用apply(只要它在索引中 - 也就是说,不是在groupby('y')之后'y'不是索引 - )
  • 调用value_counts(),drop_duplicates()或corr()
  • 使用locisin和行方式选择进行过滤
#仅通过引用返回x> 5的行(写入它们会改变原始df)
df2 = df.loc[df['x'] > 5] 
#通过引用仅返回x为0,1,2,3或4的行
df3 = df.x.isin(range(4)) 
#只返回x> 5的行,只读参考(无法写入)
df4 = df[df['x']>5] 

如何使用Dask Dataframes

Dask Dataframes与Pandas Dataframes具有相同的API,除了聚合和apply s被懒惰地评估,并且需要通过调用compute方法来计算。为了生成Dask Dataframe,您可以像在Pandas中一样调用read_csv方法,或者,如果给出Pandas Dataframe df,您只需调用

dd = ddf.from_pandas(df,npartitions = N)

其中ddf是您导入Dask Dataframes的名称,而npartitions是一个参数,告诉Dataframe如何对其进行分区。

根据StackOverflow的说法,建议将Dataframe划分为与计算机所拥有的核心数量相同的分区,或者是该数量的几倍,因为每个分区将在不同的线程上运行,如果有的话,它们之间的通信将变得过于昂贵许多。

弄脏:让我们来做基准!

我制作了一个Jupyter笔记本来试用这个框架,并在Github提供它,以防你想要检查它甚至自己运行它。

我运行的基准测试可以在Github的笔记本中找到,但主要有:

def get_big_mean():
    return dfn.salary.mean().compute()
def get_big_mean_old():
    return df3.salary.mean()

def get_big_max():
    return dfn.salary.max().compute()
def get_big_max_old():
    return df3.salary.max()

def get_big_sum():
    return dfn.salary.sum().compute()
def get_big_sum_old():
    return df3.salary.sum()

def filter_df():
    df = dfn[dfn['salary']>5000]
def filter_df_old():
    df = df3[df3['salary']>5000]

这里df3是一个普通的Pandas Dataframe,拥有2500万行,使用上一篇文章中的脚本生成(列是名称,姓氏薪水,从列表中随机抽样)。我拿了50行数据集并连接了500000次,因为我对分析本身并不太感兴趣,但只是在运行它的时候。

dfn就是基于df3的Dask Dataframe 。

第一批结果:不太乐观

我首先尝试使用3个分区进行测试,因为我只有4个内核,并且不想过度使用我的PC。我和Dask的结果非常差,并且还要等很多才能得到它们,但我担心这可能是因为我制作的分区太少了:

204.313940048 seconds for get_big_mean
39.7543280125 seconds for get_big_mean_old
131.600986004 seconds for get_big_max
43.7621600628 seconds for get_big_max_old
120.027213097 seconds for get_big_sum
7.49701309204 seconds for get_big_sum_old
0.581165790558 seconds for filter_df
226.700095892 seconds for filter_df_old

当我使用Dask时,您可以看到大多数操作变得慢得多。这给了我一些暗示我可能不得不使用更多分区。产生惰性评估所花费的金额也可以忽略不计(在某些情况下不到半秒),所以如果重复使用它,它就不会随着时间的推移而摊销。

我也用apply方法尝试了这个测试:

def f(x):
    return (13*x+5)%7

def apply_random_old():
    df3['random']= df3['salary'].apply(f)
    
def apply_random():
    dfn['random']= dfn['salary'].apply(f).compute()

并有非常相似的结果:

369.541605949 seconds for apply_random
157.643756866 seconds for apply_random_old

因此,一般来说,大多数操作的速度都是原始操作的两倍,尽管过滤器的速度要快得多。我担心也许我应该在那个上调用计算器,所以把这个结果带上一粒盐。

更多分区:惊人的加速

在这些令人沮丧的结果之后,我决定我可能只是没有使用足够的分区。毕竟,这一点的重点是并行运行,所以也许我只需要更多并行化?所以我尝试了8个分区的相同测试,这就是我得到的(我省略了非并行数据帧的结果,因为它们基本相同):

3.08352184296 seconds for get_big_mean
1.3314101696 seconds for get_big_max
1.21639800072 seconds for get_big_sum
0.228978157043 seconds for filter_df
112.135010004 seconds for apply_random
50.2007009983 seconds for value_count_test

 那就对了!大多数操作的运行速度比常规Dataframe快十倍,甚至申请速度也更快!我还运行了value_count测试,它只调用salary系列上的value_count方法。对于上下文,请记住,在经过十分钟的等待后,我在常规Dataframe上运行此测试时必须终止该过程。这次只用了50秒! 所以基本上我只是使用错误的工具,而且它非常快。比常规Dataframe快很多。

最后

鉴于我们刚刚在一台相当旧的4核PC上运行了2500万行,我可以看到这在行业中将是多么巨大。所以我的建议是在下次必须在本地或从单个AWS实例处理数据集时尝试此框架。这很快。

原文:https://towardsdatascience.com/trying-out-dask-dataframes-in-python-for-fast-data-analysis-in-parallel-aa960c18a915

猜你喜欢

转载自blog.csdn.net/ChenVast/article/details/82114946