Znajdź szybko pary symetryczne w 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]

Jak szybko znaleźć, zidentyfikować i usunąć ostatni duplikat wszystkich par symetrycznych w tej ramce danych?

Przykładem pary symetrycznej jest to, że „(0, 1)” jest równe „(1, 0)”. Ten ostatni należy usunąć.

Algorytm musi być szybki, dlatego zaleca się użycie numpy. Konwersja na obiekt python jest niedozwolona.


1
Czy możesz podać przykład tego, co rozumiesz symmetric pairs?
yatu

(0, 1) == (1,0) jest prawdą
The Unfun Cat

1
Czy (0, 1) == (0, 1) jest również Prawdą?
wundermahn

@JerryM. Tak, ale usunięcie go za pomocądf.drop_duplicates()
The Unfun Cat

2
@ molybdenum42 Używam produktu itertools do stworzenia przykładu, same dane nie są tworzone za pomocą produktu itertools.
The Unfun Cat

Odpowiedzi:


13

Możesz posortować wartości, a następnie groupby:

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

Opcja 2 : jeśli masz wiele par c1, c2, groupbymoże być powolny. W takim przypadku możemy przypisać nowe wartości i filtrować według 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

Jednym ze sposobów jest użycie np.uniquez return_index=Truei wykorzystać wynik do indeksowania dataframe:

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
Tak, inaczej unikalny nie wykryje par symetrycznych @DanielMesejo
yatu

Ok, rozumiem, więc sortujesz pary
Dani Mesejo

Tak, ale mam na myśli, że przekształcasz [1, 0] w [0, 1] prawda?
Dani Mesejo

6

frozenset

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

df[~mask]

1
Czy nie iterujesz powoli po krotkach nad każdą kolumną tutaj? Mimo to głosuj pozytywnie.
The Unfun Cat

Tak, iteruję. Nie, to nie jest tak wolne, jak myślisz.
piRSquared

5

zrobię

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

Od pand i 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

Oto jeden oparty na NumPy dla liczb całkowitych -

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

Jeśli chcesz zachować dane indeksu bez zmian, użyj return df.iloc[np.sort(sidx[m])].

W przypadku liczb ogólnych (ints / floats itp.) Użyjemy view-basedjednego -

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

i po prostu zastąpić krok, aby dostać idxsię idx = view1D(b)w remove_symm_pairs.


1

Jeśli to musi być szybkie , a twoje zmienne są liczbami całkowitymi, może pomóc następująca sztuczka: niech v,wbędą kolumny wektora; konstruować [v+w, np.abs(v-w)] =: [x, y]; następnie posortuj tę matrycę leksykograficznie, usuń duplikaty, a na końcu zamapuj ją z powrotem [v, w] = [(x+y), (x-y)]/2.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.