Repita el análisis de cohorte de Power BI con Python

Buenas tardes, queridos lectores! La razón para escribir esta publicación fue un seminario web, que miré en Youtube. Se dedicó a un análisis de cohorte de ventas. El autor utilizó la plataforma Power BI Desktop para trabajar con datos. No proporcionaré un enlace al video especificado para que este artículo no se considere como un anuncio, pero en el transcurso de la narración intentaré hacer spoilers a la fuente original para explicar mejor la lógica de mi propia decisión. Este seminario web me dio la idea de que sería interesante repetir las posibilidades de las fórmulas DAX con las funciones de la biblioteca Pandas.

Dos puntos en los que quiero centrarme. En primer lugar, este material está destinado a analistas principiantes que recién están dando sus primeros pasos en el uso del lenguaje de programación Python. Ideal si los lectores están familiarizados con la plataforma analítica Power BI BI. En segundo lugar, dado que los cálculos DAX sirvieron como fuente de inspiración, "copiaré" los algoritmos del autor en la medida de lo posible, e inevitablemente habrá una desviación de los principales paradigmas de programación.

Con la introducción, eso es todo. ¡Vamos a la carretera!

Realizaremos todos los cálculos en el entorno JupyterLab. Las soluciones para computadoras portátiles se pueden encontrar en ( enlace ).

Los datos se cargan en Power BI utilizando la herramienta Power Query (de hecho, es un editor visual que genera consultas en el lenguaje M). Se debe seguir la siguiente regla durante el desarrollo: todo el preprocesamiento de datos se debe realizar con Power Query y las métricas se deben calcular con Power Pivot. Como nuestra biblioteca principal es Pandas, utilizamos sus capacidades de inmediato.

%%time
#   
path_to_data = "C:/Users/Pavel/Documents/Demo/"
# 
df = pd.read_csv(os.path.join(path_to_data, "ohortAnalysis_2016_2018.csv"), sep=";", parse_dates=["date"], dayfirst=True)

Probaremos las líneas de código en tiempo de ejecución para establecer las secciones que consumen más tiempo en el futuro. Para establecer la ruta completa al archivo de lectura, use la biblioteca os. Para simplificar el proceso de desarrollo de una computadora portátil, puede prescindir de ella. El conjunto de datos en sí está compuesto aleatoriamente. Hay 1.048.575 líneas en el archivo CSV. Leer datos con la función read_csv () suele ser sencillo. Es suficiente especificar el separador de columna y la columna con fechas, si las hay, en la matriz. Si la información se cargó con algunas "características", es posible que deba configurar parámetros adicionales, por ejemplo, especificando la codificación para cada columna.

La función head () a menudo se usará en el caso para monitorear visualmente el progreso de la transformación de datos. Todos los errores no se pueden cortar, pero los defectos obvios se pueden corregir en el acto.

Después de cargar los datos en el modelo, el autor del seminario web clasifica la matriz de datos. Esto se hace para agregar una columna auxiliar con índices. En nuestro caso, esta columna no se usará, pero los datos también se ordenarán para controlar más convenientemente el cálculo correcto de los campos en las tablas.

%%time
#  ,      
df.sort_values(["user_id","date"], inplace = True)

imagen

En el siguiente paso, la solución en la plataforma Power BI propone crear una tabla auxiliar, cuyos datos se extraerán en la matriz principal. La creación de una tabla se realiza utilizando la función SUMMARIZE (). Crea una tabla dinámica con totales agregados para grupos seleccionados: df_groupby_user = SUMMARIZE(df;df[user_id];"first_date_transaction";MIN(df[date]);"total_amount_user";SUM(df[amount]);"count_transaction_user";COUNT(df[amount]))

la biblioteca Pandas tiene su contraparte: la función groupby (). Para aplicar groupby () es suficiente especificar el marco de datos necesario, columnas agrupables, al final de la lista de las columnas para las que se aplicarán las funciones de agregación. El resultado obtenido se reduce al formato del marco de datos habitual mediante la función reset_index (). En conclusión, cambie el nombre de los campos.

%%time
#       user_id. 
df_groupby_user = df.groupby(by = ["user_id"]).agg({"date": "min", "amount": ["sum","count"]})
df_groupby_user.reset_index(inplace = True)
# 
new_columns = ["user_id","first_date_transaction", "total_amount_user","count_transaction_user"]
df_groupby_user.columns = new_columns

Además de la métrica de “primera fecha de compra”, se calcula el número de transacciones por cliente y el monto total de compras de clientes para todo el período. Las mediciones no encontraron su aplicación en la computadora portátil, pero no las eliminaremos.

Regresamos al webinar. Se calcula la nueva métrica "año-mes de la primera compra". Fórmula DAX: first_transaction = FORMAT(df_groupby_user[first_date_transaction];"YYYY-MM")

imagen

Python usa la sintaxis dt.strftime ('% Y-% m'). Encontrará una explicación detallada de cómo funciona en las publicaciones en línea sobre el trabajo con fecha y hora en Python. En este paso, algo más es importante. Presta atención al momento de la operación.

No tiene nada de pandas (24.8 segundos). La línea de código es más lenta que todas las anteriores.
Este listado se convierte en el primer candidato para una posible refactorización.

%%time
#  -
df_groupby_user["first_transaction"] = df_groupby_user["first_date_transaction"].dt.strftime('%Y-%m')

Es hora de volver al seminario web nuevamente. Hay una unión de tablas por un campo clave. Luego, los campos necesarios se introducen en la tabla principal utilizando la función RELATED (). Pandas no tiene esta característica. Pero hay merge (), join (), concat (). En este caso, es mejor aplicar la primera opción.

