第3章:Python数据处理核心库:NumPy与Pandas基础

说实话,做遥测数据可视化,最绕不开的就是NumPy和Pandas。我刚开始接触遥测数据时,面对一堆时间戳和传感器数值,整个人是懵的。后来才发现,这两个库就是数据处理的两把瑞士军刀。今天咱们就把它们掰开揉碎了讲清楚。

本章核心脉络:NumPy负责底层数组运算,Pandas负责结构化数据处理。两者配合,能搞定90%的遥测数据清洗任务。

遥测数据处理核心 NumPy 数组运算 数组创建 索引切片 统计计算 Pandas 结构化数据 DataFrame 时间序列 数据清洗 数据清洗实战 缺失值处理 异常值检测 数据规整 📊 遥测数据 → 清洗 → 可视化

3.1 NumPy基础:数组操作与统计计算

NumPy的核心就是ndarray,也就是N维数组。你想想看,遥测数据里一个传感器每秒采集1000个点,一小时就是360万个数据点。用Python原生列表处理?那速度慢得让人抓狂。NumPy底层用C语言实现,运算速度能快几十倍。

3.1.1 创建数组

我个人习惯用np.array()把列表转成数组,或者用np.zeros()、np.ones()预分配空间。在遥测场景里,经常需要生成时间轴,这时候np.arange()和np.linspace()就特别好用。

import numpy as np

# 从列表创建
sensor_data = np.array([23.5, 24.1, 23.8, 24.3, 23.9])
print(sensor_data.dtype)  # float64

# 生成时间轴:0到10秒,每0.01秒一个点
time_axis = np.arange(0, 10, 0.01)
print(f"时间点数量: {len(time_axis)}")  # 1000个点

# 等间隔生成100个点
freq_axis = np.linspace(0, 100, 100)

我的经验:创建数组时一定要指定dtype。遥测数据里温度、压力这些浮点数用float32就够了,能省一半内存。我之前处理一个48小时的振动数据,没注意dtype,结果内存爆了。后来改成float32,问题就解决了。

3.1.2 数组索引与切片

NumPy的切片语法和Python列表很像,但功能更强。你可以用布尔索引来筛选数据,这在遥测数据里太常用了。

# 二维数组:3个传感器,每个采集5个点
data = np.array([
    [23.5, 24.1, 23.8, 24.3, 23.9],
    [45.2, 45.8, 44.9, 46.1, 45.5],
    [12.3, 12.1, 12.5, 12.0, 12.4]
])

# 取第2个传感器的所有数据
sensor2 = data[1, :]  # [45.2, 45.8, 44.9, 46.1, 45.5]

# 布尔索引:找出温度超过24.0的点
temp = data[0, :]
high_temp = temp[temp > 24.0]  # [24.1, 24.3]

3.1.3 统计计算

NumPy提供了全套统计函数。我经常用np.mean()、np.std()来算均值和标准差,用np.percentile()来算分位数。这些在遥测数据异常检测里是基础操作。

# 模拟遥测数据:1000个温度值
np.random.seed(42)
temperatures = np.random.normal(loc=25.0, scale=2.0, size=1000)

# 基本统计量
mean_temp = np.mean(temperatures)
std_temp = np.std(temperatures)
max_temp = np.max(temperatures)
min_temp = np.min(temperatures)

# 分位数
q25, q50, q75 = np.percentile(temperatures, [25, 50, 75])

print(f"均值: {mean_temp:.2f}, 标准差: {std_temp:.2f}")
print(f"中位数: {q50:.2f}, 四分位距: {q75-q25:.2f}")

避坑指南:我曾经用np.mean()处理含NaN的数据,结果返回了NaN。后来才知道要加np.nanmean()。遥测数据经常有缺失值,记得用nan开头的函数。

3.2 Pandas基础:DataFrame与时间序列

如果说NumPy是数据处理的发动机,那Pandas就是驾驶舱。DataFrame这个数据结构,说白了就是带标签的表格。每一列有名字,每一行有索引,操作起来特别直观。

3.2.1 创建DataFrame

我习惯用字典来创建DataFrame,键就是列名。在遥测场景里,通常会把时间戳作为索引。

import pandas as pd

# 创建遥测数据表
data = {
    'timestamp': ['2024-01-01 00:00:00', '2024-01-01 00:01:00',
                  '2024-01-01 00:02:00', '2024-01-01 00:03:00'],
    'temperature': [23.5, 24.1, 23.8, 24.3],
    'pressure': [1013.2, 1013.5, 1013.1, 1013.8],
    'humidity': [45.2, 45.8, 44.9, 46.1]
}
df = pd.DataFrame(data)

# 把时间戳列转成datetime类型,并设为索引
df['timestamp'] = pd.to_datetime(df['timestamp'])
df.set_index('timestamp', inplace=True)

