3、CSV文件读写:用csv模块和pandas搞定数据导入导出

CSV文件,说白了就是测绘数据交换的"通用语言"。不管你是用全站仪、RTK还是无人机,最后导出来的数据十有八九都是CSV格式。我做了这么多年测绘,处理过的CSV文件少说也有几万个了。今天就把我踩过的坑和总结的经验都告诉你。

核心要点:CSV读写看似简单,但测绘数据里经常夹杂着空值、异常坐标、乱码等问题。掌握好csv模块和pandas两种方式,能让你少加三天班。

3.1 为什么测绘数据离不开CSV?

你想想看,一个测区动辄几千个点,每个点包含点号、X、Y、Z、属性等信息。用Excel打开?文件大了直接卡死。用数据库?杀鸡用牛刀。CSV格式轻量、通用、几乎所有软件都支持,自然成了首选。

但CSV也有坑。我记得有一次项目验收,甲方说数据打不开。我一看,原来是编码格式是GBK,对方电脑默认UTF-8,全乱码了。从那以后,我每次导出CSV都会确认编码格式。

对比项 csv模块 pandas
适用场景 小文件、简单读写 大数据量、复杂处理
内存占用 低(逐行读取) 高(全量加载)
缺失值处理 需手动判断 内置dropna/fillna
异常值检测 需写逻辑 配合条件筛选
批量合并 逐行拼接 concat一行搞定

3.2 用csv模块读写:轻量级方案

Python自带的csv模块,不需要装任何第三方库。我一般用它来处理小批量数据,或者做数据预览。

3.2.1 读取CSV文件

import csv

# 读取测绘点数据
with open('survey_points.csv', 'r', encoding='utf-8') as f:
    reader = csv.reader(f)
    header = next(reader)  # 跳过表头
    print(f"字段列表:{header}")
    
    for row in reader:
        point_id, x, y, z, desc = row
        print(f"点号:{point_id},坐标:({x}, {y}, {z})")

我的习惯:读取CSV时,先用next(reader)把表头单独拿出来。这样既能确认字段顺序,又方便后续按列索引取值。

3.2.2 写入CSV文件

import csv

# 写入处理后的数据
data = [
    ['P001', 4523678.12, 524678.34, 125.67, '控制点'],
    ['P002', 4523789.45, 524690.12, 126.03, '界址点'],
    ['P003', 4523801.78, 524702.56, 125.89, '碎部点']
]

with open('output_points.csv', 'w', encoding='utf-8-sig', newline='') as f:
    writer = csv.writer(f)
    writer.writerow(['点号', 'X', 'Y', 'Z', '类型'])
    writer.writerows(data)

注意:编码用utf-8-sig而不是utf-8。为什么?因为Excel打开UTF-8文件时,如果不带BOM头,中文会乱码。我曾经因为这个被甲方骂了一顿,后来就长记性了。

3.3 用pandas读写:工业级方案

当数据量超过几万行,或者需要做复杂处理时,我肯定用pandas。它处理缺失值和异常值的能力,比手动写循环强太多了。

3.3.1 读取CSV并处理缺失值

import pandas as pd

# 读取数据,自动识别缺失值
df = pd.read_csv('survey_data.csv', encoding='utf-8')

# 查看缺失情况
print("每列缺失值数量:")
print(df.isnull().sum())

# 处理缺失值:高程缺失的用插值填充
df['Z'].fillna(method='ffill', inplace=True)

# 或者直接删除缺失严重的行
df.dropna(subset=['X', 'Y'], inplace=True)

为什么会缺失?我遇到过几种情况:仪器没锁定卫星、测量员漏记、数据传输中断。处理方式要看具体情况,不能一刀切。

3.3.2 检测并处理异常值

import pandas as pd
import numpy as np

df = pd.read_csv('survey_points.csv')

# 检测坐标异常:超出测区范围
valid_x = (df['X'] > 4500000) & (df['X'] < 4600000)
valid_y = (df['Y'] > 500000) & (df['Y'] < 600000)

# 标记异常点
df['异常标记'] = '正常'
df.loc[~valid_x | ~valid_y, '异常标记'] = '坐标超限'

# 或者用3σ原则检测高程异常
z_mean = df['Z'].mean()
z_std = df['Z'].std()
df['Z异常'] = np.abs(df['Z'] - z_mean) > 3 * z_std

print(f"发现 {df['Z异常'].sum()} 个高程异常点")

实战经验:有一次处理无人机航测数据,发现有个点的高程是-9999。一看就是仪器默认的空值标记。这种"伪数值"最坑人,不处理的话,算出来的DEM全是坑。我建议在读取数据后,先检查有没有这种约定俗成的"特殊值"。

3.4 批量合并多个CSV文件

这是测绘内业最常遇到的需求。一个测区可能分了好几天测量,每天生成一个CSV。手动合并?几百个文件能把你累死。

3.4.1 用pandas批量合并

import pandas as pd
import glob
import os

# 获取所有CSV文件
csv_files = glob.glob('data/*.csv')
print(f"找到 {len(csv_files)} 个CSV文件")

# 批量读取并合并
df_list = []
for file in csv_files:
    df = pd.read_csv(file, encoding='utf-8')
    # 添加来源文件名,方便追溯
    df['来源文件'] = os.path.basename(file)
    df_list.append(df)

# 纵向合并
merged_df = pd.concat(df_list, ignore_index=True)

# 去重(按点号)
merged_df.drop_duplicates(subset=['点号'], keep='first', inplace=True)

print(f"合并后共 {len(merged_df)} 个点")
merged_df.to_csv('merged_points.csv', index=False, encoding='utf-8-sig')

3.4.2 合并时的常见问题

问题 表现 解决方案
字段名不一致 有的叫"X",有的叫"东坐标" 统一字段映射表
重复点号 不同文件有相同点号 按时间戳或优先级去重
编码不一致 部分文件乱码 用chardet检测编码
分隔符不同 有的用逗号,有的用分号 指定sep参数

避坑指南:我曾经合并100多个测站的数据,结果发现坐标系统不一致——有的文件是CGCS2000,有的是WGS84。合并前一定要先检查坐标系统,不然算出来的面积全是错的。

3.5 本章知识体系

下面这张图把CSV读写的核心流程串起来了,建议你保存下来,做项目时对照着用。

CSV文件读写核心流程 原始CSV文件 csv模块(轻量) pandas(工业级) 逐行读取 / 手动处理 缺失值处理 / 异常检测 批量合并多个CSV 清洗后的CSV文件 核心原则:先清洗(缺失值+异常值)→ 再合并 → 最后输出,顺序不能乱

3.6 实战建议

  • 小文件(<1万行):用csv模块,速度快,不占内存
  • 大文件(>10万行):用pandas,处理能力强,代码简洁
  • 批量合并:先统一字段名和编码,再concat,最后去重
  • 异常值处理:不要直接删除,先标记再人工复核

最后提醒一句:CSV文件看似简单,但测绘数据是成果,出了问题要担责任的。我见过有人合并时把坐标列搞反了,结果整个测区的图全部偏移。每次处理完数据,一定要抽检几个点,跟原始记录核对一下。