第三章 数据读取与存储:从文件到数据库的实战之路

大家好,我是你们的老朋友。今天我们来聊聊数据读取与存储这个话题。说实话,这可能是整个课程里最「接地气」的一章了。为什么?因为不管你算法写得再漂亮,数据读不进来、存不下去,一切都是白搭。

我在做仪器开发的头几年,就吃过不少这方面的亏。有一次现场调试,仪器采集了一整天的数据,结果因为存储格式选错了,最后数据全废了。从那以后,我对数据读写这块就格外上心。

好,咱们直接进入正题。

3.1 CSV与Excel文件读写:最常用的数据交换格式

先说CSV。这玩意儿简单,说白了就是用逗号分隔的文本文件。几乎所有仪器软件都支持导出CSV格式。我个人的习惯是:能用CSV就用CSV,因为它跨平台、跨语言,而且人类可读。

Python里读写CSV,我推荐用pandas。为什么?因为快,而且代码少。

import pandas as pd

# 读取CSV
df = pd.read_csv('instrument_data.csv')
print(df.head())

# 写入CSV
df.to_csv('processed_data.csv', index=False)

嗯,就这么简单。但这里有个坑——编码问题。我曾经在项目中遇到过一个CSV文件,怎么读都是乱码。后来发现是编码格式不对。中文数据经常用GBK,而pandas默认是UTF-8。

避坑指南: 我曾经因为编码问题浪费了整整一个下午。建议读取时显式指定编码:pd.read_csv('file.csv', encoding='gbk')。如果不确定编码,可以用chardet库先检测一下。

再说Excel。Excel文件比CSV复杂,因为它支持多工作表、公式、格式等。但好在pandas也支持。

# 读取Excel
df = pd.read_excel('data.xlsx', sheet_name='Sheet1')

# 写入Excel
with pd.ExcelWriter('output.xlsx') as writer:
    df.to_excel(writer, sheet_name='原始数据', index=False)

你想想看,如果数据量不大(几千行以内),Excel完全够用。但一旦数据量上来了,Excel就扛不住了。这时候就需要二进制格式出场了。

3.2 二进制数据解析:与仪器直接对话

很多工业仪器输出的不是文本,而是二进制数据。为什么?因为二进制体积小、传输快。我见过一些老式的频谱仪,输出的就是二进制文件,一个文件几兆字节,但包含了几十万个数据点。

解析二进制数据,核心就是搞清楚数据格式。说白了,你得知道每个字节代表什么。

举个例子,假设仪器输出的数据格式是:前4个字节是时间戳(int32),后面每4个字节是一个浮点数(float32)。

import struct
import numpy as np

def parse_binary_file(filename):
    with open(filename, 'rb') as f:
        raw_data = f.read()
    
    # 解析时间戳
    timestamp = struct.unpack('<i', raw_data[:4])[0]
    
    # 解析数据点
    data_bytes = raw_data[4:]
    num_points = len(data_bytes) // 4
    data = np.frombuffer(data_bytes, dtype=np.float32)
    
    return timestamp, data
小技巧:struct.unpack解析固定长度的二进制数据,用numpy.frombuffer解析大量同类型数据。前者灵活,后者高效。

这里要注意字节序的问题。不同的仪器可能用大端序或小端序。我遇到过一台德国产的仪器,它用的是大端序,而我的电脑默认是小端序。结果解析出来的数据全是错的。解决办法很简单:在格式字符串里加个><

# 大端序
timestamp = struct.unpack('>i', raw_data[:4])[0]

# 小端序
timestamp = struct.unpack('<i', raw_data[:4])[0]

3.3 HDF5大文件存储:海量数据的救星

当数据量达到GB级别时,CSV和Excel就彻底没戏了。这时候HDF5就派上用场了。

HDF5是一种分层数据格式,专门为大规模科学数据设计。它的特点是什么?快、压缩率高、支持随机访问。说白了,你不需要把整个文件读进内存,就能访问其中的任意一部分。

我在做振动信号分析时,经常要处理连续采集几小时的数据,文件大小动辄几十GB。用HDF5存储,读取速度比CSV快几十倍。

import h5py
import numpy as np

# 写入HDF5
with h5py.File('vibration_data.h5', 'w') as f:
    f.create_dataset('time_series', data=np.random.randn(1000000))
    f.create_dataset('metadata', data='采样率: 1000Hz')

# 读取HDF5
with h5py.File('vibration_data.h5', 'r') as f:
    data = f['time_series'][:]
    print(data.shape)
重点: HDF5支持数据压缩。在创建数据集时指定压缩参数,可以大幅减小文件体积。我一般用compression='gzip',压缩比大概在3-5倍。
# 带压缩的写入
f.create_dataset('big_data', data=large_array, compression='gzip', compression_opts=9)

嗯,这里要注意一点:HDF5虽然强大,但它的学习曲线稍微陡峭一些。不过一旦用上了,你就回不去了。

3.4 数据库存储(SQLite):结构化数据的最佳归宿

最后说说数据库。如果你的数据是结构化的(比如有明确的字段、需要频繁查询),那数据库就是最佳选择。

SQLite是嵌入式数据库,不需要安装服务器,一个文件就是一个数据库。非常适合仪器数据这种单机应用场景。

我为什么推荐SQLite?因为它零配置、跨平台、稳定可靠。我曾经在一个嵌入式仪器上跑SQLite,连续运行了三个月没出过问题。

import sqlite3

# 连接数据库(如果不存在则自动创建)
conn = sqlite3.connect('instrument.db')
cursor = conn.cursor()

# 创建表
cursor.execute('''
    CREATE TABLE IF NOT EXISTS measurements (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        timestamp TEXT NOT NULL,
        channel INTEGER,
        value REAL
    )
''')

# 插入数据
cursor.execute('''
    INSERT INTO measurements (timestamp, channel, value)
    VALUES (?, ?, ?)
''', ('2024-01-15 10:30:00', 1, 3.14159))

conn.commit()

# 查询数据
cursor.execute('SELECT * FROM measurements WHERE channel = ?', (1,))
rows = cursor.fetchall()
for row in rows:
    print(row)

conn.close()
避坑指南: 我曾经在批量插入数据时,一条一条地执行INSERT语句,结果插入10万条数据花了十几分钟。后来改用executemany,几秒钟就搞定了。
# 批量插入,效率提升百倍
data_batch = [
    ('2024-01-15 10:30:01', 1, 2.718),
    ('2024-01-15 10:30:02', 1, 3.142),
    # ... 更多数据
]
cursor.executemany('INSERT INTO measurements VALUES (NULL, ?, ?, ?)', data_batch)
conn.commit()

另外,SQLite支持索引。如果你的查询经常按某个字段过滤,记得加索引。

cursor.execute('CREATE INDEX idx_channel ON measurements (channel)')

小结

好了,这一章的内容就到这里。我们来总结一下:

  • CSV/Excel:适合小数据量、跨平台交换。注意编码问题。
  • 二进制解析:适合与仪器直接通信。搞清楚字节序和数据结构是关键。
  • HDF5:海量数据的首选。支持压缩和随机访问。
  • SQLite:结构化数据的归宿。批量操作和索引是性能的关键。

我个人建议:在实际项目中,根据数据的特点选择合适的存储方式。没有银弹,只有最适合的方案。下一章我们会讲数据清洗与预处理,到时候这些读写技巧都会用上。

好,今天就到这里。有问题随时交流。