print(df.head())

3.2.2 时间序列处理

Pandas的时间序列功能非常强大。你可以用resample()重采样,用shift()做滞后计算。我记得有一次做振动数据分析,需要把毫秒级数据重采样到秒级,用resample('1S').mean()一行代码就搞定了。

# 生成高频时间序列
time_index = pd.date_range('2024-01-01', periods=1000, freq='100ms')
values = np.random.randn(1000).cumsum() + 20
ts = pd.Series(values, index=time_index)

# 重采样到秒级
ts_second = ts.resample('1S').mean()

# 计算滑动平均
ts_ma = ts.rolling(window=10).mean()

# 计算差分(变化率)
ts_diff = ts.diff()

我的习惯:处理时间序列时,我总会先检查时间索引是否连续。用df.index.is_monotonic_increasing检查,用df.asfreq()填充缺失的时间点。不然画出来的图会有断点,看起来很奇怪。

3.3 数据清洗实战

真实世界的遥测数据,从来都不是干干净净的。缺失值、异常值、重复值,什么妖魔鬼怪都有。数据清洗占了我工作中至少60%的时间。嗯,这里要注意,清洗做不好,后面分析全是白费。

3.3.1 缺失值处理

遥测数据缺失的原因很多:传感器故障、网络丢包、存储异常。处理方式有三种:删除、填充、插值。我个人偏好插值,因为它能保留数据趋势。

# 模拟含缺失值的遥测数据
df_clean = pd.DataFrame({
    'time': pd.date_range('2024-01-01', periods=10, freq='1H'),
    'value': [23.5, 24.1, None, 24.3, None, 23.9, 24.2, None, 24.0, 23.8]
})
df_clean.set_index('time', inplace=True)

# 方法1:删除缺失值
df_drop = df_clean.dropna()

# 方法2:向前填充(用上一个值填充)
df_ffill = df_clean.fillna(method='ffill')

# 方法3:线性插值(推荐)
df_interp = df_clean.interpolate(method='linear')

print("插值结果:")
print(df_interp)

注意:如果连续缺失超过3个点,插值结果可能不准确。我曾经处理一个温度传感器数据,连续缺失了10个点,用线性插值后画出的曲线完全失真。后来改用limit=3参数,超过3个缺失就直接标记为无效。

3.3.2 异常值检测

异常值检测我常用两种方法:3σ原则和IQR(四分位距)法。3σ适合正态分布的数据,IQR更稳健,不受极端值影响。

# 模拟含异常值的数据
np.random.seed(42)
data = np.random.normal(25, 2, 100)
data[50] = 35.0  # 人为加入异常值
data[80] = 15.0

# 方法1:3σ原则
mean = np.mean(data)
std = np.std(data)
outliers_3sigma = data[(data < mean - 3*std) | (data > mean + 3*std)]
print(f"3σ检测到异常值: {outliers_3sigma}")

# 方法2:IQR法
q1 = np.percentile(data, 25)
q3 = np.percentile(data, 75)
iqr = q3 - q1
lower_bound = q1 - 1.5 * iqr
upper_bound = q3 + 1.5 * iqr
outliers_iqr = data[(data < lower_bound) | (data > upper_bound)]
print(f"IQR检测到异常值: {outliers_iqr}")

3.3.3 数据规整

数据规整包括去重、类型转换、格式统一。遥测数据里经常遇到时间格式不统一的问题,有的用ISO格式,有的用时间戳。我习惯统一转成datetime类型。

# 数据规整示例
df_raw = pd.DataFrame({
    'time': ['2024-01-01 00:00:00', '2024-01-01 00:00:00',  # 重复
             '2024-01-01 00:01:00', '2024-01-01 00:02:00'],
    'temp': ['23.5', '23.5', '24.1', '23.8'],  # 字符串类型
    'pressure': [1013.2, 1013.2, 1013.5, None]  # 缺失值
})

# 去重
df_dedup = df_raw.drop_duplicates()

# 类型转换
df_dedup['temp'] = pd.to_numeric(df_dedup['temp'])

# 时间统一
df_dedup['time'] = pd.to_datetime(df_dedup['time'])

# 缺失值填充
df_dedup['pressure'].fillna(method='ffill', inplace=True)

print("清洗后的数据:")
print(df_dedup)

实战总结:数据清洗没有银弹。我的经验是:先了解数据来源和采集方式,再决定清洗策略。比如温度传感器,变化缓慢,用向前填充没问题;但振动传感器,变化剧烈,最好用插值或直接丢弃缺失段。

好了,NumPy和Pandas的基础就讲到这里。这两个库是遥测数据处理的基石,多练练就能找到感觉。下一节我们会用这些工具,真正动手处理一份真实的遥测数据集。


可可精品资料库