分布式Dask:并行读取和分析许多单个文件

时间:2019-04-15 20:43:44

标签: python dask dask-distributed pyarrow

问题

如何使用Dask Distributed将文件目录并行读取到各个DataFrame中,然后使用自定义函数对其进行处理?假设n个文件大约是100,000

背景

我是Dask的新手,也不怎么问这个问题(要使用哪些术语,等等),所以这是我要完成的工作的图片:

Overview

我有很多存储在HDFS中的小型.txt单独的“分类帐”文件(例如,带有时间戳的行分隔文件和时间戳的属性值)。

同时,我想...

  1. 将每个文件读入一个DataFrame中(注释:我尝试将所有文​​件组合成一个大df!);

  2. 对每个DataFrame应用自定义函数(请参见下文);然后

  3. 将每个结果(从自定义函数返回)合并到最终对象中,并将其保存回HDFS。

似乎(当使用Google搜索相关术语时)我发现的几乎所有答案都是关于将多个文件加载到单个数据框中的。

我正在处理的是我正在使用的功能

每个分类帐文件/数据框:

+---------+------+-------------------+-----+
| location|status|          timestamp|wh_id|
+---------+------+-------------------+-----+
|  PUTAWAY|     I|2019-04-01 03:14:00|   20|
|PICKABLE1|     X|2019-04-01 04:24:00|   20|
|PICKABLE2|     X|2019-04-01 05:33:00|   20|
|PICKABLE2|     A|2019-04-01 06:42:00|   20|
|  HOTPICK|     A|2019-04-10 05:51:00|   20|
| ICEXCEPT|     A|2019-04-10 07:04:00|   20|
| ICEXCEPT|     X|2019-04-11 09:28:00|   20|
+---------+------+-------------------+-----+

分析功能:

from dateutil.relativedelta import relativedelta
from datetime import datetime
from pyspark.sql.functions import to_timestamp

def analyze(df):

  columns_with_age = ("location", "status")
  columns_without_age = ("wh_id")

  # Get the most-recent values (from the last row of the df)
  row_count = df.count()
  last_row = df.collect()[row_count-1]

  # Create an empty "final row" dictionary
  final_row = {}

  # For each column for which we want to calculate an age value ...
  for c in columns_with_age:

      # Initialize loop values
      target_value = last_row.__getitem__(c)
      final_row[c] = target_value
      timestamp_at_lookback = last_row.__getitem__("timestamp")
      look_back = 1
      different = False

      while not different:
          previous_row = df.collect()[row_count - 1 - look_back]
          if previous_row.__getitem__(c) == target_value:
              timestamp_at_lookback = previous_row.__getitem__("timestamp")
              look_back += 1

          else:
              different = True

      # At this point, a difference has been found, so calculate the age
      final_row["days_in_{}".format(c)] = relativedelta(datetime.now(), timestamp_at_lookback).days

这样,分类帐数据/ DataFrame会减少为(假设计算是在2019-04-14进行的):

{ '_id': 'ledger-filename', 'location': 'ICEXCEPT', 'days_in_location': 4, 'status': 'X', 'days_in_status': 3, 'wh_id': 20 }

1 个答案:

答案 0 :(得分:0)

实际上不可能从多个进程并行写入一个输出文件,因为您不知道每个结果会预先存储多长时间,因此您也不知道文件中将其他结果放置在何处。而且,HDFS确实喜欢接收大块连续数据,而不是增量更新(也许是64MB)。

您可以做几件事:

  • 将所有输出写入单独的文件,然后运行单独的作业以将它们连接起来;如果与读/写时间相比,数据帧的处理量大,那将是一件很好的事情
  • 使用分布式client.submit API和as_completed将结果从主进程写入输出文件。请注意,如果这很重要,您可以使遵守原始顺序,但是这将需要一些额外的工作。