Tengo la siguiente matriz numpy:
import numpy as np pair_array = np.array([(205, 254), (205, 382), (254, 382), (18, 69), (205, 382), (31, 183), (31, 267), (31, 382), (183, 267), (183, 382)]) print(pair_array) #[[205 254] # [205 382] # [254 382] # [ 18 69] # [205 382] # [ 31 183] # [ 31 267] # [ 31 382] # [183 267] # [183 382]]¿Hay alguna forma de transformar esta matriz en un marco de datos de pandas simétrico que contenga el recuento de ocurrencias para todas las combinaciones posibles? Espero algo como esto:
# 18 31 69 183 205 254 267 382 # 18 0 0 1 0 0 0 0 0 # 31 0 0 0 1 0 0 1 1 # 69 1 0 0 0 0 0 0 0 # 183 0 1 0 0 0 0 1 1 # 205 0 0 0 0 0 1 0 2 # 254 0 0 0 0 1 0 0 1 # 267 0 1 0 1 0 0 0 0 # 382 0 1 0 1 2 1 0 0Si está de acuerdo en agregar pandas como una dependencia, puede usar esta implementación
>>> import pandas as pd >>> df = pd.DataFrame(pair_array) >>> pd.crosstab(df[0], df[1]) 1 69 183 254 267 382 0 18 1 0 0 0 0 31 0 1 0 1 1 183 0 0 0 1 1 205 0 0 1 0 2 254 0 0 0 0 1Esta es una crosstab :
pd.crosstab(pair_array[:,0], pair_array[:,1])Producción:
col_0 69 82 183 254 267 382 row_0 18 1 0 0 0 0 0 31 0 1 1 0 1 0 183 0 0 0 0 1 1 205 0 0 0 1 0 2 254 0 0 0 0 0 1Una forma podría ser construir un gráfico usando NetworkX y obtener la matriz de adyacencia directamente como un marco de datos con nx.to_pandas_adjacency . Para dar cuenta de las co-ocurrencias de los bordes en el gráfico, podemos crear un nx.MultiGraph , que permite múltiples bordes que conectan el mismo par de nodos:
import networkx as nx G = nx.from_edgelist(pair_array, create_using=nx.MultiGraph) nx.to_pandas_adjacency(G, nodelist=sorted(G.nodes()), dtype='int') 18 31 69 183 205 254 267 382 18 0 0 1 0 0 0 0 0 31 0 0 0 1 0 0 1 1 69 1 0 0 0 0 0 0 0 183 0 1 0 0 0 0 1 1 205 0 0 0 0 0 1 0 2 254 0 0 0 0 1 0 0 1 267 0 1 0 1 0 0 0 0 382 0 1 0 1 2 1 0 0 Construir un grafo NetworkX , también permitirá crear una matriz de adyacencia u otra dependiendo del comportamiento que esperamos. Podemos crearlo usando un:
nx.Graph : si queremos establecer en 1 ambas entradas (x,y) y ( y,x ) para un borde (x,y) (o (y,x) ). Por lo tanto, esto producirá una matriz de adyacencia simétricanx.DiGraph : si (x,y) solo debe establecer (x,y) la entrada en 1nx.MultiGraph : para el mismo comportamiento que un nx.Graph pero teniendo en cuenta las co-ocurrencias de bordenx.MultiDiGraph : para el mismo comportamiento que un nx.DiGraph pero también teniendo en cuenta las co-ocurrencias de bordeUna forma de hacerlo es pair_array con pair_array invertido en el eje 1, lo que se puede hacer usando [::-1] . Y para agregar use np.vstack / np.r_ / np.concatenate .
Ahora use pd.crosstab para realizar una tabulación cruzada.
all_vals = np.r_[pair_array, pair_array[:, ::-1]] pd.crosstab(all_vals[:, 0], all_vals[:, 1]) col_0 18 31 69 183 205 254 267 382 row_0 18 0 0 1 0 0 0 0 0 31 0 0 0 1 0 0 1 1 69 1 0 0 0 0 0 0 0 183 0 1 0 0 0 0 1 1 205 0 0 0 0 0 1 0 2 254 0 0 0 0 1 0 0 1 267 0 1 0 1 0 0 0 0 382 0 1 0 1 2 1 0 0 Como señaló @QuangHoang cuando hay pares idénticos que ocurren más de una vez, es decir [(18, 18), (18, 18), ...] , luego use
rev = pair_array[:, ::-1] m = (pair_array == rev) rev = rev[~np.all(m, axis=1)] all_vals = np.r_[pair_arr, rev]Puede crear un marco de datos del tamaño apropiado con ceros de antemano y simplemente incrementar las celdas apropiadas al recorrer los pares:
import numpy as np import pandas as pd pair_array = np.array([(205, 254), (205, 382), (254, 382), (18, 69), (205, 382), (31, 183), (31, 267), (31, 82), (183, 267), (183, 382)]) vals = sorted(set(pair_array.flatten())) n = len(vals) df = pd.DataFrame(np.zeros((n, n), dtype=np.int), columns=vals, index=vals) for r, c in pair_array: df.at[r, c] += 1 df.at[c, r] += 1 print(df)Producción:
18 31 69 82 183 205 254 267 382 18 0 0 1 0 0 0 0 0 0 31 0 0 0 1 1 0 0 1 0 69 1 0 0 0 0 0 0 0 0 82 0 1 0 0 0 0 0 0 0 183 0 1 0 0 0 0 0 1 1 205 0 0 0 0 0 0 1 0 2 254 0 0 0 0 0 1 0 0 1 267 0 1 0 0 1 0 0 0 0 382 0 0 0 0 1 2 1 0 0