📊 第10章 京东电商销售数据分析与预测
📄 一句话概括

日期从 Excel 读进来是字符串,不是时间;不转类型,取不出月份和星期。 缺失 0、重复 0、越界 0——这份数据是干净的,干净也要当成一条结果报出来。 加了月份、季度、星期、是否周末四列,派生字段是后面所有统计的地基。

📝 本章和前面几章的关系

上一篇把数据落成了 xlsx,这一篇读回来,走一遍清洗流程。 和第 9 章不一样:那边脏得明显(重复、空渠道、2099 年),这边干净。两种都要会对付。

一、读进来先看形状

read_excel 一句读完:文件路径,加一个 sheet_name。 形状是 (365, 4),四个中文列名读进来直接用,不用改名。 sheet_name 建议写全。不写默认读第一个工作表,一个文件里放几张表时就容易读错片。

第 3 章的老规矩在这里照样适用:拿到数据先看形状、看头五行、看类型,三样都不看就开算,等于蒙。

二、类型不对,后面全别扭

列 读进来是什么 说明
日期 str 文本,不是时间
销售额 float64 浮点
广告费 float64 浮点
订单量 int64 整数

日期是字符串,这是 Excel 数据最常见的坑。Excel 里那一列存的是文本,读进来就是 object。 文本能排序、能切片,但取不出「几月」「星期几」——这两个只有 datetime 才有。 pd.to_datetime 一句转完,之后 df["日期"].dt.year 的类型是 int32。

如果格式五花八门(有的是 2025/1/1,有的是 2025-01-01),就得加 format 参数,否则会转出一批 NaT。 NaT 很阴:它不报错,只让那一行的分组结果消失。所以转完一定要数一下空值。

三、查三样:缺、重、怪

毛病 怎么查 这次的结果
缺失 isnull().sum().sum() 全表 0 个
重复 duplicated().sum() 0 条
异常 看最小值和最大值 销售额 17.29 到 80.88

三项全 0,这份数据是干净的。真实项目里三项全 0 的概率很低。 干净也要报告。「我查过了,都是 0」和「应该没问题」,在别人听来完全不是一回事。 看异常不只看范围,还要看三样:有没有负数、有没有 0、有没有比均值大十倍的值。这次一个都没有。

广告费的范围是 8.15 到 24.98 万,和销售额 17.29 到 80.88 万放在一起看,倍数关系合理, 说明两列单位一致。

四、加四列派生字段

月份、季度、星期、是否周末,四列全从「日期」一列拆出来。 dt.day_of_week 给的是 0 到 6:0 是周一,5 是周六,6 是周日。所以 >= 5 就是周末。 星期那一列存中文(周一…周日)比存 0 到 6 好读,用 map 换一次就行。

派生字段一次拆好,全表都能用。每次分析前临时算一遍,既慢,又容易算错口径。 还有一层好处:口径写死在数据里,别人接手时看得见。「周末」到底含不含周五,一眼就能查。

五、洗完另存一份

洗完存成 销售数据_clean.xlsx,365 行 8 列,原四列加派生四列。 原始文件一个字不动。出问题还能回头对,覆盖了就没了。 这是第 9 章那条规矩,换了数据源照样成立。数据源越方便(Excel 双击就能改),越容易犯这个错。

下一篇开始统计。所有统计都从这份干净的、带派生列的文件读,不再碰原始表。

六、动手 Demo:数据处理

读回来、看类型、查三样、加四列、另存一份,五步一次走完。 重点是第②步那个「日期是字符串」的坑,和第④步那四列的拆法。

import pandas as pd

df = pd.read_excel("京东销售数据.xlsx", sheet_name="销售明细")
print("形状 %s" % (df.shape,))
print(df.dtypes.to_string())

print("空值 %d,重复 %d"
      % (df.isnull().sum().sum(), df.duplicated().sum()))

