Repita a análise de coorte do Power BI usando Python

Boa tarde, queridos leitores! O motivo para escrever esta publicação foi um webinar, que eu analisei no Youtube. Ele se dedicou a uma análise de coorte de vendas. O autor usou a plataforma Power BI Desktop para trabalhar com dados. Não fornecerei um link para o vídeo especificado, para que este artigo não seja considerado um anúncio publicitário, mas, no decorrer da narração, tentarei fazer spoilers da fonte original para explicar melhor a lógica da minha própria decisão. Este webinar me deu a ideia de que seria interessante repetir as possibilidades das fórmulas DAX com as funções da biblioteca Pandas.

Dois pontos que eu quero focar. Primeiramente, este material é destinado a analistas iniciantes que estão apenas dando os primeiros passos no uso da linguagem de programação Python. Ideal se os leitores estiverem familiarizados com a plataforma de análise do Power BI BI. Em segundo lugar, como os cálculos do DAX serviram como fonte de inspiração, "copiarei" os algoritmos do autor o máximo possível e, inevitavelmente, haverá um afastamento dos principais paradigmas de programação.

Com a introdução, isso é tudo. Vamos pegar a estrada!

Realizaremos todos os cálculos no ambiente JupyterLab. As soluções para laptops podem ser encontradas em ( link ).

Os dados são carregados no Power BI usando a ferramenta Power Query (na verdade, é um editor visual que gera consultas no idioma M). A regra a seguir deve ser seguida durante o desenvolvimento: todo o pré-processamento de dados deve ser feito usando o Power Query e as métricas devem ser calculadas usando o Power Pivot. Como nossa biblioteca principal é o Pandas, usamos imediatamente seus recursos.

%%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)

Testaremos as linhas de código em tempo de execução para estabelecer as seções mais demoradas no futuro. Para definir o caminho completo para o arquivo lido, use a biblioteca os. Para simplificar o processo de desenvolvimento de um laptop, você pode ficar sem ele. O próprio conjunto de dados é composto aleatoriamente. Existem 1.048.575 linhas no arquivo CSV. A leitura de dados com a função read_csv () geralmente é simples. É suficiente especificar o separador de colunas e a coluna com datas, se houver, na matriz. Se as informações foram carregadas com alguns "recursos", pode ser necessário configurar parâmetros adicionais, por exemplo, especificando a codificação para cada coluna.

A função head () geralmente será usada no caso para monitorar visualmente o progresso da transformação de dados. Todos os erros não podem ser eliminados, mas falhas óbvias podem ser corrigidas no local.

Depois de carregar os dados no modelo, o autor do webinar classifica a matriz de dados. Isso é feito para adicionar uma coluna auxiliar com índices. No nosso caso, essa coluna não será usada, mas os dados também serão classificados para controlar de forma mais conveniente o cálculo correto dos campos nas tabelas.

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

imagem

Na próxima etapa, a solução na plataforma Power BI propõe a criação de uma tabela auxiliar, cujos dados serão puxados para a matriz principal. A criação de uma tabela é feita usando a função SUMMARIZE (). Ele cria uma tabela dinâmica com totais agregados para grupos selecionados: 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]))

A biblioteca do Pandas tem sua contrapartida - a função groupby (). Para aplicar groupby (), basta especificar o quadro de dados necessário, colunas agrupáveis, no final, lista as colunas às quais as funções de agregação serão aplicadas. O resultado obtido é reduzido ao formato do quadro de dados usual pela função reset_index (). Em conclusão, renomeie os 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

Além da métrica "primeira data da compra", são calculados o número de transações por cliente e o valor total de compras do cliente para todo o período. As medidas não encontraram sua aplicação no laptop, mas não as removeremos.

Voltamos ao webinar. A nova métrica "ano-mês da primeira compra" é calculada. Fórmula DAX: first_transaction = FORMAT(df_groupby_user[first_date_transaction];"YYYY-MM")

imagem

Python usa a sintaxe dt.strftime ('% Y-% m'). Você encontrará uma explicação detalhada de como funciona em publicações on-line sobre como trabalhar com data e hora no Python. Nesta etapa, outra coisa é importante. Preste atenção na hora da operação.

