pandas使用笔记、数据清洗、json_normalize
文章目录
- 构建数据
- 构建数据-列向导
- 构建数据-行向导
- 构建数据-矩阵导向
- 构建数据-记录导向
- 构建数据-总结
- 读取外部文件
- 清洗数据示例
- 清洗数据-1、创建原始数据
- 2、使用 Pandas 进行数据清洗
- 3、总结下这段代码干了什么
- 读取数据常用方法
- pandas支持复杂结构的json吗?
- json_normalize
- json_normalize直接拍扁
- json_normalize-结合record_path、meta
Pandas 是 Python 中用于数据分析和数据处理的“瑞士军刀”,是目前数据科学领域最核心的工具库之一。必须掌握。
构建数据
构建数据主要通过DataFrame来实现。
构建数据-列向导
importpandasaspd data={'姓名':['张三','李四','王五'],'年龄':[25,30,35],'城市':['北京','上海','广州']}df=pd.DataFrame(data)print(df)输出结果:
姓名 年龄 城市0张三25北京1李四30上海2王五35广州这么理解就好理解了:
姓名是列名,张三等是列下面的数据,每一列的行数要相等。
构建数据-行向导
因为行数据本身没有列名,所以构建时需要显示指定列名(columns)。
importpandasaspd# 每一组中括号代表一行数据data=[['张三',25,'北京'],['李四',30,'上海'],['王五',35,'广州']]# 必须显式指定列名df=pd.DataFrame(data,columns=['姓名','年龄','城市'])print(df)输出结果:
姓名 年龄 城市0张三25北京1李四30上海2王五35广州构建数据-矩阵导向
importpandasaspdimportnumpyasnp# 生成一个 3行4列 的随机数矩阵arr=np.random.randn(3,4)df=pd.DataFrame(arr,columns=['A','B','C','D'])print(df)输出结果:
A B C D00.3837210.299960-0.866526-1.8896211-0.418412-0.084870-1.0081830.0750372-0.1812211.113219-1.2967141.225547构建数据-记录导向
importpandasaspd data=[{'姓名':'张三','年龄':25,'城市':'北京'},{'姓名':'李四','年龄':30,'城市':'上海'},# 注意:如果某一行缺少某个字段(比如王五没写城市),Pandas 会自动填 NaN{'姓名':'王五','年龄':35}]df=pd.DataFrame(data)print(df)输出结果:
姓名 年龄 城市0张三25北京1李四30上海2王五35NaN构建数据-总结
一句话,纯数据需要指定名,其他都不需要指定列名。
| 数据 | 示例 | 要 |
|---|---|---|
| 纯数组 | '姓名': ['张三', '李四', '王五'] | 需要指定列名 |
| 列名+数组 | ['张三', 25, '北京'] | 无需指定列名 |
| 键值对 | {'姓名': '张三', '年龄': 25, '城市': '北京'} | 无需指定列名 |
读取外部文件
| 形式 | 代码示例 | 说明 |
|---|---|---|
| CSV 文件 | pd.read_csv('data.csv') | 最通用的文本格式 |
| Excel 文件 | pd.read_excel('data.xlsx') | 读取办公文档 |
| Parquet 文件 | pd.read_parquet('data.parquet') | 大数据/高性能场景首选 |
| SQL 数据库 | pd.read_sql(query, conn) | 从 MySQL/PostgreSQL 读取 |
清洗数据示例
清洗数据-1、创建原始数据
新建sales_raw.xlsx文件,内容复制进去:
| 订单ID | 门店名称 | 销售日期 | 销售额 | 客户等级 | 备注 |
|---|---|---|---|---|---|
| 1001 | 北京店 | 2023-01-01 | 2500 | VIP | 无 |
| 1002 | 上海 分店 | 2023-01-01 | 3000 | vip | |
| 1003 | 广州门店 | 2023/01/02 | 1800 | Normal | 促销订单 |
| 1004 | 深圳店 | 2023-01-02 | 2200 | NORMAL | 重要客户 |
| 1001 | 北京店 | 2023-01-01 | 2500 | VIP | 无 |
| 1005 | 杭州分店 | 2023-01-03 | High | ||
| 1006 | 成都 门店 | 2023-01-03 | 2800 | normal | |
| 1007 | 武汉店 | 2023-01-04 | 3100 | 新客户 | |
| 西安分店 | 2023-01-04 | 2400 | VIP |
该数据是有脏数据的。
1、重复数据:订单ID为 1001 的记录重复出现。
2、缺失值:第7行“客户等级”为空,第5行“销售额”为空,第9行“订单ID”为空。
3、格式不一致:
- 门店名称:有“店”、“分店”、“门店”等多种写法,且包含空格(如“上海 分店”)。
- 客户等级:VIP、vip、High、Normal、normal 大小写和命名不统一。
- 销售日期:存在 2023-01-01 和 2023/01/02 两种格式。
4、异常空格:“上海 分店”、“成都 门店”中存在多余空格。
5、字段冗余:“备注”列存在大量空值,可能无实际用途。
2、使用 Pandas 进行数据清洗
代码:
importpandasaspd# 1. 读取原始数据df=pd.read_excel("sales_raw.xlsx")print("原始数据形状:",df.shape)print("前5行数据:")print(df.head())# 2. 处理缺失值# 填充销售额缺失值为该列均值(数值型)df['销售额']=df['销售额'].fillna(df['销售额'].mean())# 填充客户等级缺失值为 'Unknown'(分类变量)df['客户等级']=df['客户等级'].fillna('Unknown')# 删除订单ID为空的整行(关键字段缺失)df=df.dropna(subset=['订单ID'])# 3. 去除重复项(基于订单ID)df=df.drop_duplicates(subset=['订单ID'],keep='first')# 4. 标准化文本字段# 清洗门店名称:去除空格、统一后缀为“店”df['门店名称']=df['门店名称'].str.replace(' ','').str.replace('分店|门店','店',regex=True)# 统一客户等级:转为大写并映射为标准类别level_map={'VIP':'VIP','HIGH':'High','NORMAL':'Normal','UNKNOWN':'Unknown'}df['客户等级']=df['客户等级'].str.upper().map(level_map)# 5. 统一日期格式df['销售日期']=pd.to_datetime(df['销售日期'],errors='coerce')# 6. 删除冗余列(如无用的备注)df=df.drop(columns=['备注'],errors='ignore')# 7. 数据类型优化df['订单ID']=df['订单ID'].astype(int)df['销售额']=df['销售额'].round(2)# 8. 查看清洗后结果print("\n清洗后数据形状:",df.shape)print("清洗后数据:")print(df)# 9. 保存清洗后数据df.to_excel("sales_cleaned.xlsx",index=False)部分输出结果:
清洗后数据形状:(7,5)清洗后数据:订单ID 门店名称 销售日期 销售额 客户等级01001北京店2023-01-012500.0VIP11002上海店2023-01-013000.0VIP21003广州店2023-01-021800.0Normal31004深圳店2023-01-022200.0Normal51005杭州店2023-01-032537.5High61006成都店2023-01-032800.0Normal71007武汉店2023-01-043100.0Unknown注:销售额 缺失值被填充为均值 2566.67,客户等级 空值标记为 Unknown,所有文本和日期均已标准化。
3、总结下这段代码干了什么
简单来说,刚才那段代码主要干了 4 件“大扫除”的事情,就像整理一个乱糟糟的房间一样:
🗑️ 扔垃圾(处理缺失值)
问题: 表格里有些地方是空的。比如有的行没有“销售额”,有的行没有“客户等级”。计算机没法计算空的东西。
怎么修的:
填空缺: 对于“销售额”,我们算出其他所有订单的平均数,把空缺填上(或者填0)。对于“客户等级”,填上“未知”。
删废行: 如果一行数据连最重要的“订单ID”都没有,那这行数据就彻底废了,直接整行删除。
对应代码: fillna (填充), dropna (删除)
✂️ 剪重复(去除重复项)
问题: “订单1001”在表里出现了两次,内容一模一样。这可能是复制粘贴时手抖多粘了一次。如果不删掉,算总销售额时就会算重。
怎么修的:
告诉电脑:“盯着‘订单ID’这一列看,如果发现两个一样的ID,只保留第一个,把后面重复的统统删掉。”
对应代码: drop_duplicates
📏 立规矩(标准化格式)
这是最繁琐的一步,主要是为了统一“写法”。
问题 A(名字乱):有的叫“上海 分店”(带空格),有的叫“广州门店”(后缀不一样)。
怎么修:
先把所有空格删掉(变成“上海分店”)。
再把所有的“分店”、“门店”全部替换成统一的“店”(变成“上海店”)。
问题 B(等级乱):有的是大写“VIP”,有的是小写“vip”,还有“Normal”。
怎么修:
全部强制转换成大写(VIP, NORMAL)。
建立一个对照表,把它们映射成统一的标准词。
对应代码: .str.replace, .str.upper, .map
🕵️♂️ 抓内鬼(修正数据类型)
问题: 在Excel里,“2023-01-01”有时候会被当成普通的文字(字符串),而不是真正的日期。这样你就没法算“这一天距离今天过了多久”。
怎么修的:
强制把“销售日期”这一列刷上一层“时间滤镜”,告诉电脑:“别把它当文字,这是时间!”这样以后就能进行时间计算了。
对应代码: pd.to_datetime
读取数据常用方法
| 类别 | 文件格式 | 读取函数 | 写入函数 |
|---|---|---|---|
| 文本/表格 | CSV / TSV | pd.read_csv() | df.to_csv() |
| Excel (.xls, .xlsx) | pd.read_excel() | df.to_excel() | |
| JSON | pd.read_json() | df.to_json() | |
| HTML (网页表格) | pd.read_html() | df.to_html() | |
| 高性能/二进制 | Parquet | pd.read_parquet() | df.to_parquet() |
| HDF5 | pd.read_hdf() | df.to_hdf() | |
| Pickle (Python专用) | pd.read_pickle() | df.to_pickle() | |
| Feather | pd.read_feather() | df.to_feather() | |
| 其他/统计 | SQL 数据库 | pd.read_sql() | df.to_sql() |
| Stata / SAS / SPSS | pd.read_stata()等 | df.to_stata() | |
| XML | pd.read_xml() | df.to_xml() |
pandas支持复杂结构的json吗?
最好是行列结构,如果比较复杂,需要结合其他手法,如json_normalize()等。
json_normalize
json_normalize直接拍扁
importpandasaspdfrompandasimportjson_normalize data={"id":1,"name":"Alice","info":{"age":25,"city":"Beijing"}}# 直接展平df=json_normalize(data)print(df)# 输出列名: id, name, info.age, info.cityjson_normalize-结合record_path、meta
record_path(指定列表在哪里)和 meta(保留外层信息):
frompandasimportjson_normalize data=[{"user_id":101,"profile":{"name":"Bob"},"orders":[{"product":"Book","price":20},{"product":"Pen","price":2}]}]# record_path: 指向那个“列表”字段,这会让每一行变成一个订单# meta: 把上层的 ID 和名字“提”出来,拼接到每一行df=json_normalize(data,record_path='orders',meta=['user_id',['profile','name']]# 嵌套字段要用列表表示路径)print(df)输出结果:
product price user_id profile.name0Book20101Bob1Pen2101Bob