[{"data":1,"prerenderedAt":528},["ShallowReactive",2],{"lang-switch-post-\u002Fprojects\u002Folist-ecommerce\u002F02-visualizacoes-exploratorias":3,"chapter-pt-olist-ecommerce-02-visualizacoes-exploratorias":4},null,{"id":5,"title":6,"body":7,"cover":3,"date":514,"description":515,"extension":516,"meta":517,"navigation":62,"order":47,"path":518,"project":519,"seo":520,"status":521,"stem":522,"tags":523,"__hash__":527},"projectChapters\u002Fpt\u002Fprojects\u002Folist-ecommerce\u002F02-visualizacoes-exploratorias.md","O Brasil Compra de Dia Útil: Storytelling Exploratório da Olist",{"type":8,"value":9,"toc":505},"minimark",[10,22,27,106,109,112,116,191,194,202,206,254,257,271,275,317,320,327,331,369,372,375,378,425,428,434,438,476,479,494,498,501],[11,12,13,14,21],"p",{},"O capítulo 1 montou o modelo relacional e o dataframe mestre. Agora eu vou deixar o dado falar visualmente, seis ângulos diferentes do mesmo negócio, antes de treinar qualquer modelo. Notebook completo, executado, ",[15,16,20],"a",{"href":17,"rel":18},"https:\u002F\u002Fcolab.research.google.com\u002Fdrive\u002F1PrCuoWoAnb5paNi5dxzbSlxAGrcyA_mk?usp=sharing",[19],"nofollow","público no Colab",".",[23,24,26],"h2",{"id":25},"temporal-o-brasil-compra-de-dia-útil","Temporal: o Brasil compra de dia útil",[28,29,34],"pre",{"className":30,"code":31,"language":32,"meta":33,"style":33},"language-python shiki shiki-themes github-light github-dark","orders_com_hora = orders.dropna(subset=['order_purchase_timestamp']).copy()\norders_com_hora['weekday'] = orders_com_hora['order_purchase_timestamp'].dt.weekday\norders_com_hora['hour'] = orders_com_hora['order_purchase_timestamp'].dt.hour\n\nweekday_hour = (\n    orders_com_hora\n    .groupby(['weekday', 'hour'])['order_id']\n    .nunique()\n    .reset_index()\n    .rename(columns={'order_id': 'orders'})\n)\n","python","",[35,36,37,45,51,57,64,70,76,82,88,94,100],"code",{"__ignoreMap":33},[38,39,42],"span",{"class":40,"line":41},"line",1,[38,43,44],{},"orders_com_hora = orders.dropna(subset=['order_purchase_timestamp']).copy()\n",[38,46,48],{"class":40,"line":47},2,[38,49,50],{},"orders_com_hora['weekday'] = orders_com_hora['order_purchase_timestamp'].dt.weekday\n",[38,52,54],{"class":40,"line":53},3,[38,55,56],{},"orders_com_hora['hour'] = orders_com_hora['order_purchase_timestamp'].dt.hour\n",[38,58,60],{"class":40,"line":59},4,[38,61,63],{"emptyLinePlaceholder":62},true,"\n",[38,65,67],{"class":40,"line":66},5,[38,68,69],{},"weekday_hour = (\n",[38,71,73],{"class":40,"line":72},6,[38,74,75],{},"    orders_com_hora\n",[38,77,79],{"class":40,"line":78},7,[38,80,81],{},"    .groupby(['weekday', 'hour'])['order_id']\n",[38,83,85],{"class":40,"line":84},8,[38,86,87],{},"    .nunique()\n",[38,89,91],{"class":40,"line":90},9,[38,92,93],{},"    .reset_index()\n",[38,95,97],{"class":40,"line":96},10,[38,98,99],{},"    .rename(columns={'order_id': 'orders'})\n",[38,101,103],{"class":40,"line":102},11,[38,104,105],{},")\n",[107,108],"olist-weekday-hour-heatmap",{},[11,110,111],{},"168 células (7 dias × 24 horas), soma batendo exatamente com o total de pedidos, sempre bom conferir isso antes de confiar no gráfico. O pico é terça-feira às 14h, com 1.124 pedidos numa hora só. Olhando o total por dia da semana, segunda a sexta somam entre 14 e 16 mil pedidos cada, enquanto sábado cai pra 10.887 e domingo pra 11.960, uma queda de quase 30%. Por hora, o horário de almoço e começo de tarde (11h, 13h-16h) concentra o grosso do movimento, e a madrugada (3h-5h) praticamente para, menos de 300 pedidos em cada uma dessas horas no dataset inteiro. Isso é o padrão clássico de \"compra de trabalho remoto ou pausa do escritório\", não de compra de lazer noturno.",[23,113,115],{"id":114},"geográfico-o-norte-espera-mais","Geográfico: o Norte espera mais",[28,117,119],{"className":30,"code":118,"language":32,"meta":33,"style":33},"orders_com_cliente = orders.merge(customers, on='customer_id', how='left')\n\nfrete_por_pedido = order_items.groupby('order_id')['freight_value'].sum().reset_index()\norders_com_cliente = orders_com_cliente.merge(frete_por_pedido, on='order_id', how='left')\n\norders_com_cliente['delivery_days'] = (\n    orders_com_cliente['order_delivered_customer_date'] - orders_com_cliente['order_purchase_timestamp']\n).dt.days\n\npor_estado = orders_com_cliente.groupby('customer_state').agg(\n    orders=('order_id', 'nunique'),\n    avg_freight=('freight_value', 'mean'),\n    avg_delivery_days=('delivery_days', 'mean'),\n).reset_index()\n",[35,120,121,126,130,135,140,144,149,154,159,163,168,173,179,185],{"__ignoreMap":33},[38,122,123],{"class":40,"line":41},[38,124,125],{},"orders_com_cliente = orders.merge(customers, on='customer_id', how='left')\n",[38,127,128],{"class":40,"line":47},[38,129,63],{"emptyLinePlaceholder":62},[38,131,132],{"class":40,"line":53},[38,133,134],{},"frete_por_pedido = order_items.groupby('order_id')['freight_value'].sum().reset_index()\n",[38,136,137],{"class":40,"line":59},[38,138,139],{},"orders_com_cliente = orders_com_cliente.merge(frete_por_pedido, on='order_id', how='left')\n",[38,141,142],{"class":40,"line":66},[38,143,63],{"emptyLinePlaceholder":62},[38,145,146],{"class":40,"line":72},[38,147,148],{},"orders_com_cliente['delivery_days'] = (\n",[38,150,151],{"class":40,"line":78},[38,152,153],{},"    orders_com_cliente['order_delivered_customer_date'] - orders_com_cliente['order_purchase_timestamp']\n",[38,155,156],{"class":40,"line":84},[38,157,158],{},").dt.days\n",[38,160,161],{"class":40,"line":90},[38,162,63],{"emptyLinePlaceholder":62},[38,164,165],{"class":40,"line":96},[38,166,167],{},"por_estado = orders_com_cliente.groupby('customer_state').agg(\n",[38,169,170],{"class":40,"line":102},[38,171,172],{},"    orders=('order_id', 'nunique'),\n",[38,174,176],{"class":40,"line":175},12,[38,177,178],{},"    avg_freight=('freight_value', 'mean'),\n",[38,180,182],{"class":40,"line":181},13,[38,183,184],{},"    avg_delivery_days=('delivery_days', 'mean'),\n",[38,186,188],{"class":40,"line":187},14,[38,189,190],{},").reset_index()\n",[192,193],"olist-state-choropleth",{},[11,195,196,197,201],{},"O mapa mostra o tempo médio real de entrega (compra até entrega) por estado do cliente. ",[198,199,200],"strong",{},"Roraima lidera com quase 29 dias de espera média"," (28,98), seguido por Amapá (26,73), Amazonas (25,99) e Alagoas (24,04). Não é coincidência esses estados aparecerem no topo: Roraima, Amapá e Amazonas são os três estados mais distantes do eixo industrial e logístico do Sudeste, onde a maior parte dos vendedores da Olist está concentrada. São Paulo, pra comparação, fica bem mais perto da ponta rápida dessa distribuição. Essa relação distância-tempo de entrega é candidata natural a feature forte quando eu for montar o cenário de previsão de atraso.",[23,203,205],{"id":204},"categoria-beleza-e-saúde-lidera-o-faturamento","Categoria: beleza e saúde lidera o faturamento",[28,207,209],{"className":30,"code":208,"language":32,"meta":33,"style":33},"top_categorias = (\n    mestre\n    .dropna(subset=['product_category_name_english'])\n    .groupby('product_category_name_english')['price']\n    .sum()\n    .sort_values(ascending=False)\n    .head(15)\n    .reset_index()\n)\n",[35,210,211,216,221,226,231,236,241,246,250],{"__ignoreMap":33},[38,212,213],{"class":40,"line":41},[38,214,215],{},"top_categorias = (\n",[38,217,218],{"class":40,"line":47},[38,219,220],{},"    mestre\n",[38,222,223],{"class":40,"line":53},[38,224,225],{},"    .dropna(subset=['product_category_name_english'])\n",[38,227,228],{"class":40,"line":59},[38,229,230],{},"    .groupby('product_category_name_english')['price']\n",[38,232,233],{"class":40,"line":66},[38,234,235],{},"    .sum()\n",[38,237,238],{"class":40,"line":72},[38,239,240],{},"    .sort_values(ascending=False)\n",[38,242,243],{"class":40,"line":78},[38,244,245],{},"    .head(15)\n",[38,247,248],{"class":40,"line":84},[38,249,93],{},[38,251,252],{"class":40,"line":90},[38,253,105],{},[255,256],"olist-top-categories-chart",{},[11,258,259,262,263,266,267,270],{},[35,260,261],{},"health_beauty"," (beleza e saúde) lidera com 1.301.947,97 dólares em receita somada, seguida de perto por ",[35,264,265],{},"watches_gifts"," (relógios e presentes, 1.254.322,95) e ",[35,268,269],{},"bed_bath_table"," (cama, mesa e banho, 1.107.249,09). É receita, não é frete nem quantidade de item, então uma categoria pode liderar por vender caro (relógio) ou por vender muito (cama\u002Fmesa\u002Fbanho é item de reposição frequente).",[23,272,274],{"id":273},"pagamento-cartão-de-crédito-domina","Pagamento: cartão de crédito domina",[28,276,278],{"className":30,"code":277,"language":32,"meta":33,"style":33},"tipos_pagamento = (\n    payments[payments['payment_type'] != 'not_defined']\n    .groupby('payment_type')['order_id']\n    .nunique()\n    .reset_index()\n    .rename(columns={'order_id': 'count'})\n    .sort_values('count', ascending=False)\n)\n",[35,279,280,285,290,295,299,303,308,313],{"__ignoreMap":33},[38,281,282],{"class":40,"line":41},[38,283,284],{},"tipos_pagamento = (\n",[38,286,287],{"class":40,"line":47},[38,288,289],{},"    payments[payments['payment_type'] != 'not_defined']\n",[38,291,292],{"class":40,"line":53},[38,293,294],{},"    .groupby('payment_type')['order_id']\n",[38,296,297],{"class":40,"line":59},[38,298,87],{},[38,300,301],{"class":40,"line":66},[38,302,93],{},[38,304,305],{"class":40,"line":72},[38,306,307],{},"    .rename(columns={'order_id': 'count'})\n",[38,309,310],{"class":40,"line":78},[38,311,312],{},"    .sort_values('count', ascending=False)\n",[38,314,315],{"class":40,"line":84},[38,316,105],{},[318,319],"olist-payment-types-chart",{},[11,321,322,323,326],{},"Cartão de crédito domina disparado: 76.505 pedidos, mais de 3 vezes o segundo colocado (boleto, 19.784). Voucher vem com 3.866 e cartão de débito com só 1.528. Descartei a categoria ",[35,324,325],{},"not_defined"," do gráfico, tinha só 3 linhas em mais de 100 mil pagamentos, ruído puro, não merece fatia de pizza.",[23,328,330],{"id":329},"satisfação-a-maioria-ama-mas-quem-odeia-é-vocal","Satisfação: a maioria ama, mas quem odeia é vocal",[28,332,334],{"className":30,"code":333,"language":32,"meta":33,"style":33},"distribuicao_notas = (\n    reviews\n    .groupby('review_score')['review_id']\n    .count()\n    .reset_index()\n    .rename(columns={'review_id': 'count'})\n)\n",[35,335,336,341,346,351,356,360,365],{"__ignoreMap":33},[38,337,338],{"class":40,"line":41},[38,339,340],{},"distribuicao_notas = (\n",[38,342,343],{"class":40,"line":47},[38,344,345],{},"    reviews\n",[38,347,348],{"class":40,"line":53},[38,349,350],{},"    .groupby('review_score')['review_id']\n",[38,352,353],{"class":40,"line":59},[38,354,355],{},"    .count()\n",[38,357,358],{"class":40,"line":66},[38,359,93],{},[38,361,362],{"class":40,"line":72},[38,363,364],{},"    .rename(columns={'review_id': 'count'})\n",[38,366,367],{"class":40,"line":78},[38,368,105],{},[370,371],"olist-review-score-chart",{},[11,373,374],{},"57.328 avaliações de nota 5, mais da metade do total, e nota 4 vem em segundo com 19.142. Mas repara no terceiro lugar: nota 1, com 11.424 avaliações, sozinha soma mais que nota 2 (3.151) e nota 3 (8.179) juntas. Isso é o padrão clássico de review de e-commerce: cliente satisfeito às vezes nem avalia, cliente neutro quase nunca avalia, e cliente muito insatisfeito sempre avalia pra desabafar. Uma distribuição assim bimodal (pico grande em 5, segundo pico menor em 1) já é um aviso pra quando eu for montar o cenário de previsão de nota: não é uma escala contínua bem comportada, é quase uma decisão \"adorei ou detestei\" com um meio-termo raro.",[11,376,377],{},"Junto com isso, uma amostra real (400 pedidos) cruzando atraso da entrega com a nota dada:",[28,379,381],{"className":30,"code":380,"language":32,"meta":33,"style":33},"nota_atraso = (\n    orders[['order_id', 'order_delivered_customer_date', 'order_estimated_delivery_date']]\n    .merge(reviews[['order_id', 'review_score']], on='order_id', how='inner')\n    .dropna(subset=['order_delivered_customer_date', 'order_estimated_delivery_date', 'review_score'])\n)\n\nnota_atraso['delay_days'] = (\n    nota_atraso['order_delivered_customer_date'] - nota_atraso['order_estimated_delivery_date']\n).dt.days\n",[35,382,383,388,393,398,403,407,411,416,421],{"__ignoreMap":33},[38,384,385],{"class":40,"line":41},[38,386,387],{},"nota_atraso = (\n",[38,389,390],{"class":40,"line":47},[38,391,392],{},"    orders[['order_id', 'order_delivered_customer_date', 'order_estimated_delivery_date']]\n",[38,394,395],{"class":40,"line":53},[38,396,397],{},"    .merge(reviews[['order_id', 'review_score']], on='order_id', how='inner')\n",[38,399,400],{"class":40,"line":59},[38,401,402],{},"    .dropna(subset=['order_delivered_customer_date', 'order_estimated_delivery_date', 'review_score'])\n",[38,404,405],{"class":40,"line":66},[38,406,105],{},[38,408,409],{"class":40,"line":72},[38,410,63],{"emptyLinePlaceholder":62},[38,412,413],{"class":40,"line":78},[38,414,415],{},"nota_atraso['delay_days'] = (\n",[38,417,418],{"class":40,"line":84},[38,419,420],{},"    nota_atraso['order_delivered_customer_date'] - nota_atraso['order_estimated_delivery_date']\n",[38,422,423],{"class":40,"line":90},[38,424,158],{},[426,427],"olist-review-delay-scatter",{},[11,429,430,433],{},[35,431,432],{},"delay_days"," negativo é entrega adiantada (chegou antes do prometido), positivo é atraso de verdade. Repara que os pontos de nota 1 (a linha de baixo) aparecem espalhados por todo o eixo, inclusive nos negativos, mas com uma concentração visualmente maior no lado direito (atraso) do que os pontos de nota 5.",[23,435,437],{"id":436},"correlação-o-punchline-antes-do-modelo","Correlação: o punchline antes do modelo",[28,439,441],{"className":30,"code":440,"language":32,"meta":33,"style":33},"features_numericas = mestre[['price', 'freight_value', 'product_weight_g', 'payment_value', 'review_score']].copy()\nfeatures_numericas['delivery_days'] = (\n    mestre['order_delivered_customer_date'] - mestre['order_purchase_timestamp']\n).dt.days\nfeatures_numericas = features_numericas.dropna()\n\nmatriz_corr = features_numericas.corr()\n",[35,442,443,448,453,458,462,467,471],{"__ignoreMap":33},[38,444,445],{"class":40,"line":41},[38,446,447],{},"features_numericas = mestre[['price', 'freight_value', 'product_weight_g', 'payment_value', 'review_score']].copy()\n",[38,449,450],{"class":40,"line":47},[38,451,452],{},"features_numericas['delivery_days'] = (\n",[38,454,455],{"class":40,"line":53},[38,456,457],{},"    mestre['order_delivered_customer_date'] - mestre['order_purchase_timestamp']\n",[38,459,460],{"class":40,"line":59},[38,461,158],{},[38,463,464],{"class":40,"line":66},[38,465,466],{},"features_numericas = features_numericas.dropna()\n",[38,468,469],{"class":40,"line":72},[38,470,63],{"emptyLinePlaceholder":62},[38,472,473],{"class":40,"line":78},[38,474,475],{},"matriz_corr = features_numericas.corr()\n",[477,478],"olist-correlation-heatmap",{},[11,480,481,482,485,486,489,490,493],{},"Aqui está o número que eu prometi lá no fechamento do capítulo 1: ",[35,483,484],{},"review_score"," correlaciona ",[198,487,488],{},"-0,30"," com ",[35,491,492],{},"delivery_days",", calculado em cima de 114.838 linhas do dataframe mestre. É a correlação mais forte que qualquer feature numérica tem com a nota de avaliação (preço, frete e peso do produto ficam todos abaixo de 0,08 em módulo). Não é uma correlação gigante (longe de -1), mas é claramente a mais relevante do grupo, e bate exatamente com o que o mapa geográfico já insinuava: entrega demorada é o fator que mais aparenta puxar a satisfação do cliente pra baixo, mais do que quanto o produto custou ou pesou.",[23,495,497],{"id":496},"fechando-o-capítulo","Fechando o capítulo",[11,499,500],{},"Seis ângulos, um padrão emergindo: tempo de entrega aparece três vezes (mapa, scatter, correlação) como o fio condutor mais forte pra explicar insatisfação. Cartão de crédito domina pagamento, beleza\u002Fsaúde domina receita, terça-feira à tarde é o pico de compra. No próximo capítulo eu entro em engenharia e seleção de feature pros quatro cenários definidos no capítulo 1, e a discussão de vazamento de dado no cenário de atraso vai ficar bem mais concreta com esses números geográficos e de correlação já na mão.",[502,503,504],"style",{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":33,"searchDepth":47,"depth":47,"links":506},[507,508,509,510,511,512,513],{"id":25,"depth":47,"text":26},{"id":114,"depth":47,"text":115},{"id":204,"depth":47,"text":205},{"id":273,"depth":47,"text":274},{"id":329,"depth":47,"text":330},{"id":436,"depth":47,"text":437},{"id":496,"depth":47,"text":497},"2026-08-24","Segundo capítulo do case Olist: mapa de calor de horário de compra, mapa coroplético de atraso por estado, categorias que mais faturam, forma de pagamento, e a correlação que já entrega o punchline antes de qualquer modelo: atraso mata nota.","md",{},"\u002Fpt\u002Fprojects\u002Folist-ecommerce\u002F02-visualizacoes-exploratorias","olist-ecommerce",{"title":6,"description":515},"published","pt\u002Fprojects\u002Folist-ecommerce\u002F02-visualizacoes-exploratorias",[524,525,526],"pandas","eda","visualizacao","H_Prd-7xLiMf38oyKD33VdFKH7EWe_HXV2SeqCY2MRM",1787605215722]