Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python Dask - vertical concatenation of 2 DataFrames

I am trying to vertically concatenate two Dask DataFrames

I have the following Dask DataFrame:

d = [
    ['A','B','C','D','E','F'],
    [1, 4, 8, 1, 3, 5],
    [6, 6, 2, 2, 0, 0],
    [9, 4, 5, 0, 6, 35],
    [0, 1, 7, 10, 9, 4],
    [0, 7, 2, 6, 1, 2]
    ]
df = pd.DataFrame(d[1:], columns=d[0])
ddf = dd.from_pandas(df, npartitions=5)

Here is the data as a Pandas DataFrame

          A         B      C      D      E      F
0         1         4      8      1      3      5
1         6         6      2      2      0      0
2         9         4      5      0      6     35
3         0         1      7     10      9      4
4         0         7      2      6      1      2

Here is the Dask DataFrame

Dask DataFrame Structure:
                   A      B      C      D      E      F
npartitions=4                                          
0              int64  int64  int64  int64  int64  int64
1                ...    ...    ...    ...    ...    ...
2                ...    ...    ...    ...    ...    ...
3                ...    ...    ...    ...    ...    ...
4                ...    ...    ...    ...    ...    ...
Dask Name: from_pandas, 4 tasks

I am trying to concatenate 2 Dask DataFrames vertically:

ddf_i = ddf + 11.5
dd.concat([ddf,ddf_i],axis=0)

but I get this error:

Traceback (most recent call last):
      ...
      File "...", line 572, in concat
        raise ValueError('All inputs have known divisions which cannot '
    ValueError: All inputs have known divisions which cannot be concatenated
    in order. Specify interleave_partitions=True to ignore order

However, if I try:

dd.concat([ddf,ddf_i],axis=0,interleave_partitions=True)

then it appears to be working. Is there a problem with setting this to True (in terms of performance - speed)? Or is there another way to vertically 2 concatenate Dask DataFrames?

like image 621
edesz Avatar asked May 05 '17 17:05

edesz


2 Answers

If you inspect the divisions of the dataframe ddf.divisions, you will find, assuming one partition, that it has the edges of the index there: (0, 4). This is useful to dask, as it knows when you do some operation on the data, not to use a partition not including required index values. This is also why some dask operations are much faster when the index is appropriate for the job.

When you concatenate, the second dataframe has the same index as the first. Concatenation would work without interleaving if the values of the index had different ranges in the two partitions.

like image 62
mdurant Avatar answered Oct 18 '22 04:10

mdurant


mdurant's answer is correct and this answer elaborate with MCVE code snippets using Dask v2021.08.1. Examples make it easier to understand divisions and interleaving.

Vertically concatenating DataFrames

Create two DataFrames, concatenate them, and view the results.

df = pd.DataFrame(
    {"nums": [1, 2, 3, 4, 5, 6], "letters": ["a", "b", "c", "d", "e", "f"]}
)
ddf1 = dd.from_pandas(df, npartitions=2)

df = pd.DataFrame({"nums": [88, 99], "letters": ["xx", "yy"]})
ddf2 = dd.from_pandas(df, npartitions=1)

ddf3 = dd.concat([ddf1, ddf2])

print(ddf3.compute())

   nums letters
0     1       a
1     2       b
2     3       c
3     4       d
4     5       e
5     6       f
0    88      xx
1    99      yy

Divisions metadata when vertically concatenating

Create two DataFrames, concatenate them, and illustrate that sometimes this operation will cause divisions metadata to be lost.

def print_partitions(ddf):
    for i in range(ddf.npartitions):
        print(ddf.partitions[i].compute())

df = pd.DataFrame(
    {"nums": [1, 2, 3, 4, 5, 6], "letters": ["a", "b", "c", "d", "e", "f"]}
)
ddf1 = dd.from_pandas(df, npartitions=2)

ddf1.divisions # (0, 3, 5)

df = pd.DataFrame({"nums": [88, 99], "letters": ["xx", "yy"]})
ddf2 = dd.from_pandas(df, npartitions=1)

ddf2.divisions # (0, 1)

ddf3 = dd.concat([ddf1, ddf2])

ddf3.divisions # (None, None, None, None)

Set interleave_partitions=True to avoid losing the divisions metadata.

ddf3_interleave = dd.concat([ddf1, ddf2], interleave_partitions=True)

ddf3_interleave.divisions # (0, 1, 3, 5)

When interleaving isn't necessary

Create two DataFrames without overlapping divisions, concatenate them, and confirm that the divisions metadata is not lost:

df = pd.DataFrame(
    {"nums": [1, 2, 3, 4], "letters": ["a", "b", "c", "d"], "some_index": [4, 5, 6, 7]}
)
ddf1 = dd.from_pandas(df, npartitions=2)

ddf1 = ddf1.set_index("some_index")

df = pd.DataFrame({"nums": [88, 99], "letters": ["xx", "yy"], "some_index": [10, 20]})
ddf2 = dd.from_pandas(df, npartitions=1)

ddf2 = ddf2.set_index("some_index")

ddf3 = dd.concat([ddf1, ddf2])

ddf3.divisions # (4, 6, 10, 20)

I wrote a blog post to explain this in more detail. Let me know if you'd like the link.

like image 35
Powers Avatar answered Oct 18 '22 03:10

Powers