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.


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

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
...

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]

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

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.


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.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.