在Dask数据框子集上强制局部性

时间:2019-05-16 18:06:51

标签: dask dask-distributed

我正在尝试在多个计算机上分布一个大型的Dask数据帧,以便在该数据帧上进行(后来的)分布式计算。我为此使用了dask-distributed。

我看到的所有分发的示例/文档都是从网络资源(hdfs,s3等)填充初始数据负载,并且似乎没有将DAG优化扩展到负载部分(似乎假设网络负载是必不可少的邪恶,只会吃掉最初的成本。)在回答另一个问题:Does Dask communicate with HDFS to optimize for data locality?

时,强调了这一点。

但是,我看到了一些我们希望这样做的情况。例如,如果我们在此数据库的节点上共存有一个分片数据库+ dask worker,我们希望将仅来自本地分片的记录强制填充到本地dask worker中。从文档/示例来看,网络冲突似乎是必须承担的成本。 是否可以强制从特定工作人员那里获取单个数据框的一部分?

我尝试过的替代方法是尝试强制每个工作程序运行一个函数(迭代地提交给每个工作程序),其中该函数仅加载该计算机/分片本地的数据。这行得通,并且我有一堆具有相同列模式的最佳本地数据框-但是,现在我没有单个数据框,而是 n 个数据框。 是否可以在多台计算机上合并/融合数据帧,从而只有一个数据帧引用,但是部分数据与特定计算机具有亲和力(在一定程度上,由任务DAG决定)?

3 个答案:

答案 0 :(得分:3)

您可以生成诸如期货和延迟对象之类的数据框之类的淡淡“集合”,它们可以很好地互操作。

对于每个分区,您知道应该在哪个计算机上加载该分区,因此可以产生如下的结果:

Schema::create('users', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->bigInteger('company_id')->unsigned();
            $table->foreign('company_id')->references('id')->on('companies')->onDelete('cascade');
            $table->boolean('enable')->default(0);
            $table->string('name', 120)->nullable();
            $table->string('surname', 120)->nullable();
            $table->string('email', 120)->unique();
            $table->timestamp('email_verified_at')->nullable();
            $table->string('password');
            $table->bigInteger('counter')->default(0);
            $table->string('url_address', 160);
            $table->string('ip', 25)->nullable();
            $table->boolean('isCompany')->default(0);
            $table->boolean('isMailing')->default(0);
            $table->text('content')->nullable();
            $table->string('nip1', 12)->nullable();
            $table->string('business1', 120)->nullable();
            $table->string('phone1', 60)->nullable();
            $table->string('street1', 150)->nullable();
            $table->string('number1', 8)->nullable();
            $table->string('postal_code1', 12)->nullable();
            $table->string('city1', 100)->nullable();
            $table->bigInteger('country_id1')->default(0);
            $table->bigInteger('provincial_id1')->default(0);
            $table->string('nip2', 12)->nullable();
            $table->string('business2', 120)->nullable();
            $table->string('phone2', 60)->nullable();
            $table->string('street2', 150)->nullable();
            $table->string('number2', 8)->nullable();
            $table->string('postal_code2', 12)->nullable();
            $table->string('city2', 100)->nullable();
            $table->bigInteger('country_id2')->default(0);
            $table->bigInteger('provincial_id2')->default(0);
            $table->string('nip3', 12)->nullable();
            $table->string('business3', 120)->nullable();
            $table->string('phone3', 60)->nullable();
            $table->string('street3', 150)->nullable();
            $table->string('number3', 8)->nullable();
            $table->string('postal_code3', 12)->nullable();
            $table->string('city3', 100)->nullable();
            $table->bigInteger('country_id3')->default(0);
            $table->bigInteger('provincial_id3')->default(0);
            $table->decimal('cash', 9, 2)->default(0);
            $table->decimal('lng', 10, 8)->default(0);
            $table->decimal('lat', 10, 8)->default(0);
            $table->boolean('enable_map')->default(0);
            $table->rememberToken();
            $table->timestamps();
            $table->engine = "InnoDB";
        });

其中f = c.submit(make_part_function, args, workers={'my.worker.ip'}) 是dask客户端,而地址是您希望看到它的机器。您还可以给c是首选,而不是要求。

要创建一个数据框,可以从此类期货的列表中进行

allow_other_workers=True

,理想情况下提供df = dd.from_delayed([dask.delayed(f) for f in futures]) ,以描述预期的数据帧。现在,对给定分区的进一步操作将更喜欢安排在已经保存数据的同一工作器上。

答案 1 :(得分:1)

我也对具有将计算限制到特定节点(以及本地化到该节点的数据)的能力感兴趣。我试图用一个简单的脚本(见下文)来实现上面的内容,但是查看结果数据框会导致错误(来自dask / dataframe / utils.py :: check_meta()):

ValueError: Metadata mismatch found in `from_delayed`.

Expected partition of type `DataFrame` but got `DataFrame`

示例:

from dask.distributed import Client
import dask.dataframe as dd
import dask

client = Client(address='<scheduler_ip>:8786')
client.restart()

filename_1 = 'http://samplecsvs.s3.amazonaws.com/Sacramentorealestatetransactions.csv'
filename_2 = 'http://samplecsvs.s3.amazonaws.com/SalesJan2009.csv'

future_1 = client.submit(dd.read_csv, filename_1, workers='w1')
future_2 = client.submit(dd.read_csv, filename_2, workers='w2')

client.has_what()
# Returns: {'tcp://<w1_ip>:41942': ('read_csv-c08b231bb22718946756cf46b2e0f5a1',),
#           'tcp://<w2_ip>:41942': ('read_csv-e27881faa0f641e3550a8d28f8d0e11d',)}

df = dd.from_delayed([dask.delayed(f) for f in [future_1, future_2]])

type(df)
# Returns: dask.dataframe.core.DataFrame

df.head()
# Returns:
#      ValueError: Metadata mismatch found in `from_delayed`.
#      Expected partition of type `DataFrame` but got `DataFrame`

注意:dask环境具有两个工作程序节点(别名为w1和w2)和一个调度程序节点,并且脚本在外部主机上运行。 dask == 1.2.2,分布式== 1.28.1

答案 2 :(得分:0)

并行调用许多dask数据框函数很奇怪。也许您打算并行调用许多熊猫read_csv调用?

# future_1 = client.submit(dd.read_csv, filename_1, workers='w1')
# future_2 = client.submit(dd.read_csv, filename_2, workers='w2')
future_1 = client.submit(pandas.read_csv, filename_1, workers='w1')
future_2 = client.submit(pandas.read_csv, filename_2, workers='w2')

有关更多信息,请参见https://docs.dask.org/en/latest/delayed-best-practices.html#don-t-call-dask-delayed-on-other-dask-collections