sản phẩm cacte ở gấu trúc


107

Tôi có hai khung dữ liệu gấu trúc:

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

Thực hành tốt nhất để có được sản phẩm cacte của họ (tất nhiên là không cần viết rõ ràng như tôi)?

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

Câu trả lời:


88

Nếu bạn có một khóa được lặp lại cho mỗi hàng, thì bạn có thể tạo ra một sản phẩm cacte bằng cách sử dụng hợp nhất (giống như bạn làm trong 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']]

Đầu ra:

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

Xem tài liệu tại đây: http://pandas.pydata.org/pandas-docs/stable/merging.html#brief-primer-on-merge-methods-relational-algebra


6
Vì vậy, để làm điều này đúng cách, trước tiên người ta phải tìm một tên cột không sử dụng, sau đó thêm các cột giả với tên đó, hợp nhất, và cuối cùng thả cột trên kết quả? Tạo, như trái ngược với đọc, dữ liệu với gấu trúc chỉ là một nỗi đau
Bananach

68

Sử dụng pd.MultiIndex.from_productlàm chỉ mục trong khung dữ liệu trống, sau đó đặt lại chỉ mục của nó và bạn đã hoàn tất.

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

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

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

ngoài:

   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
Tôi tin rằng đây là cách giống gấu trúc nhất hiện nay đối với gấu trúc> = 0,21
shadi

6
Bạn có phiếu phản đối vì bạn chưa chỉ ra cách tổng quát hóa điều này cho bất kỳ thứ gì có nhiều hơn 1 cột.
cs95

Hàm này ( stackoverflow.com/a/58242079/1840471 ) tổng quát hóa nó thành một số danh sách tùy ý bằng cách sử dụng mệnh đề là args. Nó hơi khác với câu hỏi ở đây, câu hỏi này lấy tích số Descartes của hai DataFrame (tức là nó không lấy tích của df1.col1df.col2).
Max Ghenis

Trong thực tế, tôi không nghĩ rằng from_productcó thể được sử dụng cho vấn đề này.
Max Ghenis

34

Điều này sẽ không giành chiến thắng trong một cuộc thi chơi gôn mã và vay mượn từ các câu trả lời trước đó - nhưng hiển thị rõ ràng cách khóa được thêm vào và cách tham gia hoạt động. Thao tác này tạo ra 2 khung dữ liệu mới từ danh sách, sau đó thêm khóa để thực hiện sản phẩm ca-ta.

Trường hợp sử dụng của tôi là tôi cần một danh sách tất cả các ID cửa hàng cho mỗi tuần trong danh sách của mình. Vì vậy, tôi đã tạo danh sách tất cả các tuần tôi muốn có, sau đó là danh sách tất cả các ID cửa hàng mà tôi muốn ánh xạ chúng.

Hợp nhất tôi đã chọn bên trái, nhưng sẽ giống về mặt ngữ nghĩa với bên trong trong thiết lập này. Bạn có thể thấy điều này trong tài liệu về hợp nhất , trong đó nói rằng nó thực hiện một tích Descartes nếu tổ hợp phím xuất hiện nhiều lần trong cả hai bảng - đó là những gì chúng tôi thiết lập.

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
Một cắn phiên bản ngắn hơn:days_and_stores = pd.merge(days.assign(key=0), stores.assign(key=0), on='key').drop('key', axis=1)
Eugene Pakhomov

Bạn đề cập đến crossJoin, nhưng bạn đang sử dụng khung dữ liệu gấu trúc, không phải khung dữ liệu tia lửa.
Bryce Guinta

Dang. Không nghĩ. Tôi sử dụng spark + pandas với nhau thường xuyên, đến nỗi khi tôi nhìn thấy bản cập nhật spark, tôi đã nghĩ về bài đăng này. Cảm ơn Bryce.
Rob Guderian

32

Mã tối thiểu cần thiết cho cái này. Tạo một 'khóa' chung để hợp nhất các-ten:

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

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

8
+ df_cartesian = df_cartesian.drop(columns=['key'])để dọn dẹp vào cuối
StackG

22

Với chuỗi phương thức:

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

14

Để thay thế, người ta có thể dựa vào sản phẩm cacte do itertools cung cấp:, itertools.producttránh tạo khóa tạm thời hoặc sửa đổi chỉ mục:

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)

Bài kiểm tra nhanh:

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
Tôi đã thử nghiệm điều này và nó hoạt động, nhưng nó chậm hơn nhiều so với các câu trả lời hợp nhất ở trên cho các tập dữ liệu lớn.
MrJ

2

Nếu bạn không có cột chồng chéo, không muốn thêm một cột và các chỉ số của khung dữ liệu có thể bị loại bỏ, điều này có thể dễ dàng hơn:

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

1
Điều này có vẻ hứa hẹn - nhưng tôi gặp lỗi ở dòng đầu tiên: TypeError: '<class 'pandas.core.index.Int64Index'>' does not support mutable operations. Tôi có thể giải quyết vấn đề này bằng cách thêm , index=[0,0]vào định nghĩa khung dữ liệu.
Đua xe Nòng nọc

2
Hoặc sử dụng df1 = df1.set_index([[0]*len(df1)]))(và tương tự cho df2).
Đua xe Nòng nọc

Các chỉnh sửa của Racing Tadpole đã thực hiện công việc này đối với tôi - cảm ơn!
Sevyns

2

Đây là một hàm trợ giúp để thực hiện một tích Descartes đơn giản với hai khung dữ liệu. Logic nội bộ xử lý bằng cách sử dụng khóa nội bộ và tránh làm xáo trộn bất kỳ cột nào có tên là "khóa" từ một trong hai bên.

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)

trình diễn:

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

đã tăng gấp đôi khi tôi thấy rằng một câu hỏi 7 tuổi có câu trả lời cách đây 4 giờ - cảm ơn rất nhiều vì điều này :)
Bruno E

0

Bạn có thể bắt đầu bằng cách lấy tích Descartes của df1.col1df2.col3, sau đó hợp nhất lại df1để lấy col2.

Đây là một hàm sản phẩm Descartes tổng quát lấy từ điển danh sách:

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

Nộp đơn như:

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

Bạn có thể sử dụng numpy vì nó có thể nhanh hơn. Giả sử bạn có hai chuỗi như sau,

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

Bạn chỉ cần,

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

-1

Tôi thấy việc sử dụng pandas MultiIndex là công cụ tốt nhất cho công việc. Nếu bạn có một danh sách các danh sách lists_list, hãy gọi pd.MultiIndex.from_product(lists_list)và lặp lại kết quả (hoặc sử dụng nó trong chỉ mục DataFrame).

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.