Empresas
Empleos
  • Sobre nosotros
  • Soluciones
    • Publicación de vacantes
      Publica tu vacante y recibe candidatos calificados en 48h.
    • Evaluación de candidatos
      500+ pruebas técnicas y psicológicas, más anti-fraude.
    • Headhunting
      Búsqueda ejecutiva a la medida de principio a fin.
    • Nómina + EOR
      Dispersión de nómina y EOR en más de 15 países de LATAM.
  • Precios
  • Empleos

0

312
Vistas
¿Construir un cubo básico con numpy?

Me preguntaba si numpy podría usarse para construir el modelo de cubo más básico donde se almacenan todas las combinaciones cruzadas y su valor calculado.

Tomemos el siguiente ejemplo de datos:

 AUTHOR BOOK YEAR SALES Shakespeare Hamlet 2000 104.2 Shakespeare Hamlet 2001 99.0 Shakespeare Romeo 2000 27.0 Shakespeare Romeo 2001 19.0 Dante Inferno 2000 11.6 Dante Inferno 2001 12.6

Y poder construir algo como:

 YEAR TOTAL AUTHOR BOOK 2000 2001 (ALL) (ALL) 142.8 130.6 273.4 Shakespeare (ALL) 131.2 118.0 249.2 Dante (ALL) 11.6 12.6 24.2 Shakespeare Hamlet 104.2 99.0 203.2 Shakespeare Romeo 27.0 19.0 46.0 Dante Inferno 11.6 12.6 24.2

Espero que el uso de algo como meshgrid me lleve al 75% allí. Básicamente, me gustaría ver si es posible construir una estructura de todos los valores precalculados con numpy (no pandas) para construir una estructura para poder recuperar el resultado anterior de todas las combinaciones posibles. En aras de la simplicidad, consideremos solo la SUM como el único cálculo posible. Tal vez esta sea una forma redondeable de preguntar, pero ¿podría ser numpy la columna vertebral para hacer esto, o necesito usar algo más?

Y finalmente, si no es posible en numpy , ¿cómo podría almacenarse esto en un MDA?

over 4 years ago · Hanz Gallego
4 Respuestas
Responde la pregunta

0

Para la estructura de datos, podría definir la siguiente clase:

 class Cube: def __init__(self, row_index, col_index, data): self.row_index = {r: i for i, r in enumerate(row_index)} self.col_index = {c: i for i, c in enumerate(col_index)} self.data = data def __getitem__(self, item): row, col = item return self.data[self.row_index[row] , self.col_index[col]] def __repr__(self): return repr(self.data)

Básicamente, una envoltura ligera alrededor de una matriz numpy bidimensional. Para calcular la tabulación cruzada, podría hacer algo como esto:

 def _x_tab(rows, columns, values): """Function for computing the cross-tab of simple arrays""" unique_values_all_cols, idx = zip(*(np.unique(col, return_inverse=True) for col in [rows, columns])) shape_xt = [uniq_vals_col.size for uniq_vals_col in unique_values_all_cols] xt = np.zeros(shape_xt, dtype=np.float) np.add.at(xt, idx, values) return unique_values_all_cols, xt def make_index(a, r): """Make array of tuples""" l = [tuple(row) for row in a[:, r]] return make_object_array(l) def make_object_array(l): a = np.empty(len(l), dtype=object) a[:] = l return a def fill_label(ar, le): """Fill missing parts with ALL label""" missing = tuple(["ALL"] * le) return [(e + missing)[:le] for e in ar] def x_tab(rows, cols, values): """Main function for cross tabulation""" _, l_cols = rows.shape total_agg = [] total_idx = [] for i in range(l_cols + 1): (idx, _), agg = _x_tab(make_index(rows, list(range(i))), cols, values) total_idx.extend(fill_label(idx, l_cols)) total_agg.append(agg) stacked_agg = np.vstack(total_agg) stacked_agg_total = stacked_agg.sum(axis=1).reshape(-1, 1) return Cube(total_idx, list(dict.fromkeys(cols)), np.concatenate((stacked_agg, stacked_agg_total), axis=1))

