第1章:Pandas基础——Series与DataFrame创建、数据读取、数据概览、索引与选择
各位同学好,我是老张。在环境数据分析这个行当摸爬滚打了十几年,我越来越觉得——数据清洗和特征工程,才是真正拉开分析水平差距的地方。今天咱们就从Pandas这个最趁手的工具开始聊。
说实话,Pandas就像是Python数据分析界的瑞士军刀。你想想看,不管是处理气象站点的逐时数据,还是分析河流断面的水质监测记录,第一件事就是把数据装进Pandas里。嗯,咱们今天就把它彻底搞明白。
1.1 Series与DataFrame:两种核心数据结构
Pandas里就两种核心数据结构:Series和DataFrame。我习惯这么理解——Series是一列数据,DataFrame是多列数据组成的表格。
1.1.1 创建Series
先看个最简单的例子。假设我们有一组某监测站点的PM2.5浓度数据:
import pandas as pd
# 从列表创建Series
pm25 = pd.Series([35, 42, 28, 55, 48],
index=['站点A', '站点B', '站点C', '站点D', '站点E'],
name='PM2.5浓度(μg/m³)')
print(pm25)
输出结果长这样:
站点A 35
站点B 42
站点C 28
站点D 55
站点E 48
Name: PM2.5浓度(μg/m³), dtype: int64
我个人习惯用name参数给Series起个名字,这样后面合并到DataFrame时一目了然。曾经有个项目,同事没给Series命名,结果数据一多全乱套了,排查了半天才发现是这个问题。
1.1.2 创建DataFrame
DataFrame才是咱们日常打交道最多的。来看一个环境监测数据的例子:
# 从字典创建DataFrame
data = {
'站点': ['站点A', '站点B', '站点C', '站点D', '站点E'],
'PM2.5': [35, 42, 28, 55, 48],
'PM10': [68, 79, 52, 102, 87],
'NO2': [22, 31, 18, 45, 36],
'日期': ['2024-01-15', '2024-01-15', '2024-01-15', '2024-01-15', '2024-01-15']
}
df = pd.DataFrame(data)
print(df)
输出:
站点 PM2.5 PM10 NO2 日期
0 站点A 35 68 22 2024-01-15
1 站点B 42 79 31 2024-01-15
2 站点C 28 52 18 2024-01-15
3 站点D 55 102 45 2024-01-15
4 站点E 48 87 36 2024-01-15
你看,DataFrame天然就是一张表格,行是观测记录,列是变量字段。这和我们做环境数据分析时的思维完全一致。
1.2 数据读取:从文件到DataFrame
实际工作中,数据很少是手动敲进去的。咱们得学会从各种文件里读数据。我遇到过最头疼的情况——同一个项目里,有人给CSV,有人给Excel,还有人从数据库导数据。所以这三种读取方式必须掌握。
1.2.1 读取CSV文件
CSV是环境数据最常见的格式。气象站、水质监测站导出的数据,十有八九是CSV。
# 读取CSV文件
df_csv = pd.read_csv('环境监测数据.csv', encoding='utf-8')
# 常用参数
df_csv = pd.read_csv(
'环境监测数据.csv',
encoding='utf-8', # 编码格式,中文数据常用utf-8或gbk
sep=',', # 分隔符,默认是逗号
header=0, # 第0行作为列名
index_col='日期', # 指定日期列作为行索引
parse_dates=['日期'], # 自动解析日期格式
na_values=['NA', 'NULL', ''] # 指定哪些值视为缺失值
)
我曾经接手过一个项目,CSV文件里日期列全是"2024/01/15"这种格式,但有的行又是"2024-01-15"。用parse_dates参数一步到位,省去了后面手动转换的麻烦。
1.2.2 读取Excel文件
Excel在环境报告中很常见。注意,读取Excel需要安装openpyxl或xlrd库。
# 读取Excel文件
df_excel = pd.read_excel('水质监测数据.xlsx', sheet_name='Sheet1')
# 读取多个sheet
dfs = pd.read_excel('水质监测数据.xlsx', sheet_name=['Sheet1', 'Sheet2'])
# 指定读取范围
df_excel = pd.read_excel(
'水质监测数据.xlsx',
sheet_name='Sheet1',
header=0, # 第0行作为列名
usecols='A:F', # 只读取A到F列
skiprows=2 # 跳过前2行(比如表头说明)
)
skiprows跳过无关行,再用header指定真正的列名行。别问我怎么知道的——都是踩坑踩出来的。
1.2.3 读取数据库
大型环境监测系统,数据通常存在数据库里。Pandas连接数据库需要配合SQLAlchemy或数据库驱动。
from sqlalchemy import create_engine
# 连接MySQL数据库
engine = create_engine('mysql+pymysql://用户名:密码@主机地址:3306/数据库名')
# 读取整张表
df_db = pd.read_sql_table('空气质量表', con=engine)
# 使用SQL查询读取
df_db = pd.read_sql_query(
"SELECT * FROM 空气质量表 WHERE 日期 >= '2024-01-01'",
con=engine
)
说实话,数据库读取这块,我最常用的还是read_sql_query。因为可以写灵活的SQL语句,只提取需要的数据,避免把整个表都拉下来——数据量大的时候,这能省不少内存。
1.3 数据概览:快速了解你的数据
数据读进来之后,第一件事不是急着分析,而是先看看数据长什么样。我管这叫"数据体检"。
1.3.1 head()与tail()
# 查看前5行
print(df.head())
# 查看前10行
print(df.head(10))
# 查看后5行
print(df.tail())
head()是我用得最多的函数。每次读数据,第一件事就是df.head(),看看列名对不对、数据类型对不对、有没有明显异常值。
1.3.2 info()——数据概览利器
# 查看数据基本信息
print(df.info())
输出示例:
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 1000 entries, 0 to 999
Data columns (total 6 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 站点 1000 non-null object
1 PM2.5 985 non-null float64
2 PM10 980 non-null float64
3 NO2 990 non-null float64
4 日期 1000 non-null datetime64[ns]
5 温度 995 non-null float64
dtypes: datetime64[ns](1), float64(4), object(1)
memory usage: 47.0+ KB
info()能告诉你三件重要的事:数据总量、每列的非空数量、每列的数据类型。你看PM2.5列只有985个非空值,说明有15个缺失值——这就是后续要处理的问题。
1.3.3 describe()——统计摘要
# 查看数值列的统计摘要
print(df.describe())
# 查看所有列的统计摘要(包括非数值列)
print(df.describe(include='all'))
输出示例:
PM2.5 PM10 NO2 温度
count 985.000000 980.00000 990.00000 995.000000
mean 42.350000 78.20000 28.50000 15.300000
std 15.200000 28.40000 12.10000 5.800000
min 12.000000 25.00000 5.00000 -2.000000
25% 30.000000 55.00000 18.00000 10.000000
50% 40.000000 75.00000 27.00000 16.000000
75% 52.000000 98.00000 38.00000 20.000000
max 85.000000 150.00000 60.00000 28.000000
describe()能快速告诉你数据的分布情况。比如PM2.5的最小值是12,最大值是85,均值42.35——如果突然出现一个500的值,那基本可以断定是异常值。
1.4 索引与选择:精准定位数据
数据量大了之后,怎么快速找到你需要的那部分数据?这就涉及到索引和选择了。
1.4.1 列选择
# 选择单列,返回Series
pm25_col = df['PM2.5']
# 选择多列,返回DataFrame
subset = df[['站点', 'PM2.5', 'PM10']]
# 使用点号选择(列名必须是合法标识符)
pm25_col = df.PM2.5 # 等价于 df['PM2.5']
我个人习惯用方括号df['列名'],因为更通用。点号虽然方便,但如果列名有空格或特殊字符就会报错。
1.4.2 行选择——loc和iloc
这是Pandas里最容易混淆的地方。我简单总结一下:
- loc:基于标签(行名、列名)选择
- iloc:基于位置(整数索引)选择
# loc:基于标签选择
# 选择第0行(标签为0的行)
print(df.loc[0])
# 选择第0行到第2行(包含两端)
print(df.loc[0:2])
# 选择特定行和列
print(df.loc[[0, 2, 4], ['站点', 'PM2.5']])
# iloc:基于位置选择
# 选择第0行(位置为0的行)
print(df.iloc[0])
# 选择第0行到第2行(不包含第3行)
print(df.iloc[0:3])
# 选择第0行、第2行,第0列、第1列
print(df.iloc[[0, 2], [0, 1]])
你看,loc的切片是包含结束位置的,而iloc的切片是不包含的——这和Python列表的切片规则一致。我刚开始用的时候也经常搞混,后来记住一句话:loc看名字,iloc看位置。
1.4.3 布尔索引——条件筛选
这是环境数据分析中最常用的选择方式。比如筛选出PM2.5超标(>75μg/m³)的站点:
# 布尔索引
mask = df['PM2.5'] > 75
high_pm25 = df[mask]
print(high_pm25)
# 多条件筛选
mask = (df['PM2.5'] > 75) & (df['NO2'] > 40)
high_pollution = df[mask]
print(high_pollution)
# 使用query方法(更简洁)
high_pollution = df.query('PM2.5 > 75 and NO2 > 40')
布尔索引是我在项目中用得最多的。比如分析重污染天气时,一个df[df['PM2.5'] > 150]就能把所有重度污染记录筛出来。注意多条件时要用括号括起来,这是新手最容易犯的错。
query()方法,写起来更直观。比如df.query('PM2.5 > 75 and 温度 > 20'),比用&符号拼接可读性强多了。
知识体系总览
下面这张图把本章的核心知识点串起来了。我建议你保存下来,后面学习时随时对照。
这张图把Pandas基础的四个核心模块串起来了。数据结构是基础,数据读取是入口,数据概览是体检,索引选择是手术刀。把这四个模块吃透了,后面做数据清洗和特征工程就会顺手很多。
好了,第一章的内容就到这里。记住,Pandas不是背出来的,是用出来的。我建议你打开Jupyter Notebook,把今天讲的代码都敲一遍。遇到问题很正常,多试几次就熟练了。
公众号:蓝海资料掘金营,微信deep3321