%%time
# 
df_final = pd.merge(df, df_groupby_user, how = "left", on = "user_id")

Después de que los datos con la fecha de la primera transacción cayeron en la tabla principal, puede calcular el delta. Usamos la construcción de aplicación (lambda x: ...) para demostrar claramente cuán intensivo es este proceso en recursos (39.7 segundos). Aquí hay otro candidato para la reescritura de código.

%%time
#   "    "
df_final["delta_days"] = df_final["date"] - df_final["first_date_transaction"]
df_final["delta_days"] = df_final["delta_days"].apply(lambda x: x.days)

La tabla principal ya tiene un delta por día, por lo que puede dividir los datos de la columna en cohortes. Principio: 0 (es decir, la primera venta al cliente) - cohorte 0; valores mayores que 0, pero menores o iguales a 30 son 30; valores mayores que 30, pero menores o iguales a 90 son 90, etc. Para estos fines, en DAX, puede usar la función CEILING (). Redondea el número al entero más cercano, un múltiplo del valor del segundo parámetro.

imagen

En Python, no encontré una función matemática similar, aunque planeé encontrarla en el módulo matemático (tal vez busqué mal). Por lo tanto, tuve que dar la vuelta y aplicar la función cut (). Después de difundir los datos en cohortes, NaN se asignó a valores numéricos de 0. No podemos resolver este problema utilizando la función fillna (), ya que estamos tratando con datos categóricos. Primero debe agregar un nuevo valor a la categoría. Al final de esta lista de códigos, cambie el tipo de datos a int. Esto se hace para que en el futuro, al construir una tabla dinámica utilizando un marco de datos, la nueva cohorte no aparezca al final de una serie de valores.

%%time
#  . 
cut_labels_days = [x for x in range (30, 1230, 30)]
cut_bins_days = [x for x in range (0, 1230, 30)]
df_final["cohort_days"] = pd.cut(df_final["delta_days"], bins = cut_bins_days, labels=cut_labels_days, right = True)
%%time
#     .   fillna   !
df_final["cohort_days"] = df_final["cohort_days"].cat.add_categories([0])
df_final["cohort_days"].fillna(0, inplace = True)
%%time
#     .   fillna   !
df_final["cohort_days"] = df_final["cohort_days"].cat.add_categories([0])
df_final["cohort_days"].fillna(0, inplace = True)
#    .    ,  "0"        ,     
#    .
df_final["cohort_days"] = df_final["cohort_days"].astype(int)

Usando la función pivot_table (), obtenemos la tabla dinámica deseada. Obtenemos bastantes cohortes, por lo que el resultado no se puede mostrar completamente en la pantalla. Para evitar esto, al resolver un caso real, puede tomar un intervalo de tiempo más corto para el análisis o ampliar los rangos de valores de las cohortes.

%%time
#  
df_pivot_table = pd.pivot_table(df_final, values=["amount"], index=["first_transaction"], columns=["cohort_days"], aggfunc=np.sum, fill_value = 0)

imagen

En Power BI, debe usar la herramienta Matriz para crear dicha visualización.

imagen

La siguiente etapa es trazar. El matiz de la situación es que necesitamos la cantidad sobre una base devengada. En Power BI, simplemente seleccione el elemento de menú 'Acción rápida' requerido y la fórmula DAX necesaria se generará automáticamente. Con la biblioteca Pandas, la situación es un poco más complicada. Agrupamos de forma secuencial doble el marco de datos existente y aplicamos la función cumsum (). Como el resultado aún se utilizará, haremos una copia del marco de datos para construir el gráfico. Los valores de ventas acumulados fueron bastante grandes, por lo que dividimos los valores por 1,000,000 y redondeamos el resultado a dos dígitos después del punto decimal.

%%time
#     amount
df_pivot_table_cumsum = df_final.groupby(by = ["first_transaction","cohort_days"]).agg({"amount": ["sum"]}).groupby(level=0).cumsum().reset_index()
df_pivot_table_cumsum.columns = ["first_transaction","cohort_days","cumsum_amount"]
%%time
#     
df_pivot_table_cumsum_chart = copy.deepcopy(df_pivot_table_cumsum)
#     ,       Y.
df_pivot_table_cumsum_chart["cumsum_amount"]=round(df_pivot_table_cumsum_chart["cumsum_amount"]/1000000, 2)

Utilizamos las capacidades de la biblioteca para construir el gráfico. Se construye un diagrama en una sola línea de código, pero el resultado no es impresionante. Este gráfico pierde claramente las visualizaciones en cualquier plataforma de BI. Puede conectar la biblioteca Plotly y conjurar con complementos, pero este es un costo de mano de obra completamente diferente en comparación con el enfoque que se muestra en el video.

%%time
df_pivot_table_cumsum_chart.pivot(index="cohort_days", columns="first_transaction", values="cumsum_amount").plot(figsize = (15,11))

imagen

Hagamos unas breves conclusiones.

En términos de cálculos, la biblioteca Pandas puede reemplazar Power Pivot (DAX).

La viabilidad de tal reemplazo permanece fuera de la conversación.

DAX, al igual que las funciones de la biblioteca Python, realiza un buen trabajo al realizar operaciones en campos completos de una tabla.

En términos de velocidad, simplicidad y facilidad de diseño de visualización, Power BI es superior a Pandas. En mi opinión, los gráficos incorporados (así como los creados con matplotlib, bibliotecas nacidas en el mar) son apropiados para aplicar en dos casos: análisis expreso de una serie de datos para valores atípicos, mínimos / máximos locales o preparación de diapositivas para su presentación. El desarrollo de paneles de control gráficos es mejor dejarlo a las soluciones de BI.

Eso es todo. ¡Toda salud, buena suerte y éxito profesional!

All Articles