df["日期"] = pd.to_datetime(df["日期"])
df["月份"] = df["日期"].dt.month
df["季度"] = df["日期"].dt.quarter
df["星期"] = df["日期"].dt.day_of_week.map(
    dict(enumerate(["周一", "周二", "周三", "周四",
                     "周五", "周六", "周日"])))
df["是否周末"] = df["日期"].dt.day_of_week >= 5
print("洗完 %s" % (df.shape,))
df.to_excel("销售数据_clean.xlsx", index=False)

实跑输出:

--------------------------------------------------------------------
数据处理
--------------------------------------------------------------------

  ① 读进来
  --------------------------------------------------------------------
  读进来形状 (365, 4),列名是 ['日期', '销售额', '广告费', '订单量']。
  read_excel 默认读第一个工作表,指定 sheet_name 更稳。
  表里有中文列名,读进来直接用,不用改。

  ② 先看类型
  --------------------------------------------------------------------

| 列 | 类型 | 说明 |
|---|---|---|
| 日期 | str | 文本,不是时间 |
| 销售额 | float64 | 浮点 |
| 广告费 | float64 | 浮点 |
| 订单量 | int64 | 整数 |

  日期读进来是字符串,不是 datetime。这是 Excel 数据的常见坑。
  文本能排序,但不能取「几月」「星期几」,所以第一件事就是转类型。
  转完之后类型是 int32。

  ③ 查:缺、重、怪
  --------------------------------------------------------------------
  缺失:全表 0 个空值。
  重复:0 条完全重复。
  异常:销售额最小 17.29、最大 80.88,广告费最小 8.15、最大 24.98。
  范围都在合理区间内,没有负值,也没有离谱的大数。
  真实数据这一步最花时间:负数、0、离谱值、日期串年份,一样样查。

  ④ 加几列派生字段
  --------------------------------------------------------------------
  加了四列:月份、季度、星期、是否周末。
  派生字段是分析的地基。想按月看就得有月份列,想按周看就得有星期列。
  这四列都是「从日期一列拆出来的」,拆一次全表都能用。

  ⑤ 存一份干净的
  --------------------------------------------------------------------
  洗完存成 销售数据_clean.xlsx,365 行 8 列。
  ! 原始文件不动,另存一份。出问题能回头对,覆盖了就没了。

dtypes 打出来是四行,日期那行写 object,就说明它是文本。先认这一行,再动手转。 dt.day_of_week 从 0 起算,不是 1 到 7。写成 >= 6 就把周日漏掉了。 存 clean 那份时 index=False 同样不能漏。这一份是给后面几节读的,多一列行号会一路传下去。

30 秒实验

  1. 把 pd.to_datetime 那一句注释掉,再跑 dt.month,看报什么错。
  2. 把 >= 5 改成 >= 6,看周末的日均销售额变成多少。
  3. 想一想:如果订单量那一列里混了「1,024」这种带逗号的文本,会读成什么类型?

常见误区

  1. 不转 datetime 就取月份:报错说 str 没有 dt 属性,很多人在这里卡住。
  2. 转完不数 NaT:格式不一致会静默丢掉几行,统计结果少一截还没人知道。
  3. 周末判断写成 1 到 7:day_of_week 是 0 到 6,差一位就把周一算成周末。
  4. 直接覆盖原始文件:Excel 双击就能改,最容易手一滑就把源数据改了。
自测三问(点开对答案) 1. 为什么日期读进来是字符串? —— Excel 那一列存的是文本,pandas 照原样读成 object,不会自动猜成时间。 2. 缺、重、怪三项全 0,还要不要报? —— 要。查过和没查是两件事,报告里写「三项均为 0」本身就是一条结论。 3. 为什么四列派生字段要一次拆好? —— 拆一次全表都能用,口径统一;每次临时算既慢,又容易各算各的。

数据干净了,开始统计。下一篇按日期的四个粒度各汇总一遍。

相关笔记