销售数据脏到没法看?40行pandas清洗+plotly画图全实战,数据分析实战第1期

发布时间:2026/10/30 6:38:30
销售数据脏到没法看?40行pandas清洗+plotly画图全实战,数据分析实战第1期

前几天朋友丢给我一份销售数据,让帮忙看看哪个产品卖得好。我打开一看,血压上来了:日期三种格式混着写,金额前面挂着¥还带着逗号,产品名"iPhone"有六种写法,缺失值、重复行一个不少。这种数据你直接 groupby,算出来的全是错的。今天这篇就干一件事:把这份脏数据洗干净,再画出能看的图。代码 40 行,复制就能跑。

环境很简单,pandas 2.x 加 plotly。没装的跑一行 pip install pandas plotly。版本这块提一句:pandas 2.x 里 to_datetime 处理混合日期格式要加 format=“mixed”,1.x 不用。这个坑后面细说,先记住。

先看数据长什么样。真实场景你是从 CSV 读,我这里用代码造一份同样脏的数据,效果一样。df.info 和 isna().sum 先跑一遍,心里有数:215 行里 region 缺了 32 个,qty 缺了 11 个,还有 15 行是重复的。

import numpy as np import pandas as pd # 造一份"脏"数据,模拟业务系统导出的常态 rng = np.random.default_rng(42) n = 200 df = pd.DataFrame({ "product": rng.choice( [" iPhone", "IPHONE", "MacBook ", "macbook", "AirPods", "airpods "], n), "date": rng.choice( ["2026/01/05", "2026-01-06", "01-07-2026", "2026/1/8"], n), "amount": ["¥" + f"{x:,}" for x in rng.integers(3000, 15000, n)], "qty": rng.integers(1, 5, n).astype(float), "region": rng.choice(["华东", "华南", "华北", None], n, p=[0.3, 0.3, 0.25, 0.15]), }) # 混入缺失、重复、脏值 df.loc[rng.choice(n, 20, replace=False), "amount"] = "N/A" df.loc[rng.choice(n, 10, replace=False), "qty"] = np.nan df = pd.concat([df, df.iloc[:15]], ignore_index=True) # 15 行重复 print("原始形状:", df.shape) print("缺失统计:\n", df.isna().sum())

接下来一步步洗。先洗产品名:" iPhone"和"IPHONE"在机器眼里是两个东西,一行 str.strip().str.lower 解决,六种写法变三种。日期是重灾区,"2026/01/05"和"01-07-2026"混在一起,pd.to_datetime 直接转会报错,pandas 2.x 加 format=“mixed” 让它自己认格式。金额那一列是字符串,先把¥和逗号替换掉,再用 pd.to_numeric 转数字,errors=“coerce” 让转不过来的"N/A"变成 NaN 而不是报错,脏值多的列全靠它。

# 洗产品名,大小写空格统一 df["product"] = df["product"].str.strip().str.lower() # 三种日期格式统一 df["date"] = pd.to_datetime(df["date"], format="mixed") # 金额去符号转数字,N/A 变 NaN df["amount"] = (df["amount"].str.replace("¥", "", regex=False) .str.replace(",", "", regex=False)) df["amount"] = pd.to_numeric(df["amount"], errors="coerce") # 去重 + 缺失处理 df = df.drop_duplicates().copy() df["qty"] = df["qty"].fillna(df["qty"].median()) df["region"] = df["region"].fillna("未知") print("清洗后形状:", df.shape) print("缺失统计:\n", df.isna().sum())

洗完 200 行,缺失清零。groupby 一看:macbook 卖了 62 万,airpods 53 万,iphone 49 万。plotly 两行画出柱状图和趋势线,write_html 存下来,浏览器打开就能看。

import plotly.express as px sales = df.groupby("product", as_index=False)["amount"].sum().sort_values("amount") print(sales) trend = df.assign(day=df["date"].dt.date).groupby("day", as_index=False)["amount"].sum() fig1 = px.bar(sales, x="product", y="amount", title="sales by product") fig1.write_html("bar.html") fig2 = px.line(trend, x="day", y="amount", title="daily sales trend") fig2.write_html("line.html")

再说三个坑,都是我真踩过的。第一,混合日期格式,pandas 1.x 和 2.x 写法不一样,抄旧教程的代码会报错,认准 format=“mixed”。第二,链式赋值警告,drop_duplicates 之后加个 .copy() 最省事。第三,groupby 里直接用 df[“date”].dt.date 做分组键,pandas 2.x 会吞掉列名,先 assign 成一列再分组。

这套流程我每次拿到新数据都跑一遍,收藏这篇,下次照着做省半小时。下期数据分析实战第2期讲 groupby 之后怎么做透视表和环比,关注我不错过。你们平时拿到最脏的数据长什么样?评论区说说。

Cyrus