produk cartesian di panda

109

Saya memiliki dua kerangka data panda:

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

Apa praktik terbaik untuk mendapatkan produk cartesian mereka (tentu saja tanpa menuliskannya secara eksplisit seperti saya)?

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

Jawaban:

88

Jika Anda memiliki kunci yang diulang untuk setiap baris, maka Anda dapat menghasilkan produk kartesius menggunakan merge (seperti yang Anda lakukan di 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']]

Keluaran:

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

Lihat di sini untuk dokumentasi: http://pandas.pydata.org/pandas-docs/stable/merging.html#brief-primer-on-merge-methods-relational-algebra

Matti John
sumber
6
Jadi untuk melakukan ini dengan benar seseorang harus terlebih dahulu menemukan nama kolom yang tidak digunakan, kemudian menambahkan kolom dummy dengan nama itu, menggabungkan, dan akhirnya menjatuhkan kolom pada hasil? Membuat, sebagai lawan membaca, data dengan panda hanya
merepotkan
69

Gunakan pd.MultiIndex.from_productsebagai indeks dalam kerangka data yang kosong, lalu setel ulang indeksnya, dan selesai.

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

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

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

di luar:

   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
Gijs
sumber
6
Saya percaya ini adalah cara paling mirip panda akhir-akhir ini untuk panda> = 0,21
shadi
6
Anda memiliki suara negatif karena Anda belum menunjukkan bagaimana hal ini akan digeneralisasikan untuk apa pun yang memiliki lebih dari 1 kolom.
cs95
Fungsi ini ( stackoverflow.com/a/58242079/1840471 ) menggeneralisasikannya ke sejumlah daftar acak menggunakan dikt argumen. Ini sedikit berbeda dari pertanyaan di sini, yang mengambil produk Cartesian dari dua DataFrame (yaitu tidak mengambil produk dari df1.col1dan df.col2).
Max Ghenis
Sebenarnya saya rasa tidak from_productbisa digunakan untuk masalah ini.
Max Ghenis
34

Ini tidak akan memenangkan kompetisi golf kode, dan meminjam dari jawaban sebelumnya - tetapi dengan jelas menunjukkan bagaimana kunci ditambahkan, dan bagaimana gabungan bekerja. Ini membuat 2 bingkai data baru dari daftar, lalu menambahkan kunci untuk menjalankan produk kartesius.

Kasus penggunaan saya adalah bahwa saya memerlukan daftar semua ID toko untuk setiap minggu dalam daftar saya. Jadi, saya membuat daftar semua minggu yang saya inginkan, lalu daftar semua ID toko yang ingin saya petakan.

Penggabungan yang saya pilih kiri, tetapi secara semantik sama dengan bagian dalam dalam pengaturan ini. Anda dapat melihat ini di dokumentasi tentang penggabungan , yang menyatakan bahwa itu adalah produk Cartesian jika kombinasi tombol muncul lebih dari sekali di kedua tabel - itulah yang kami siapkan.

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)
Rob Guderian
sumber
25
Versi yang sedikit lebih pendek:days_and_stores = pd.merge(days.assign(key=0), stores.assign(key=0), on='key').drop('key', axis=1)
Eugene Pakhomov
Anda menyebutkan crossJoin, tetapi Anda menggunakan bingkai data pandas, bukan bingkai data percikan.
Bryce Guinta
Dang. Tidak berpikir. Saya sering menggunakan spark + pandas bersama-sama, sehingga ketika saya melihat pembaruan untuk spark saya memikirkan posting ini. Terima kasih Bryce.
Rob Guderian
32

Kode minimal yang dibutuhkan untuk yang satu ini. Buat 'kunci' umum untuk menggabungkan keduanya:

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

df_cartesian = df1.merge(df2, how='outer')
A.Kot
sumber
8
+ df_cartesian = df_cartesian.drop(columns=['key'])untuk membersihkan di akhir
StackG
22

Dengan metode perangkaian:

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

Sebagai alternatif, seseorang dapat mengandalkan produk cartesian yang disediakan oleh itertools:, itertools.productyang menghindari pembuatan kunci sementara atau modifikasi indeks:

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)

Tes cepat:

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
Svend
sumber
4
Saya menguji ini dan berhasil, tetapi ini jauh lebih lambat daripada jawaban gabungan di atas untuk kumpulan data besar.
MrJ
2

Jika Anda tidak memiliki kolom yang tumpang tindih, tidak ingin menambahkannya, dan indeks bingkai data dapat dibuang, ini mungkin lebih mudah:

df1.index[:] = df2.index[:] = 0
df_cartesian = df1.join(df2, how='outer')
df_cartesian.index[:] = range(len(df_cartesian))
sergeyk
sumber
1
Ini terlihat menjanjikan - tetapi saya mendapatkan kesalahan pada baris pertama: TypeError: '<class 'pandas.core.index.Int64Index'>' does not support mutable operations. Saya bisa menyiasati ini dengan menambahkan , index=[0,0]ke definisi kerangka data.
Balapan Kecebong
2
Atau menggunakan df1 = df1.set_index([[0]*len(df1)]))(dan juga untuk df2).
Racing Tadpole
Hasil edit Racing Tadpole membuat ini berhasil untuk saya - terima kasih!
Sevyns
2

Berikut adalah fungsi pembantu untuk melakukan produk Cartesian sederhana dengan dua bingkai data. Logika internal menangani menggunakan kunci internal, dan menghindari merusak kolom apa pun yang kebetulan bernama "kunci" dari kedua sisi.

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)

acara:

   number  key  digit
0       1    3      5
1       1    3      6
2       2    4      5
3       2    4      6
Mike T
sumber
mengambil ganda ketika saya melihat bahwa pertanyaan berusia 7 tahun memiliki jawaban lama 4 jam - terima kasih banyak untuk ini :)
Bruno E
0

Anda bisa mulai dengan mengambil produk Kartesius dari df1.col1dan df2.col3, kemudian menggabungkan kembali df1untuk mendapatkancol2 .

Berikut adalah fungsi produk Cartesian umum yang mengambil kamus daftar:

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

Lamar sebagai:

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
Max Ghenis
sumber
0

Anda bisa menggunakan numpy karena bisa lebih cepat. Misalkan Anda memiliki dua rangkaian sebagai berikut,

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

Anda hanya perlu,

pd.DataFrame(
    s1[:, None] @ s2[None, :], 
    index = s1.index, columns = s2.index
)
Yanqi Huang
sumber
-1

Saya menemukan menggunakan pandas MultiIndex sebagai alat terbaik untuk pekerjaan itu. Jika Anda memiliki daftar daftar lists_list, panggil pd.MultiIndex.from_product(lists_list)dan ulangi hasilnya (atau gunakan dalam indeks DataFrame).

Ankur Kanoria
sumber