3. 数据清洗与预处理:Pandas基础、缺失值处理、重复值删除、数据类型转换
说实话,我见过太多人拿到数据就直接往地图里塞。结果呢?要么坐标对不上,要么属性全是空值,要么一个点重复了八百遍。嗯,数据清洗这一步,看着不起眼,但能决定你后面所有工作的生死。
我个人习惯,拿到任何数据源,第一件事不是看地图,而是先打开 Pandas 做一次全面体检。说白了,数据干净了,地图才有灵魂。
3.1 Pandas 基础:你得先有个 DataFrame
Pandas 是 Python 里处理表格数据的核心工具。你想想看,GIS 里的属性表、CSV 文件、Excel 表格,本质上都是二维表格。Pandas 的 DataFrame 就是专门干这个的。
先看怎么把数据读进来:
import pandas as pd
# 读取 CSV 文件
df = pd.read_csv('poi_data.csv')
# 看看数据长什么样
print(df.head()) # 前5行
print(df.info()) # 列名、类型、非空数量
print(df.describe()) # 数值列的统计摘要
我在项目中遇到过最坑的事:有人用 Excel 打开 CSV 后另存,结果经纬度被自动截断了。所以我现在一律用 Pandas 直接读原始文件,不经过 Excel 中转。
df.shape 快速看行列数,用 df.columns.tolist() 列出所有列名。这两个命令我几乎每天都会敲。
3.2 缺失值处理:别让空值毁了你的地图
缺失值,说白了就是数据里的窟窿。在地图里,一个缺失的坐标字段,直接导致这个点无法落图。一个缺失的名称字段,弹窗里显示 NaN,用户看了直接懵。
先看看数据里到底有多少缺失值:
# 检查每列的缺失值数量
print(df.isnull().sum())
# 缺失值占比
print(df.isnull().sum() / len(df) * 100)
处理缺失值,我一般分三步走:
- 先判断能不能补 —— 比如经纬度缺失,基本没法补,直接删行。
- 能补的怎么补 —— 比如人口数据缺失,可以用均值或中位数填充。
- 不能补的果断删 —— 别心疼,留着反而污染结果。
# 删除包含缺失值的行(默认只要有一列缺失就删)
df_clean = df.dropna()
# 只删除特定列缺失的行(比如经纬度)
df_clean = df.dropna(subset=['longitude', 'latitude'])
# 用均值填充数值列
df['population'].fillna(df['population'].mean(), inplace=True)
# 用字符串填充文本列
df['name'].fillna('未知地点', inplace=True)
df.dropna() 删掉了 40% 的数据,后来才发现是因为有一列几乎全是空值。所以一定要先看 isnull().sum(),别盲目删除。
3.3 重复值删除:一个点出现两次,地图就乱了
重复数据在地图里特别要命。你想想看,同一个 POI 在地图上叠了两个点,用户点击时弹窗可能弹出两个,或者聚合图层的计数直接翻倍。
我遇到过最离谱的一次:一个客户的数据里,同一个加油站重复了 17 次,就因为不同的人录入了同一份数据。
# 检查重复行
print(df.duplicated().sum())
# 删除完全重复的行
df_unique = df.drop_duplicates()
# 根据特定列判断重复(比如根据 ID 或名称+地址)
df_unique = df.drop_duplicates(subset=['name', 'address'])
# 保留最后出现的重复项
df_unique = df.drop_duplicates(keep='last')
3.4 数据类型转换:让 Pandas 认识你的数据
这个坑我踩过太多次了。你从 CSV 读进来的数据,Pandas 默认全当字符串处理。但地图需要的是数值型的经纬度、日期型的时间戳。
先看看当前的数据类型:
print(df.dtypes)
常见的转换场景:
| 原始类型 | 目标类型 | 代码示例 | 常见问题 |
|---|---|---|---|
| 字符串 | 浮点数 | df['lon'] = df['lon'].astype(float) |
字符串里有空格或逗号 |
| 字符串 | 日期 | df['date'] = pd.to_datetime(df['date']) |
日期格式不统一 |
| 浮点数 | 整数 | df['count'] = df['count'].astype(int) |
有 NaN 时无法直接转 |
| 字符串 | 分类 | df['type'] = df['type'].astype('category') |
类别太多时内存反而更大 |
# 实战:清洗一个典型的 POI 数据集
df = pd.read_csv('poi_raw.csv')
# 1. 删除经纬度缺失的行
df = df.dropna(subset=['lng', 'lat'])
# 2. 转换经纬度为浮点数
df['lng'] = df['lng'].astype(float)
df['lat'] = df['lat'].astype(float)
# 3. 删除名称和地址完全重复的行
df = df.drop_duplicates(subset=['name', 'address'])
# 4. 转换时间字段
df['create_time'] = pd.to_datetime(df['create_time'])
# 5. 填充缺失的类别字段
df['category'].fillna('其他', inplace=True)
print(f'清洗完成,剩余 {len(df)} 条有效数据')
astype(float) 转经纬度时,发现有一行数据里写的是 "116.40, 39.90"(带了逗号)。结果整列转换失败。后来我养成了先做 df['col'].unique() 看看异常值的习惯。
3.5 本章知识体系总览
下面这张图,把数据清洗的整个流程串起来了。从原始数据到干净可用的 DataFrame,每一步都有对应的 Pandas 操作。
嗯,到这里,数据清洗的核心操作就全了。你想想看,从原始数据到干净可用的 DataFrame,其实就这几步。但每一步都有细节,细节决定地图的质量。
我个人习惯,每次清洗完数据,都会用 df.to_csv('cleaned_data.csv', index=False) 保存一份干净版本。这样后面做地图时,直接读这个文件,省心又省力。