Temukan pasangan simetris dengan cepat di 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]

Bagaimana cara cepat menemukan, mengidentifikasi, dan menghapus duplikat terakhir dari semua pasangan simetris dalam bingkai data ini?

Contoh pasangan simetris adalah '(0, 1)' sama dengan '(1, 0)'. Yang terakhir harus dihapus.

Algoritma harus cepat, jadi disarankan untuk menggunakan numpy. Konversi ke objek python tidak diizinkan.

Kucing Unfun
sumber
1
Bisakah Anda memberi contoh apa yang Anda pahami symmetric pairs?
yatu
(0, 1) == (1,0) Benar
Kucing Unfun
1
Apakah (0, 1) == (0, 1) juga Benar?
wundermahn
@ JerryM. Ya, tapi sepele untuk menghapus dengandf.drop_duplicates()
The Unfun Cat
2
@ molybdenum42 Saya menggunakan produk itertools untuk membuat contoh, data itu sendiri tidak dibuat dengan produk itertools.
The Unfun Cat

Jawaban:

13

Anda dapat mengurutkan nilai, lalu groupby:

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

Opsi 2 : Jika Anda memiliki banyak pasangan c1, c2, groupbybisa lambat. Dalam hal ini, kami dapat menetapkan nilai baru dan memfilter menurut 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)
)
Quang Hoang
sumber
7

Salah satu cara menggunakan np.uniquedengan return_index=Truedan menggunakan hasilnya untuk mengindeks kerangka data:

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
...
yatu
sumber
1
Ya jika tidak, unik gagal mendeteksi pasangan simetris @DanielMesejo
yatu
Oke, saya mengerti jadi Anda sedang menyortir pasangan
Dani Mesejo
Ya tapi maksud saya Anda mengubah [1, 0] menjadi [0, 1] kan?
Dani Mesejo
6

frozenset

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

df[~mask]
piRquared
sumber
1
Apakah Anda tidak mengulangi tuple secara perlahan pada setiap kolom di sini? Tetap saja, jengkel.
The Unfun Cat
Ya, saya mengulangi. Tidak, itu tidak selambat yang Anda pikirkan.
piRSquared
5

Saya akan lakukan

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

Dari panda dan numpy tri

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

Inilah satu berbasis NumPy untuk bilangan bulat -

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

Jika Anda ingin menyimpan data indeks apa adanya, gunakan return df.iloc[np.sort(sidx[m])].

Untuk nomor generik (ints / float, dll.), Kami akan menggunakan view-basedsatu -

# 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()

dan hanya mengganti langkah untuk mendapatkan idxdengan idx = view1D(b)di remove_symm_pairs.

Divakar
sumber
1

Jika ini perlu cepat , dan jika variabel Anda integer, maka trik berikut ini dapat membantu: biarkan v,wmenjadi kolom vektor Anda; membangun [v+w, np.abs(v-w)] =: [x, y]; kemudian urutkan matriks ini secara leksikografis, hapus duplikat, dan akhirnya petakan kembali [v, w] = [(x+y), (x-y)]/2.

Federico Poloni
sumber