第4章 数据源接入(上):从CSV/Excel文件读取设备数据

大家好,我是你们的老朋友。今天咱们聊聊数据源接入这件事。

做可视化看板,第一步就是搞定数据。没有数据,再炫酷的图表都是空中楼阁。我个人习惯把数据接入分成两步:先读进来,再洗干净。今天咱们就先把「读进来」这件事讲透。

核心要点:用Pandas读取CSV和Excel文件,是设备数据接入最基础、最常用的方式。掌握它,你就拿到了数据处理的入场券。

4.1 为什么选择Pandas?

你可能会问:Python里读取文件的方式那么多,为什么非要用Pandas?

嗯,这个问题我当年也想过。直到我在项目中遇到一个场景:一个设备日志文件有50万行,用普通文件读取方式,光解析就要花掉好几分钟。换成Pandas的read_csv(),几秒钟搞定。说白了,Pandas就是为表格数据而生的。

我在项目中遇到过不少同事,明明数据量不大,却硬要用open()加循环去解析CSV。结果代码写了一长串,还容易出错。其实用Pandas,一行代码就能搞定。

对比项 普通文件读取 Pandas读取
代码量 10-20行 1-2行
处理速度 慢(逐行解析) 快(底层C优化)
数据类型推断 需要手动处理 自动推断
缺失值处理 需要手动判断 自动识别NaN

4.2 读取CSV文件:最常用的方式

CSV文件是设备数据最常见的存储格式。传感器数据、日志记录、设备状态,很多都存成CSV。

先看一个最简单的例子:

import pandas as pd

# 读取CSV文件
df = pd.read_csv('device_data.csv')
print(df.head())  # 查看前5行

就这么简单?对,就这么简单。但实际项目中,CSV文件往往没那么规整。我曾经遇到过一个设备数据文件,编码格式是GBK,字段分隔符是分号,还带表头。如果直接用默认参数,读出来全是乱码。

这时候就需要调整参数了:

# 处理常见问题的读取方式
df = pd.read_csv(
    'device_data.csv',
    encoding='gbk',        # 指定编码
    sep=';',               # 指定分隔符
    header=0,              # 第一行作为列名
    skiprows=[1, 3],       # 跳过第2行和第4行(索引从0开始)
    na_values=['NA', 'N/A', 'NULL']  # 指定哪些值视为缺失
)

小技巧:如果你不确定文件编码,可以用chardet库自动检测。我习惯先检测再读取,避免反复试错。

4.3 读取Excel文件:更复杂的场景

Excel文件比CSV复杂一些。一个Excel文件可能包含多个工作表,每个表里还有合并单元格、不同数据类型等。

读取Excel的基本方式:

# 读取Excel文件
df = pd.read_excel('device_data.xlsx', sheet_name='设备数据')

但实际项目中,我遇到过更头疼的情况。有一次,一个设备供应商发来的Excel文件,里面居然有3个工作表,每个表的结构都不一样。第一个表是设备基本信息,第二个表是运行数据,第三个表是维修记录。

这时候就需要分别读取:

# 读取多个工作表
df_info = pd.read_excel('device_data.xlsx', sheet_name='基本信息')
df_runtime = pd.read_excel('device_data.xlsx', sheet_name='运行数据')
df_maintenance = pd.read_excel('device_data.xlsx', sheet_name='维修记录')

# 或者一次性读取所有工作表
all_sheets = pd.read_excel('device_data.xlsx', sheet_name=None)
# all_sheets是一个字典,键是工作表名,值是DataFrame

注意:读取Excel需要安装openpyxl或xlrd库。我推荐用openpyxl,它对.xlsx格式支持更好。安装命令:pip install openpyxl

4.4 数据清洗与预处理:让数据变得可用

数据读进来了,但往往不能直接用。你想想看,设备数据里经常有缺失值、重复值、异常值。不清洗的话,做出来的图表全是错的。

我曾经接手过一个项目,前同事做的看板,温度曲线总是莫名其妙地跳变。查了半天,发现是原始数据里混入了几个9999的异常值。这就是数据清洗没做好的后果。

