декартово произведение в панд
у меня есть две панды 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 ответов:
Если у вас есть ключ, который повторяется для каждой строки, то вы можете создать декартово произведение с помощью слияния (как в 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))