第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天然就是一张表格,行是观测记录,列是变量字段。这和我们做环境数据分析时的思维完全一致。

小技巧: 创建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需要安装openpyxlxlrd库。

# 读取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行(比如表头说明)
)
注意: Excel文件里经常有合并单元格、空行、表头说明等"脏数据"。我建议读取时先用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的值,那基本可以断定是异常值。

核心要点: 数据读进来后,我建议按这个顺序做"体检":head()看长相 → info()看结构 → describe()看分布。三步走完,数据的基本情况就心里有数了。

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核心 数据结构 Series:一维带标签数组 DataFrame:二维表格结构 数据读取 CSV:read_csv() Excel:read_excel() 数据库:read_sql() 数据概览 head() / tail() info():结构+缺失值 describe():统计摘要 索引与选择 列选择:df['列名'] 行选择:loc / iloc

这张图把Pandas基础的四个核心模块串起来了。数据结构是基础,数据读取是入口,数据概览是体检,索引选择是手术刀。把这四个模块吃透了,后面做数据清洗和特征工程就会顺手很多。

好了,第一章的内容就到这里。记住,Pandas不是背出来的,是用出来的。我建议你打开Jupyter Notebook,把今天讲的代码都敲一遍。遇到问题很正常,多试几次就熟练了。


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