下面是我常用的清洗步骤:

4.4.1 处理缺失值

# 查看缺失值情况
print(df.isnull().sum())

# 方法1:删除包含缺失值的行
df_clean = df.dropna()

# 方法2:用均值填充
df['温度'].fillna(df['温度'].mean(), inplace=True)

# 方法3:用前一个值填充(适用于时间序列数据)
df['温度'].fillna(method='ffill', inplace=True)

4.4.2 处理重复值

# 查看重复行
print(df.duplicated().sum())

# 删除重复行
df_clean = df.drop_duplicates()

# 按指定列判断重复
df_clean = df.drop_duplicates(subset=['设备ID', '时间戳'])

4.4.3 处理异常值

# 使用Z-score方法检测异常值
from scipy import stats
z_scores = stats.zscore(df['温度'])
abs_z_scores = np.abs(z_scores)
filtered_entries = (abs_z_scores < 3)  # 保留Z-score小于3的数据
df_clean = df[filtered_entries]

# 或者用IQR方法
Q1 = df['温度'].quantile(0.25)
Q3 = df['温度'].quantile(0.75)
IQR = Q3 - Q1
df_clean = df[(df['温度'] >= Q1 - 1.5*IQR) & (df['温度'] <= Q3 + 1.5*IQR)]

经验之谈:处理异常值时,不要一刀切。我建议先可视化看看数据分布,再决定阈值。有时候看似异常的值,其实是设备在特殊工况下的正常数据。

4.5 实战案例:设备温度数据清洗

咱们来个完整的例子。假设你拿到了一份设备温度数据,包含设备ID、时间戳、温度值三列。数据里有缺失值、重复行,还有几个明显异常的温度值。

import pandas as pd
import numpy as np

# 1. 读取数据
df = pd.read_csv('device_temperature.csv', encoding='utf-8')

# 2. 查看数据概况
print("数据形状:", df.shape)
print("数据类型:\n", df.dtypes)
print("缺失值统计:\n", df.isnull().sum())

# 3. 清洗数据
# 3.1 删除重复行
df = df.drop_duplicates()

# 3.2 处理缺失值(用前一个有效值填充)
df['温度'] = df['温度'].fillna(method='ffill')

# 3.3 处理异常值(温度正常范围:-20°C ~ 80°C)
df = df[(df['温度'] >= -20) & (df['温度'] <= 80)]

# 4. 转换时间戳为datetime类型
df['时间戳'] = pd.to_datetime(df['时间戳'])

# 5. 按时间排序
df = df.sort_values('时间戳')

print("清洗完成,剩余数据量:", len(df))
print(df.head())

关键点:数据清洗的顺序很重要。我习惯先删重复,再处理缺失,最后处理异常。这样能避免重复操作带来的干扰。

4.6 本章知识体系

下面这张图,是我自己总结的数据接入与清洗的完整流程。你可以把它当作一个检查清单,每次做数据接入时对照着来。

数据接入与清洗流程 1. 数据读取 2. 数据预览 3. 数据清洗 读取方式 • read_csv() • read_excel() • 参数调整 预览内容 • head() / info() • 数据类型检查 • 缺失值统计 清洗操作 • 处理缺失值 • 处理重复值 • 处理异常值 4. 数据转换 转换操作 • 类型转换 • 排序 / 索引设置 ✅ 干净可用的数据

这张图把整个流程串起来了。从数据读取开始,经过预览、清洗、转换,最后得到干净可用的数据。每一步都有对应的Pandas方法,你照着做就行。

我的习惯:每次清洗完数据,我都会保存一份清洗后的副本。这样万一后续发现清洗逻辑有问题,还能回溯到原始数据重新处理。

好了,今天的内容就到这里。数据接入是可视化看板的基础,把这一步做扎实了,后面的图表制作才能事半功倍。记住,好的数据质量,胜过一百个炫酷的图表效果。


公众号:蓝海资料掘金营,微信deep3321