Supongamos como entrada una matriz arr :

 [['Shakespeare' 'Hamlet' 2000 104.2] ['Shakespeare' 'Hamlet' 2001 99.0] ['Shakespeare' 'Romeo' 2000 27.0] ['Shakespeare' 'Romeo' 2001 19.0] ['Dante' 'Inferno' 2000 11.6] ['Dante' 'Inferno' 2001 12.6]]

Entonces x_tab se puede llamar así:

 result = x_tab(arr[:, [0, 1]], arr[:, 2], arr[:, 3]) print(result)

Producción

 array([[142.8, 130.6, 273.4], [ 11.6, 12.6, 24.2], [131.2, 118. , 249.2], [ 11.6, 12.6, 24.2], [104.2, 99. , 203.2], [ 27. , 19. , 46. ]])

Tenga en cuenta que esta representación (repr) es solo para mostrar los resultados, puede cambiarla cuando lo crea conveniente. Luego puede acceder a las celdas del cubo de la siguiente manera:

 print(result[('Dante', 'ALL'), 2001]) print(result[('Dante', 'Inferno'), 2001]) print(result[('Shakespeare', 'Hamlet'), 2000])

Producción

 12.6 12.6 104.2

Tenga en cuenta que la mayor parte de las operaciones se encuentran en la función _x_tab, que utiliza funciones numéricas puras. Al mismo tiempo, proporciona una interfaz flexible para cualquier función de agregación que elija, simplemente cambie ufunc en esta línea:

 np.add.at(xt, idx, values)

por cualquier otro de esta lista . Para obtener más información, consulte la documentación sobre el operador at .

Una copia de trabajo del código se puede encontrar aquí . Lo anterior se basa en esta esencia .

Nota Esto supone que está pasando varias columnas para el índice (parámetro de filas).

over 4 years ago · Hanz Gallego Denunciar

0

Solo la inicialización de la clase:

 import numpy as np class Olap: def __init__(self, values, headers, *locators): self.labels = [] self.indices = [] self.headers = headers self.shape = (len(l) for l in locators) for loc in locators: unique, ix = np.unique(loc, return_inverse = True) self.labels.append(unique) self.indices.append(ix) self.arr = np.zeros(self.shape) self.count = np.zeros(self.shape, dtype = int) np.add.at(self.arr, tuple(self.indices), values) np.add.at(self.count, tuple(self.indices), np.ones(values.shape)) author = 4*["Shakespeare"]+ 2*["Dante"] book = 2*["Hamlet"] + 2*["Romeo"] + 2*["Inferno"] year = 3*["2000", "2001"] sales = [104.2, 99.0, 27.0, 19.0, 11.6, 12.6] olap = Olap(sales, ["author", "book", "year"], author, book, year)

A partir de ahí, puede crear funciones de suma usando self.arr.sum() a lo largo de diferentes ejes, e incluso puede promediar usando self.count.sum() también. Probablemente querrá alguna forma de agregar más datos (una vez más usando np.add.at para ponerlos en arr ), pero su estructura de datos ahora es Nd en lugar de tabular, lo que debería brindarle los mismos beneficios para datos de alta dimensión. ese pivot lo hace.

No se trata de poner todo eso en el código (incluso para 400 repeticiones), pero no parece demasiado complejo una vez que crea la estructura de datos multidimensional.

over 4 years ago · Hanz Gallego Denunciar

0

