Tìm cặp đối xứng nhanh chóng trong numpy


15
from itertools import product
import pandas as pd

df = pd.DataFrame.from_records(product(range(10), range(10)))
df = df.sample(90)
df.columns = "c1 c2".split()
df = df.sort_values(df.columns.tolist()).reset_index(drop=True)
#     c1  c2
# 0    0   0
# 1    0   1
# 2    0   2
# 3    0   3
# 4    0   4
# ..  ..  ..
# 85   9   4
# 86   9   5
# 87   9   7
# 88   9   8
# 89   9   9
# 
# [90 rows x 2 columns]

Làm cách nào để nhanh chóng tìm, xác định và xóa bản sao cuối cùng của tất cả các cặp đối xứng trong khung dữ liệu này?

Một ví dụ về cặp đối xứng là '(0, 1)' bằng '(1, 0)'. Cái sau nên được loại bỏ.

Thuật toán phải nhanh, vì vậy nên sử dụng numpy. Chuyển đổi sang đối tượng python không được phép.


1
Bạn có thể cho một ví dụ về những gì bạn hiểu bằng cách symmetric pairs?
yatu

(0, 1) == (1,0) là Đúng
Mèo Unun

1
Có phải (0, 1) == (0, 1) cũng đúng không?
wundermahn

@JerryM. Có, nhưng thật là tầm thường khi gỡ bỏdf.drop_duplicates()
Con mèo bất khuất

2
@ molybdenum42 Tôi sử dụng sản phẩm itertools để tạo một ví dụ, bản thân dữ liệu không được tạo bằng sản phẩm itertools.
Mèo Unun

Câu trả lời:


13

Bạn có thể sắp xếp các giá trị, sau đó groupby:

a= np.sort(df.to_numpy(), axis=1)
df.groupby([a[:,0], a[:,1]], as_index=False, sort=False).first()

Tùy chọn 2 : Nếu bạn có nhiều cặp c1, c2, groupbycó thể chậm. Trong trường hợp đó, chúng ta có thể gán các giá trị và bộ lọc mới bằng cách drop_duplicates:

a= np.sort(df.to_numpy(), axis=1) 

(df.assign(one=a[:,0], two=a[:,1])   # one and two can be changed
   .drop_duplicates(['one','two'])   # taken from above
   .reindex(df.columns, axis=1)
)

7

Một cách là sử dụng np.uniquevới return_index=Truevà sử dụng kết quả để lập chỉ mục khung dữ liệu:

a = np.sort(df.values)
_, ix = np.unique(a, return_index=True, axis=0)

print(df.iloc[ix, :])

    c1  c2
0    0   0
1    0   1
20   2   0
3    0   3
40   4   0
50   5   0
6    0   6
70   7   0
8    0   8
9    0   9
11   1   1
21   2   1
13   1   3
41   4   1
51   5   1
16   1   6
71   7   1
...

1
Có, nếu không, duy nhất không phát hiện được các cặp đối xứng @DanielMesejo
yatu

Ok, tôi hiểu rồi, bạn đang sắp xếp các cặp
Dani Mesejo

Có nhưng ý tôi là bạn biến [1, 0] thành [0, 1] phải không?
Dani Mesejo

6

frozenset

mask = pd.Series(map(frozenset, zip(df.c1, df.c2))).duplicated()

df[~mask]

1
Không phải bạn đang lặp đi lặp lại từ từ trên các cột ở đây sao? Tuy nhiên, upvote.
Mèo Unun

Vâng, tôi đang lặp đi lặp lại. Không, nó không chậm như bạn nghĩ.
piRSquared

5

tôi sẽ làm

df[~pd.DataFrame(np.sort(df.values,1)).duplicated().values]

Từ gấu trúc và tri kỉ

s=pd.crosstab(df.c1,df.c2)
s=s.mask(np.triu(np.ones(s.shape)).astype(np.bool) & s==0).stack().reset_index()

5

Đây là một NumPy dựa trên một số nguyên -

def remove_symm_pairs(df):
    a = df.to_numpy(copy=False)
    b = np.sort(a,axis=1)
    idx = np.ravel_multi_index(b.T,(b.max(0)+1))
    sidx = idx.argsort(kind='mergesort')
    p = idx[sidx]
    m = np.r_[True,p[:-1]!=p[1:]]
    a_out = a[np.sort(sidx[m])]
    df_out = pd.DataFrame(a_out)
    return df_out

Nếu bạn muốn giữ dữ liệu chỉ mục như hiện tại, hãy sử dụng return df.iloc[np.sort(sidx[m])].

Đối với các số chung (ints / float, v.v.), chúng tôi sẽ sử dụng một view-based-

# https://stackoverflow.com/a/44999009/ @Divakar
def view1D(a): # a is array
    a = np.ascontiguousarray(a)
    void_dt = np.dtype((np.void, a.dtype.itemsize * a.shape[1]))
    return a.view(void_dt).ravel()

và chỉ cần thay thế các bước để có được idxvới idx = view1D(b)trong remove_symm_pairs.


1

Nếu điều này cần phải nhanh và nếu các biến của bạn là số nguyên, thì mẹo sau đây có thể giúp: hãy v,wlà các cột của vectơ của bạn; xây dựng [v+w, np.abs(v-w)] =: [x, y]; sau đó sắp xếp ma trận này theo từ vựng, loại bỏ các bản sao và cuối cùng ánh xạ lại [v, w] = [(x+y), (x-y)]/2.

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.