декартовий продукт у пандах


108

У мене є два фрейми даних панд:

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]})

Відповіді:


88

Якщо у вас є ключ, який повторюється для кожного рядка, ви можете створити декартовий продукт за допомогою об'єднання (як у 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']]

Вихід:

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

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


6
Отже, щоб правильно це зробити, потрібно спочатку знайти невикористану назву стовпця, потім додати манекенні стовпці з таким ім'ям, об'єднати та, нарешті, скинути стовпець на результат? Створення, на відміну від читання, даних з пандами - це просто біль
Банах

68

Використовуйте 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

6
Я вважаю, що це найбільше подібний до панди в наші дні для панд> = 0,21
шаді

6
У вас є голоси, оскільки ви не показали, як це буде узагальнено для будь-якого з більш ніж 1 стовпцем.
cs95

Ця функція ( stackoverflow.com/a/58242079/1840471 ) узагальнює її до довільної кількості списків за допомогою dict аргументів. Це дещо відрізняється від питання, що стосується декартового продукту двох DataFrames (тобто він не бере продукт df1.col1і df.col2).
Макс Ghenis

Насправді я не думаю, що це from_productможе бути використане для цієї проблеми.
Макс Ghenis

34

Це не виграє змагання з гольфу на коді та запозичить попередні відповіді - але чітко показує, як додається ключ та як працює приєднання. При цьому створюються 2 нових кадри даних зі списків, після чого додається ключ, щоб зробити декартовий продукт.

У моєму випадку було те, що мені потрібен список усіх ідентифікаторів магазину на кожен тиждень у моєму списку. Отже, я створив список усіх тижнів, які я хотів мати, а потім список усіх ідентифікаторів магазину, проти яких я хотів їх скласти.

Злиття, яке я вибрав ліворуч, але буде семантично таким же, як і внутрішній у цій установці. Це ви можете побачити в документації про злиття , в якій зазначено, що це декартовий продукт, якщо комбінація клавіш з'являється неодноразово в обох таблицях - що ми і створили.

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)

25
Трохи коротша версія:days_and_stores = pd.merge(days.assign(key=0), stores.assign(key=0), on='key').drop('key', axis=1)
Євген Пахомов

Ви згадуєте crossJoin, але ви використовуєте фрейм даних pandas, а не іскровий фрейм даних.
Брайс Гінта

Данг. Не думав. Я використовую іскри + панди разом так часто, що, побачивши оновлення, щоб виблискувати, я подумав про цю публікацію. Спасибі Брайс.
Роб Гудеріан

32

Мінімальний код, необхідний для цього. Створіть загальний 'ключ' для декартового злиття двох:

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

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

8
+ df_cartesian = df_cartesian.drop(columns=['key'])прибирати наприкінці
StackG

22

З методом ланцюга:

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

14

Як альтернатива, можна покластися на декартовий продукт, наданий 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

4
Я перевірив це, і це працює, але це набагато повільніше, ніж вище відповіді на об'єднання для великих наборів даних.
1818 р.

2

Якщо у вас немає стовпців, що перекриваються, не потрібно додавати його, і індекси кадрів даних можуть бути відкинуті, це може бути простіше:

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

1
Це виглядає багатообіцяюче, але я отримую помилку в першому рядку: TypeError: '<class 'pandas.core.index.Int64Index'>' does not support mutable operations. я можу обійти це, додавши , index=[0,0]до визначення фрейму даних.
Гонки в Апостолі

2
Або використовуючи df1 = df1.set_index([[0]*len(df1)]))(і аналогічно для df2).
Гонки на Апостолі

Редагування гоночної Tadpole зробило цю роботу для мене - дякую!
Севінс

2

Ось допоміжна функція для виконання простого декартового продукту з двома кадрами даних. Внутрішня логіка обробляє за допомогою внутрішнього ключа і уникає керування будь-якими стовпцями, які, начебто, називаються "ключами" з обох сторін.

import pandas as pd

def cartesian(df1, df2):
    """Determine Cartesian product of two data frames."""
    key = 'key'
    while key in df1.columns or key in df2.columns:
        key = '_' + key
    key_d = {key: 0}
    return pd.merge(
        df1.assign(**key_d), df2.assign(**key_d), on=key).drop(key, axis=1)

# Two data frames, where the first happens to have a 'key' column
df1 = pd.DataFrame({'number':[1, 2], 'key':[3, 4]})
df2 = pd.DataFrame({'digit': [5, 6]})
cartesian(df1, df2)

показує:

   number  key  digit
0       1    3      5
1       1    3      6
2       2    4      5
3       2    4      6

зробив подвійний результат, коли побачив, що на 7-річне запитання було 4 години відповіді - велике спасибі за це :)
Бруно Е

0

Ви можете почати, взявши декартовий продукт df1.col1і df2.col3, потім, з’єднайтеся назад, df1щоб отримати col2.

Ось загальна функція декартового продукту, яка бере словник списків:

def cartesian_product(d):
    index = pd.MultiIndex.from_product(d.values(), names=d.keys())
    return pd.DataFrame(index=index).reset_index()

Застосувати як:

res = cartesian_product({'col1': df1.col1, 'col3': df2.col3})
pd.merge(res, df1, on='col1')
#  col1 col3 col2
# 0   1    5    3
# 1   1    6    3
# 2   2    5    4
# 3   2    6    4

0

Ви можете використовувати numpy як можна швидше. Припустимо, у вас є дві серії наступним чином,

s1 = pd.Series(np.random.randn(100,))
s2 = pd.Series(np.random.randn(100,))

Вам просто потрібно,

pd.DataFrame(
    s1[:, None] @ s2[None, :], 
    index = s1.index, columns = s2.index
)

-1

Я вважаю, що використання панд MultiIndex є найкращим інструментом для роботи. Якщо у вас є список списків lists_list, зателефонуйте pd.MultiIndex.from_product(lists_list)та повторіть результат (або використовуйте його в індексі DataFrame).

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.