python - Encuentra pares simétricos rápidamente en numpy
pandas (6)
Esta pregunta ya tiene una respuesta aquí:
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]
¿Cómo encuentro rápidamente identificar y eliminar el último duplicado de todos los pares simétricos en este marco de datos?
Por par simétrico quiero decir que
(0, 1)
es igual a
(1, 0)
.
Este último debe ser eliminado.
Tiene que ser rápido, por lo que se aprecian soluciones vacías. No hay conversión a objetos python por favor :)
frozenset
mask = pd.Series(map(frozenset, zip(df.c1, df.c2))).duplicated()
df[~mask]
Aquí hay uno basado en NumPy para enteros:
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
Si desea mantener los datos del índice tal como están, use
return df.iloc[np.sort(sidx[m])]
.
Para números genéricos (ints / floats, etc.), usaremos uno
view-based
:
# https://.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()
y simplemente reemplace el paso para obtener
idx
con
idx = view1D(b)
en
remove_symm_pairs
.
Puede ordenar los valores, luego
groupby
:
a= np.sort(df.to_numpy(), axis=1)
df.groupby([a[:,0], a[:,1]], as_index=False, sort=False).first()
Opción 2
: si tiene muchos pares
c1, c2
,
groupby
puede ser lento.
En ese caso, podemos asignar nuevos valores y filtrar por
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)
)
Si esto necesita ser
rápido
, y si sus variables son enteras, entonces el siguiente truco puede ayudar: sea
v,w
las columnas de su vector;
construir
[v+w, np.abs(vw)] =: [x, y]
;
luego ordene esta matriz lexicográficamente, elimine los duplicados y finalmente vuelva a asignarla a
[v, w] = [(x+y), (xy)]/2
.
Una forma es usar
np.unique
con
return_index=True
y usar el resultado para indexar el marco de datos:
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
...
voy a hacer
df[~pd.DataFrame(np.sort(df.values,1)).duplicated().values]
De pandas y 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()