декартово произведение в панд


у меня есть две панды dataframes:

from pandas import DataFrame
df1 = DataFrame({'col1':[1,2],'col2':[3,4]})
df2 = DataFrame({'col3':[5,6]})     

какова лучшая практика, чтобы получить их декартово произведение (конечно, не записывая его явно, как я)?

#df1, df2 cartesian product
df_cartesian = DataFrame({'col1':[1,2,1,2],'col2':[3,4,3,4],'col3':[5,5,6,6]})
9 54

9 ответов:

Если у вас есть ключ, который повторяется для каждой строки, то вы можете создать декартово произведение с помощью слияния (как в SQL).

from pandas import DataFrame, merge
df1 = DataFrame({'key':[1,1], 'col1':[1,2],'col2':[3,4]})
df2 = DataFrame({'key':[1,1], 'col3':[5,6]})

merge(df1, df2,on='key')[['col1', 'col2', 'col3']]

смотрите здесь для документации: http://pandas.pydata.org/pandas-docs/stable/merging.html#brief-primer-on-merge-methods-relational-algebra

Это не выиграет соревнование по гольфу с кодом и заимствует из предыдущих ответов, но ясно показывает, как добавляется ключ и как работает соединение. Это создает 2 новых фрейма данных из списков, а затем добавляет ключ для выполнения декартового произведения.

мой вариант использования состоял в том, что мне нужен список всех идентификаторов магазина на каждую неделю в моем списке. Итак, я создал список всех недель, которые я хотел иметь, а затем список всех идентификаторов магазинов, с которыми я хотел сопоставить их.

слияние I выбрал left, но будет семантически таким же, как inner в этой настройке. Вы можете видеть это в документации по слиянию указано, что он делает декартово произведение, если комбинация клавиш появляется более одного раза в обеих таблицах - это то, что мы создали.

days = pd.DataFrame({'date':list_of_days})
stores = pd.DataFrame({'store_id':list_of_stores})
stores['key'] = 0
days['key'] = 0
days_and_stores = days.merge(stores, how='left', on = 'key')
days_and_stores.drop('key',1, inplace=True)

в качестве альтернативы можно полагаться на декартово произведение, предоставляемое itertools:itertools.product, что позволяет избежать создания временного ключа или изменения индекса:

import numpy as np 
import pandas as pd 
import itertools

def cartesian(df1, df2):
    rows = itertools.product(df1.iterrows(), df2.iterrows())

    df = pd.DataFrame(left.append(right) for (_, left), (_, right) in rows)
    return df.reset_index(drop=True)

быстрый тест:

In [46]: a = pd.DataFrame(np.random.rand(5, 3), columns=["a", "b", "c"])

In [47]: b = pd.DataFrame(np.random.rand(5, 3), columns=["d", "e", "f"])    

In [48]: cartesian(a,b)
Out[48]:
           a         b         c         d         e         f
0   0.436480  0.068491  0.260292  0.991311  0.064167  0.715142
1   0.436480  0.068491  0.260292  0.101777  0.840464  0.760616
2   0.436480  0.068491  0.260292  0.655391  0.289537  0.391893
3   0.436480  0.068491  0.260292  0.383729  0.061811  0.773627
4   0.436480  0.068491  0.260292  0.575711  0.995151  0.804567
5   0.469578  0.052932  0.633394  0.991311  0.064167  0.715142
6   0.469578  0.052932  0.633394  0.101777  0.840464  0.760616
7   0.469578  0.052932  0.633394  0.655391  0.289537  0.391893
8   0.469578  0.052932  0.633394  0.383729  0.061811  0.773627
9   0.469578  0.052932  0.633394  0.575711  0.995151  0.804567
10  0.466813  0.224062  0.218994  0.991311  0.064167  0.715142
11  0.466813  0.224062  0.218994  0.101777  0.840464  0.760616
12  0.466813  0.224062  0.218994  0.655391  0.289537  0.391893
13  0.466813  0.224062  0.218994  0.383729  0.061811  0.773627
14  0.466813  0.224062  0.218994  0.575711  0.995151  0.804567
15  0.831365  0.273890  0.130410  0.991311  0.064167  0.715142
16  0.831365  0.273890  0.130410  0.101777  0.840464  0.760616
17  0.831365  0.273890  0.130410  0.655391  0.289537  0.391893
18  0.831365  0.273890  0.130410  0.383729  0.061811  0.773627
19  0.831365  0.273890  0.130410  0.575711  0.995151  0.804567
20  0.447640  0.848283  0.627224  0.991311  0.064167  0.715142
21  0.447640  0.848283  0.627224  0.101777  0.840464  0.760616
22  0.447640  0.848283  0.627224  0.655391  0.289537  0.391893
23  0.447640  0.848283  0.627224  0.383729  0.061811  0.773627
24  0.447640  0.848283  0.627224  0.575711  0.995151  0.804567

использовать pd.MultiIndex.from_product как индекс в другом пустом фрейме данных, затем сбросьте его индекс, и все готово.

a = [1, 2, 3]
b = ["a", "b", "c"]

index = pd.MultiIndex.from_product([a, b], names = ["a", "b"])

pd.DataFrame(index = index).reset_index()

выход:

   a  b
0  1  a
1  1  b
2  1  c
3  2  a
4  2  b
5  2  c
6  3  a
7  3  b
8  3  c

минимальный код, необходимый для этого. Создайте общий "ключ" для декартового слияния двух:

df1['key'] = 0
df2['key'] = 0

df_cartesian = df1.merge(df2, how='outer')

с цепочкой:

product = (
    df1.assign(key=1)
    .merge(df2.assign(key=1), on="key")
    .drop("key", axis=1)
)

Если у вас нет перекрывающихся столбцов, не хотите добавлять один, и индексы фреймов данных могут быть отброшены, это может быть проще:

df1.index[:] = df2.index[:] = 0
df_cartesian = df1.join(df2, how='outer')
df_cartesian.index[:] = range(len(df_cartesian))

map и zip в понимании

DataFrame([
    d1 + d2
    for d1 in zip(*map(df1.get, df1))
    for d2 in zip(*map(df2.get, df2))
], columns=df1.columns.append(df2.columns))

   col1  col2  col3
0     1     3     5
1     1     3     6
2     2     4     5
3     2     4     6

Я считаю, что использование pandas MultiIndex является лучшим инструментом для работы. Если у вас есть список списков lists_list, называют pd.MultiIndex.from_product(lists_list) и повторите результат (или используйте его в индексе DataFrame).