在Dask中返回数据框

时间:2018-07-30 18:47:58

标签: python pandas dask

目标:要加快在大型数据帧(190万〜行)中逐行应用功能

尝试::使用dask map_partitions,其中partitions ==核心数。我编写了一个应用于每行的函数,创建了一个包含可变数量的新值(在1到55之间)的字典。此功能可以独立运行。

问题:我需要一种将每个函数的输出组合到最终数据框中的方法。我尝试使用df.append,在其中将每个字典追加到新的数据帧并返回此数据帧。如果我了解Dask文件,那么Dask应该将它们合并为一个大型DF。不幸的是,此行触发错误( ValueError:无法将输入数组从形状(56)广播到形状(1))。这使我相信这与Dask中的Combine功能有关?

#Function to applied row wise down the dataframe. Takes a column (post) and new empty df. 
def func(post,New_DF):
    post = str(post)
    scores = OtherFUNC.countWords(post)
    scores['post'] = post
    New_DF = New_DF.append(scores, ignore_index=True)
    return(New_DF)

#Dask 
dd.from_pandas(dataset,npartitions=nCores).\
 map_partitions(
      lambda df : df.apply(
         lambda x : func(x.post,New_DF),axis=1)).\
   compute(get=get)

1 个答案:

答案 0 :(得分:1)

我不太确定我能完全理解您的代码来代替MCVE,但我认为这里有些误解。

在这段代码中,您需要一行和一个DataFrame并将一行附加到该DataFrame。

#Function to applied row wise down the dataframe. Takes a column (post) and new empty df. 
def func(post,New_DF):
    post = str(post)
    scores = OtherFUNC.countWords(post)
    scores['post'] = post
    New_DF = New_DF.append(scores, ignore_index=True)
    return(New_DF)

我建议不要返回附加到New_DF的{​​{1}},而不是附加到pd.Series。这是因为,如果要在所有df.apply分区中附加相同的DataFrame对象,那么肯定会遇到麻烦。

New_DF

您的错误还表明,正如您在问题中所写的那样,您的函数创建了数量可变的值。如果您返回的nCores具有不同的形状和列名,那么 #Function to applied row wise down the dataframe. Takes a row and returns a row. def tobsecret_func(row): post = str(row.post) scores = OtherFUNC.countWords(post) scores['post'] = post length_adjusted_series = pd.Series(scores).reindex(range(55)) return(length_adjusted_series) 将无法将它们连接成pd.Series。因此,请确保每次返回的df.apply形状相同。该问题向您展示如何创建长度和索引相等的pd.DataFramePandas: pad series on top or bottom

我不知道您的pd.Series返回的是哪种pd.Series,因此您可能需要调整以下行:  dict

按原样,该行将返回一个带有索引0、1、2,...,54和最多55个值的Series(如果dict最初的键数少于55个,则其余单元格将包含{{1 }}值)。 这意味着在应用于OtherFUNC.countWords之后,该DataFrame的列将被命名为0、1、2,...,54。

现在,您将length_adjusted_series = pd.Series(scores).reindex(range(55))映射到每个分区,并在每个分区中使用NaN将其应用于DataFrame

dataset

DataFrame需要一个函数,该函数将一个DataFrame作为输入并输出一个DataFrame。您的函数通过使用lambda函数来执行此操作,该函数基本上会调用您的其他函数并将其应用到DataFrame,而后者又返回一个DataFrame。这行得通,但我强烈建议编写一个命名函数,该函数将一个DataFrame作为输入并输出一个DataFrame,这样可以使您更轻松地调试代码。

例如,具有如下简单的包装函数:

apply

尤其是当您的代码变得更加复杂时,放弃使用调用诸如您的自定义#Dask dd.from_pandas(dataset,npartitions=nCores).\ map_partitions( lambda df : df.apply( lambda x : func(x.post,New_DF),axis=1)).\ compute(get=get) 之类的非平凡代码的map_partitions函数,而是制作一个简单的命名函数可以帮助您调试,因为回溯不会就像在代码中一样,仅将您引向带有一堆lambda函数的行,但也将直接指向命名函数df_wise(df): return df.apply(tobsecret_func) ,因此您将确切地看到错误的出处。

lambda

请注意,我们刚刚将func馈送到df_wise来创建我们的元关键字,该关键字与Dask在幕后的操作类似。

您正在使用dask.get(同步调度程序),这就是整个New_DF.append(...)代码可以工作的原因,因为您为每个连续的分区追加了DataFrame。

这不会给您任何并行性,因此,如果您使用其他调度程序之一,则所有这些调度程序都会并行化您的代码,因此它将不起作用。

documentation还提到了#Dask dd.from_pandas(dataset,npartitions=nCores).\ map_partitions(df_wise, meta=df_wise(dd.head()) ).\ compute(get=get) 关键字参数,您应该在dd.head()调用中提供该关键字参数,因此,dask知道您的DataFrame将具有哪些列。如果您不这样做,dask首先必须在一个分区上对函数进行试运行,然后检查输出的形状,然后再继续处理其他分区。如果您的分区很大,这会使您的代码减慢一吨。赋予关键字df_wise可以绕过这种不必要的计算。