Desempenho nada parecido com pandas (24,8 seg.). A linha de código é mais lenta que todas as anteriores.
Este item da lista se torna o primeiro candidato a uma possível refatoração.

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

É hora de voltar ao webinar novamente. Há uma união de tabelas por um campo-chave. Em seguida, os campos necessários são puxados para a tabela principal usando a função RELATED (). O Pandas não possui esse recurso. Mas há mesclagem (), junção (), concat (). Nesse caso, é melhor aplicar a primeira opção.

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

Após os dados com a data da primeira transação caírem na tabela principal, você pode calcular o delta. Usamos a construção apply (lambda x: ...) para demonstrar claramente como esse processo consome muitos recursos (39,7 segundos). Aqui está outro candidato para a reescrita 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)

A tabela principal já possui um delta por dia, para que você possa dividir os dados da coluna em coortes. Princípio: 0 (ou seja, a primeira venda ao cliente) - coorte 0; valores maiores que 0, mas menores ou iguais a 30 são 30; valores maiores que 30, mas menores ou iguais a 90 são 90, etc. Para esses fins, no DAX, você pode usar a função CEILING (). Arredonda o número até o número inteiro mais próximo, um múltiplo do valor do segundo parâmetro.

imagem

No Python, não encontrei uma função matemática semelhante, embora planejasse encontrá-la no módulo de matemática (talvez tenha pesquisado mal). Portanto, eu tive que dar a volta e aplicar a função cut (). Depois de espalhar os dados em coortes, o NaN foi mapeado para valores numéricos de 0. Não podemos resolver esse problema usando a função fillna (), pois estamos lidando com dados categóricos. Primeiro, você precisa adicionar um novo valor à categoria. No final desta lista de códigos, altere o tipo de dados para int. Isso é feito para que, no futuro, ao construir uma tabela dinâmica usando um quadro de dados, a nova coorte não apareça no final de uma série 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 a função pivot_table (), obtemos a tabela dinâmica desejada. Temos muitas coortes, então o resultado não pode ser completamente exibido na tela. Para evitar isso, ao resolver um caso real, você pode demorar um pouco para analisar ou aumentar os intervalos de valores das próprias coortes.

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

imagem

No Power BI, você precisa usar a ferramenta Matrix para criar essa visualização.

imagem

A próxima etapa está planejando. A nuance da situação é que precisamos do montante pelo regime de competência. No Power BI, basta selecionar o item de menu 'Ação rápida' necessário e a fórmula DAX necessária será gerada automaticamente. Com a biblioteca Pandas, a situação é um pouco mais complicada. Agrupamos sequencialmente o quadro de dados existente e aplicamos a função cumsum (). Como o resultado ainda será usado, faremos uma cópia do quadro de dados para construir o gráfico. Os valores de vendas acumulados eram bastante grandes, portanto, dividimos os valores por 1.000.000 e arredondamos o resultado para dois dígitos após o ponto 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)

Usamos os recursos da biblioteca para construir o gráfico. Um diagrama é construído em apenas uma linha de código, mas o resultado não é impressionante. Esse gráfico perde claramente para visualizações em qualquer plataforma de BI. Você pode conectar a biblioteca Plotly e conjurar com complementos, mas esse é um custo de mão-de-obra completamente diferente em comparação com a abordagem mostrada no vídeo.

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

imagem

Vamos tirar breves conclusões.

Em termos de cálculos, a biblioteca do Pandas pode substituir o Power Pivot (DAX).

A viabilidade de tal substituição permanece fora da conversa.

O DAX, como as funções da biblioteca Python, faz um bom trabalho ao executar operações em campos inteiros de uma tabela.

Em termos de velocidade, simplicidade e facilidade de design de visualização, o Power BI é superior ao Pandas. Na minha opinião, os gráficos internos (bem como aqueles criados usando o matplotlib, bibliotecas marítimas) são apropriados para aplicação em dois casos: análise expressa de vários dados para valores extremos, mínimos / máximos locais ou preparação de slides para apresentação. É melhor deixar o desenvolvimento de painéis de controle gráficos para soluções de BI.

Isso é tudo. Toda a saúde, boa sorte e sucesso profissional!

All Articles