prodotto cartesiano in panda


109

Ho due dataframe panda:

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

Qual è la migliore pratica per ottenere il loro prodotto cartesiano (ovviamente senza scriverlo esplicitamente come me)?

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

Risposte:


88

Se hai una chiave che viene ripetuta per ogni riga, puoi produrre un prodotto cartesiano utilizzando merge (come faresti in 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']]

Produzione:

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

Vedere qui per la documentazione: http://pandas.pydata.org/pandas-docs/stable/merging.html#brief-primer-on-merge-methods-relational-algebra


6
Quindi, per farlo correttamente, devi prima trovare un nome di colonna inutilizzato, quindi aggiungere colonne fittizie con quel nome, unire e infine rilasciare la colonna sul risultato? Creare, al contrario della lettura, dati con i panda è solo un
problema

69

Usa pd.MultiIndex.from_productcome indice in un dataframe altrimenti vuoto, quindi reimposta il suo indice e il gioco è fatto.

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

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

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

su:

   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
Credo che questo sia il modo più simile ai panda in questi giorni per i panda> = 0,21
shadi

6
Hai dei voti negativi perché non hai mostrato come si generalizzerà per qualcosa con più di 1 colonna.
cs95

Questa funzione ( stackoverflow.com/a/58242079/1840471 ) la generalizza a un numero arbitrario di elenchi utilizzando un dict di argomenti. È un po 'diverso dalla domanda qui, che prende il prodotto cartesiano di due DataFrame (cioè non prende il prodotto di df1.col1e df.col2).
Max Ghenis

In effetti non credo from_productpossa essere utilizzato per questo problema.
Max Ghenis

34

Questo non vincerà una competizione di golf in codice e prende in prestito dalle risposte precedenti, ma mostra chiaramente come viene aggiunta la chiave e come funziona il join. Questo crea 2 nuovi frame di dati dagli elenchi, quindi aggiunge la chiave su cui eseguire il prodotto cartesiano.

Il mio caso d'uso era che avevo bisogno di un elenco di tutti gli ID negozio su ogni settimana nel mio elenco. Quindi, ho creato un elenco di tutte le settimane che volevo avere, quindi un elenco di tutti gli ID negozio su cui volevo mapparli.

L'unione che ho scelto è rimasta, ma sarebbe semanticamente uguale a quella interna in questa configurazione. Puoi vederlo nella documentazione sull'unione , che afferma che fa un prodotto cartesiano se la combinazione di tasti appare più di una volta in entrambe le tabelle, che è ciò che abbiamo impostato.

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
Versione un po 'più breve:days_and_stores = pd.merge(days.assign(key=0), stores.assign(key=0), on='key').drop('key', axis=1)
Eugene Pakhomov

Hai menzionato crossJoin, ma stai usando un dataframe panda, non un dataframe spark.
Bryce Guinta

Dang. Non stavo pensando. Uso Spark + Panda insieme così spesso che quando ho visto l'aggiornamento per Spark ho pensato a questo post. Grazie Bryce.
Rob Guderian

32

Codice minimo necessario per questo. Creare una 'chiave' comune per unire cartesiane le due:

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

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

8
+ df_cartesian = df_cartesian.drop(columns=['key'])per ripulire alla fine
StackG

22

Con il concatenamento del metodo:

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

14

In alternativa ci si può affidare al prodotto cartesiano fornito da itertools:, itertools.productche evita di creare una chiave temporanea o di modificare l'indice:

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)

Test veloce:

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
Ho provato questo e funziona, ma è molto più lento di quanto sopra unire le risposte per grandi set di dati.
MrJ

2

Se non hai colonne sovrapposte, non vuoi aggiungerne una e gli indici dei frame di dati possono essere scartati, questo potrebbe essere più semplice:

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

1
Sembra promettente, ma ottengo l'errore sulla prima riga: TypeError: '<class 'pandas.core.index.Int64Index'>' does not support mutable operations. posso aggirare questo problema aggiungendo , index=[0,0]alla definizione del dataframe.
Racing Tadpole

2
O usando df1 = df1.set_index([[0]*len(df1)]))(e allo stesso modo per df2).
Racing Tadpole

Le modifiche di Racing Tadpole hanno reso questo lavoro per me - grazie!
Sevyns

2

Ecco una funzione di supporto per eseguire un semplice prodotto cartesiano con due frame di dati. La logica interna gestisce l'utilizzo di una chiave interna ed evita di alterare le colonne che vengono chiamate "chiave" da entrambi i lati.

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)

Spettacoli:

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

ho fatto una doppia ripresa quando ho visto che una domanda di 7 anni aveva una risposta vecchia di 4 ore - grazie mille per questo :)
Bruno E

0

Potresti iniziare prendendo il prodotto cartesiano di df1.col1e df2.col3, quindi unire di nuovo df1per ottenere col2.

Ecco una funzione di prodotto cartesiano generale che accetta un dizionario di elenchi:

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

Applicare come:

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

Puoi usare numpy perché potrebbe essere più veloce. Supponiamo di avere due serie come segue,

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

Hai solo bisogno

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

-1

Trovo che l'utilizzo di panda MultiIndex sia lo strumento migliore per il lavoro. Se hai un elenco di elenchi lists_list, chiama pd.MultiIndex.from_product(lists_list)e ripeti il ​​risultato (o usalo nell'indice DataFrame).

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.