Aquí hay un boceto de una solución, obviamente incluiría funciones y clases auxiliares para proporcionar una interfaz fácil. La idea es asignar cada nombre único a un índice (secuencial aquí para simplificar) y luego usarlo como índice para almacenar el valor en una matriz. Es subóptimo en el sentido de que debe rellenar una matriz al tamaño máximo de la mayor cantidad de elementos diferentes. De lo contrario, la matriz es cero, así que no se incluya en las sumas. Podría considerar matrices de máscaras y suma de máscaras si desea evitar agregar cero elementos.

 import numpy as np def get_dict(x): return {a:i for i, a in enumerate(set(x))} #Mapping name to unique contiguous numbers (obviously put in a fn or class) author = 4*["Shakespeare"]+ 2*["Dante"] book = 2*["Hamlet"] + 2*["Romeo"] + 2*["Inferno"] year = 3*["2000", "2001"] sales = [104.2, 99.0, 27.0, 19.0, 11.6, 12.6] #Define dictonary of indices d = get_dict(author) d.update(get_dict(book)) d.update(get_dict(year)) #Index values to put in multi-dimension array ai = [d[i] for i in author] bi = [d[i] for i in book] yi = [d[i] for i in year] #Pad array up to maximum size A = np.zeros([np.max(ai)+1, np.max(bi)+1, np.max(yi)+1]) #Store elements with unique name as index in 3D datacube for n in range(len(sales)): i = ai[n]; j = bi[n]; k = yi[n] A[i,j,k] = sales[n] #Now we can get the various sums, for example all sales print("Total=", np.sum(A)) #All shakespeare (0) print("All shakespeare=", np.sum(A[d["Shakespeare"],:,:])) #All year 2001 print("All year 2001", np.sum(A[:,:,d["2001"]])) #All Shakespeare in 2000 print("All Shakespeare in 2000", np.sum(A[d["Shakespeare"],:,d["2000"]]))
over 4 years ago · Hanz Gallego Denunciar

0

Creo que se pueden usar matrices de registros numpy para esta tarea, a continuación se muestra mi solución basada en matrices de registros.

 class rec_array(): def __init__(self,author=None,book=None,year=None,sales=None): self.dtype = [('author','<U20'), ('book','<U20'),('year','<U20'),('sales',float)] self.rec_array = np.rec.fromarrays((author,book,year,sales),dtype=self.dtype) def add_record(self,author,book,year,sales): new_rec = np.rec.fromarrays((author,book,year,sales),dtype=self.dtype) if not self.rec_array.shape == (): self.rec_array = np.hstack((self.rec_array,new_rec)) else: self.rec_array = new_rec def get_view(self,conditions): """ conditions: A list of conditions, for example [["author",<,"Shakespeare"],["year","<=","2000"]] """ mask = np.ones(self.rec_array.shape[0]).astype(bool) for item in conditions: field,op,target = item field_op = "self.rec_array['%s'] %s '%s'" % (field,op,target) mask &= eval(field_op) selected_sales = self.rec_array['sales'][mask] return np.sum(selected_sales)

Basado en este rec_array , dados los datos

 author = 4*["Shakespeare"]+ 2*["Dante"] book = 2*["Hamlet"] + 2*["Romeo"] + 2*["Inferno"] year = 3*["2000", "2001"] sales = [104.2, 99.0, 27.0, 19.0, 11.6, 12.6]

creamos una instancia

 test = rec_array() test.add_record(author,book,year,sales)

Si, por ejemplo, desea las ventas de Romeo de Shakespeare, simplemente puede hacer esto

 test.get_view([["author","==","Shakespeare"],["book","==","Romeo"]])

la salida es 46.0

o también puedes hacer

 test.get_view([["author","==","Shakespeare"],["year","<=","2000"]])

la salida es 131.2

over 4 years ago · Hanz Gallego Denunciar
Responde la pregunta
Encuentra empleos remotos

¡Descubre la nueva forma de encontrar empleo!

Top de empleos
Top categorías de empleo
Empresas
Publicar vacante Precios Comercial
Legal
Términos y condiciones Política de privacidad
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Recomiéndame algunas ofertas
Necesito ayuda