DataFrame 数据结构详解
🎯 本章目标:彻底掌握 Pandas 最核心的二维数据结构 DataFrame,它是数据分析的主战场。
1.1 什么是 DataFrame?
1.1.1 从 Excel 表格说起
如果你用过 Excel,DataFrame 就是编程版的 Excel 表格:
#mermaid-svg-3Z5IjrrmOhL5ZAXu{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-3Z5IjrrmOhL5ZAXu .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-3Z5IjrrmOhL5ZAXu .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-3Z5IjrrmOhL5ZAXu .error-icon{fill:#552222;}#mermaid-svg-3Z5IjrrmOhL5ZAXu .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-3Z5IjrrmOhL5ZAXu .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-3Z5IjrrmOhL5ZAXu .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-3Z5IjrrmOhL5ZAXu .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-3Z5IjrrmOhL5ZAXu .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-3Z5IjrrmOhL5ZAXu .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-3Z5IjrrmOhL5ZAXu .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-3Z5IjrrmOhL5ZAXu .marker{fill:#333333;stroke:#333333;}#mermaid-svg-3Z5IjrrmOhL5ZAXu .marker.cross{stroke:#333333;}#mermaid-svg-3Z5IjrrmOhL5ZAXu svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-3Z5IjrrmOhL5ZAXu p{margin:0;}#mermaid-svg-3Z5IjrrmOhL5ZAXu .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-3Z5IjrrmOhL5ZAXu .cluster-label text{fill:#333;}#mermaid-svg-3Z5IjrrmOhL5ZAXu .cluster-label span{color:#333;}#mermaid-svg-3Z5IjrrmOhL5ZAXu .cluster-label span p{background-color:transparent;}#mermaid-svg-3Z5IjrrmOhL5ZAXu .label text,#mermaid-svg-3Z5IjrrmOhL5ZAXu span{fill:#333;color:#333;}#mermaid-svg-3Z5IjrrmOhL5ZAXu .node rect,#mermaid-svg-3Z5IjrrmOhL5ZAXu .node circle,#mermaid-svg-3Z5IjrrmOhL5ZAXu .node ellipse,#mermaid-svg-3Z5IjrrmOhL5ZAXu .node polygon,#mermaid-svg-3Z5IjrrmOhL5ZAXu .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-3Z5IjrrmOhL5ZAXu .rough-node .label text,#mermaid-svg-3Z5IjrrmOhL5ZAXu .node .label text,#mermaid-svg-3Z5IjrrmOhL5ZAXu .image-shape .label,#mermaid-svg-3Z5IjrrmOhL5ZAXu .icon-shape .label{text-anchor:middle;}#mermaid-svg-3Z5IjrrmOhL5ZAXu .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-3Z5IjrrmOhL5ZAXu .rough-node .label,#mermaid-svg-3Z5IjrrmOhL5ZAXu .node .label,#mermaid-svg-3Z5IjrrmOhL5ZAXu .image-shape .label,#mermaid-svg-3Z5IjrrmOhL5ZAXu .icon-shape .label{text-align:center;}#mermaid-svg-3Z5IjrrmOhL5ZAXu .node.clickable{cursor:pointer;}#mermaid-svg-3Z5IjrrmOhL5ZAXu .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-3Z5IjrrmOhL5ZAXu .arrowheadPath{fill:#333333;}#mermaid-svg-3Z5IjrrmOhL5ZAXu .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-3Z5IjrrmOhL5ZAXu .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-3Z5IjrrmOhL5ZAXu .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-3Z5IjrrmOhL5ZAXu .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-3Z5IjrrmOhL5ZAXu .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-3Z5IjrrmOhL5ZAXu .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-3Z5IjrrmOhL5ZAXu .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-3Z5IjrrmOhL5ZAXu .cluster text{fill:#333;}#mermaid-svg-3Z5IjrrmOhL5ZAXu .cluster span{color:#333;}#mermaid-svg-3Z5IjrrmOhL5ZAXu div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-3Z5IjrrmOhL5ZAXu .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-3Z5IjrrmOhL5ZAXu rect.text{fill:none;stroke-width:0;}#mermaid-svg-3Z5IjrrmOhL5ZAXu .icon-shape,#mermaid-svg-3Z5IjrrmOhL5ZAXu .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-3Z5IjrrmOhL5ZAXu .icon-shape p,#mermaid-svg-3Z5IjrrmOhL5ZAXu .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-3Z5IjrrmOhL5ZAXu .icon-shape .label rect,#mermaid-svg-3Z5IjrrmOhL5ZAXu .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-3Z5IjrrmOhL5ZAXu .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-3Z5IjrrmOhL5ZAXu .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-3Z5IjrrmOhL5ZAXu :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Excel 表格
行 Row
第1行, 第2行...
列 Column
姓名, 年龄, 城市...
单元格 Cell
具体数据
DataFrame
行索引 Index
0, 1, 2...
列名 Columns
'姓名', '年龄'...
数据 Values
具体数据
DataFrame = 多个 Series 共享同一个行索引
#mermaid-svg-2yoULkvn315tSdZS{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-2yoULkvn315tSdZS .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-2yoULkvn315tSdZS .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-2yoULkvn315tSdZS .error-icon{fill:#552222;}#mermaid-svg-2yoULkvn315tSdZS .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-2yoULkvn315tSdZS .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-2yoULkvn315tSdZS .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-2yoULkvn315tSdZS .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-2yoULkvn315tSdZS .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-2yoULkvn315tSdZS .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-2yoULkvn315tSdZS .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-2yoULkvn315tSdZS .marker{fill:#333333;stroke:#333333;}#mermaid-svg-2yoULkvn315tSdZS .marker.cross{stroke:#333333;}#mermaid-svg-2yoULkvn315tSdZS svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-2yoULkvn315tSdZS p{margin:0;}#mermaid-svg-2yoULkvn315tSdZS .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-2yoULkvn315tSdZS .cluster-label text{fill:#333;}#mermaid-svg-2yoULkvn315tSdZS .cluster-label span{color:#333;}#mermaid-svg-2yoULkvn315tSdZS .cluster-label span p{background-color:transparent;}#mermaid-svg-2yoULkvn315tSdZS .label text,#mermaid-svg-2yoULkvn315tSdZS span{fill:#333;color:#333;}#mermaid-svg-2yoULkvn315tSdZS .node rect,#mermaid-svg-2yoULkvn315tSdZS .node circle,#mermaid-svg-2yoULkvn315tSdZS .node ellipse,#mermaid-svg-2yoULkvn315tSdZS .node polygon,#mermaid-svg-2yoULkvn315tSdZS .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-2yoULkvn315tSdZS .rough-node .label text,#mermaid-svg-2yoULkvn315tSdZS .node .label text,#mermaid-svg-2yoULkvn315tSdZS .image-shape .label,#mermaid-svg-2yoULkvn315tSdZS .icon-shape .label{text-anchor:middle;}#mermaid-svg-2yoULkvn315tSdZS .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-2yoULkvn315tSdZS .rough-node .label,#mermaid-svg-2yoULkvn315tSdZS .node .label,#mermaid-svg-2yoULkvn315tSdZS .image-shape .label,#mermaid-svg-2yoULkvn315tSdZS .icon-shape .label{text-align:center;}#mermaid-svg-2yoULkvn315tSdZS .node.clickable{cursor:pointer;}#mermaid-svg-2yoULkvn315tSdZS .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-2yoULkvn315tSdZS .arrowheadPath{fill:#333333;}#mermaid-svg-2yoULkvn315tSdZS .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-2yoULkvn315tSdZS .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-2yoULkvn315tSdZS .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-2yoULkvn315tSdZS .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-2yoULkvn315tSdZS .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-2yoULkvn315tSdZS .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-2yoULkvn315tSdZS .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-2yoULkvn315tSdZS .cluster text{fill:#333;}#mermaid-svg-2yoULkvn315tSdZS .cluster span{color:#333;}#mermaid-svg-2yoULkvn315tSdZS div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-2yoULkvn315tSdZS .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-2yoULkvn315tSdZS rect.text{fill:none;stroke-width:0;}#mermaid-svg-2yoULkvn315tSdZS .icon-shape,#mermaid-svg-2yoULkvn315tSdZS .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-2yoULkvn315tSdZS .icon-shape p,#mermaid-svg-2yoULkvn315tSdZS .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-2yoULkvn315tSdZS .icon-shape .label rect,#mermaid-svg-2yoULkvn315tSdZS .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-2yoULkvn315tSdZS .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-2yoULkvn315tSdZS .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-2yoULkvn315tSdZS :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} DataFrame
Series 1: 姓名
张三, 李四, 王五
Series 2: 年龄
25, 30, 28
Series 3: 城市
北京, 上海, 广州
共享行索引
0, 1, 2
1.1.2 DataFrame 的核心组成
python
import pandas as pd
df = pd.DataFrame({
'姓名': ['张三', '李四', '王五'],
'年龄': [25, 30, 28],
'城市': ['北京', '上海', '广州']
})
print(df)
输出:
姓名 年龄 城市
0 张三 25 北京
1 李四 30 上海
2 王五 28 广州
三个核心属性:
python
# 1. 行索引(Index)
print(df.index) # RangeIndex(start=0, stop=3, step=1)
# 2. 列名(Columns)
print(df.columns) # Index(['姓名', '年龄', '城市'], dtype='object')
# 3. 数据值(Values)
print(df.values)
# [['张三' 25 '北京']
# ['李四' 30 '上海']
# ['王五' 28 '广州']]
1.2 创建 DataFrame 的 N 种方式
1.2.1 从字典创建(最常用)
python
import pandas as pd
# 🌟 方式1:字典的 value 是列表
# 每个 key 是列名,每个 value 是该列的数据
df1 = pd.DataFrame({
'姓名': ['张三', '李四', '王五', '赵六'],
'年龄': [25, 30, 28, 35],
'城市': ['北京', '上海', '广州', '深圳'],
'月薪': [15000, 20000, 18000, 25000]
})
print(df1)
💡 原理:字典的每个 key-value 对变成一列,key 是列名,value 是列数据。
python
# 🌟 方式2:字典的 value 是 Series
# 可以指定不同的索引
df2 = pd.DataFrame({
'A': pd.Series([1, 2, 3], index=['x', 'y', 'z']),
'B': pd.Series([4, 5, 6], index=['x', 'y', 'z'])
})
print(df2)
1.2.2 从列表的列表创建
python
# 🌟 方式3:列表的列表(二维数组)
# 需要手动指定列名
df3 = pd.DataFrame(
[['张三', 25, '北京'],
['李四', 30, '上海'],
['王五', 28, '广州']],
columns=['姓名', '年龄', '城市']
)
print(df3)
💡 适用场景:从数据库查询结果、API 返回的 JSON 数组转换而来。
1.2.3 从 NumPy 数组创建
python
import numpy as np
# 🌟 方式4:从 NumPy 数组创建
arr = np.array([
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
])
df4 = pd.DataFrame(
arr,
columns=['A', 'B', 'C'],
index=['x', 'y', 'z']
)
print(df4)
1.2.4 从字典的列表创建
python
# 🌟 方式5:字典的列表(JSON 风格)
# 每个字典是一行数据
df5 = pd.DataFrame([
{'姓名': '张三', '年龄': 25, '城市': '北京'},
{'姓名': '李四', '年龄': 30, '城市': '上海'},
{'姓名': '王五', '年龄': 28, '城市': '广州'}
])
print(df5)
💡 适用场景:从 Web API 获取的 JSON 数据直接转换。
1.2.5 创建方法总结
渲染错误: Mermaid 渲染失败: Parse error on line 7: ...B1pd.DataFrame {'列名': \[数据}] C -----------------------^ Expecting 'SQE', 'DOUBLECIRCLEEND', 'PE', '-)', 'STADIUMEND', 'SUBROUTINEEND', 'PIPE', 'CYLINDEREND', 'DIAMOND_STOP', 'TAGEND', 'TRAPEND', 'INVTRAPEND', 'UNICODE_TEXT', 'TEXT', 'TAGSTART', got 'DIAMOND_START'
1.3 查看和了解 DataFrame
1.3.1 基础查看方法
python
df = pd.DataFrame({
'姓名': ['张三', '李四', '王五', '赵六', '钱七', '孙八'],
'年龄': [25, 30, 28, 35, 22, 40],
'城市': ['北京', '上海', '广州', '深圳', '杭州', '成都'],
'月薪': [15000, 20000, 18000, 25000, 12000, 30000],
'部门': ['技术', '产品', '技术', '销售', '设计', '技术']
})
# 🌟 查看前 N 行(默认5行)
print(df.head()) # 前5行
print(df.head(3)) # 前3行
# 🌟 查看后 N 行
print(df.tail()) # 后5行
print(df.tail(2)) # 后2行
# 🌟 查看形状(行数, 列数)
print(df.shape) # (6, 5)
# 🌟 查看列名
print(df.columns) # Index(['姓名', '年龄', '城市', '月薪', '部门'], dtype='object')
# 🌟 查看行索引
print(df.index) # RangeIndex(start=0, stop=6, step=1)
# 🌟 查看数据类型
print(df.dtypes)
# 姓名 object
# 年龄 int64
# 城市 object
# 月薪 int64
# 部门 object
# dtype: object
1.3.2 详细信息
python
# 🌟 查看详细信息(非空值数量、数据类型、内存占用)
print(df.info())
输出:
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 6 entries, 0 to 5
Data columns (total 5 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 姓名 6 non-null object
1 年龄 6 non-null int64
2 城市 6 non-null object
3 月薪 6 non-null int64
4 部门 6 non-null object
dtypes: int64(2), object(3)
memory usage: 368.0+ bytes
1.3.3 统计摘要
python
# 🌟 数值列的统计摘要
print(df.describe())
输出:
年龄 月薪
count 6.000000 6.000000
mean 30.000000 20000.000000
std 6.190476 6396.862137
min 22.000000 12000.000000
25% 25.750000 15750.000000
50% 29.000000 19000.000000
75% 33.250000 23750.000000
max 40.000000 30000.000000
python
# 🌟 包含所有列(包括非数值)
print(df.describe(include='all'))
# 🌟 只看字符串列
print(df.describe(include=['object']))
# 🌟 查看内存使用
print(df.memory_usage())
1.4 列的操作
1.4.1 选择列
python
# 🌟 选择单列(返回 Series)
ages = df['年龄']
print(type(ages)) # <class 'pandas.core.series.Series'>
# 🌟 选择多列(返回 DataFrame)
subset = df[['姓名', '月薪']]
print(type(subset)) # <class 'pandas.core.frame.DataFrame'>
# 🌟 用点号访问(仅当列名是合法变量名时)
print(df.年龄) # 等同于 df['年龄']
# 注意:如果列名有空格或特殊字符,不能用点号!
1.4.2 新增列
python
# 🌟 直接赋值新增列
df['年薪'] = df['月薪'] * 12
print(df[['姓名', '月薪', '年薪']])
# 🌟 基于条件新增列
df['是否高薪'] = df['月薪'] > 20000
print(df[['姓名', '月薪', '是否高薪']])
# 🌟 使用 assign 方法(链式操作,不修改原数据)
df_new = df.assign(
年薪=lambda x: x['月薪'] * 12,
年龄段=lambda x: x['年龄'].apply(lambda a: '青年' if a < 30 else '中年')
)
print(df_new[['姓名', '年龄', '年薪', '年龄段']])
1.4.3 删除列
python
# 🌟 用 del(直接修改原数据)
df_copy = df.copy()
del df_copy['年薪']
# 🌟 用 pop(删除并返回该列)
df_copy = df.copy()
annual_salary = df_copy.pop('年薪') # 删除'年薪'列,并返回其数据
# 🌟 用 drop(推荐,不修改原数据,返回新 DataFrame)
df_dropped = df.drop(columns=['年薪', '是否高薪'])
print(df_dropped.columns)
# 🌟 删除行(axis=0 或默认)
df_dropped_rows = df.drop(index=[0, 2]) # 删除第0行和第2行
1.4.4 修改列
python
# 🌟 修改整列
df_copy = df.copy()
df_copy['月薪'] = df_copy['月薪'] * 1.1 # 全员涨薪10%
# 🌟 基于条件修改
df_copy = df.copy()
df_copy.loc[df_copy['部门'] == '技术', '月薪'] *= 1.2 # 技术部涨薪20%
# 🌟 修改列名
df_copy = df.copy()
df_copy.columns = ['name', 'age', 'city', 'salary', 'dept']
print(df_copy.columns)
# 🌟 批量修改列名(rename)
df_renamed = df.rename(columns={
'姓名': 'name',
'年龄': 'age',
'月薪': 'salary'
})
print(df_renamed.columns)
1.5 行的操作
1.5.1 选择行
python
# 🌟 用 loc 按标签选择行
df_indexed = df.set_index('姓名') # 把姓名设为索引
print(df_indexed.loc['张三'])
# 🌟 用 iloc 按位置选择行
print(df.iloc[0]) # 第1行
print(df.iloc[0:3]) # 第1-3行
print(df.iloc[[0, 2, 4]]) # 第1, 3, 5行
# 🌟 同时选择行和列
print(df.loc[0:2, ['姓名', '月薪']]) # 第0-2行,姓名和月薪列
print(df.iloc[0:3, 0:2]) # 第0-2行,第0-1列
1.5.2 新增行
python
# 🌟 用 loc 新增行(如果索引不存在)
df_copy = df.copy()
df_copy.loc[len(df_copy)] = ['周九', 27, '武汉', 16000, '产品']
print(df_copy.tail())
# 🌟 用 concat 拼接(推荐用于添加多行)
new_rows = pd.DataFrame({
'姓名': ['吴十', '郑十一'],
'年龄': [29, 33],
'城市': ['南京', '西安'],
'月薪': [19000, 22000],
'部门': ['销售', '产品']
})
df_concat = pd.concat([df, new_rows], ignore_index=True)
print(df_concat.tail())
1.5.3 删除行
python
# 🌟 用 drop 删除行
df_dropped = df.drop(index=[0, 1]) # 删除第0行和第1行
print(df_dropped)
# 🌟 删除满足条件的行
df_filtered = df[df['年龄'] >= 25] # 只保留年龄>=25的行
print(df_filtered)
1.6 索引的操作
1.6.1 设置索引
python
# 🌟 把某列设为索引
df_indexed = df.set_index('姓名')
print(df_indexed)
# 🌟 设置多级索引(层次化索引)
df_multi = df.set_index(['部门', '城市'])
print(df_multi)
# 🌟 重置索引(把索引变回列)
df_reset = df_indexed.reset_index()
print(df_reset)
1.6.2 重新索引
python
# 🌟 重新排列行顺序
df_reindexed = df.reindex([2, 0, 1, 3, 4, 5])
print(df_reindexed)
# 🌟 添加不存在的行(填充 NaN)
df_reindexed = df.reindex([0, 1, 2, 10])
print(df_reindexed)
1.7 条件筛选
1.7.1 单条件筛选
python
# 🌟 筛选月薪大于 20000 的员工
high_salary = df[df['月薪'] > 20000]
print(high_salary)
# 🌟 筛选技术部的员工
tech_dept = df[df['部门'] == '技术']
print(tech_dept)
1.7.2 多条件筛选
python
# 🌟 多条件:技术部且月薪大于 18000
tech_high = df[(df['部门'] == '技术') & (df['月薪'] > 18000)]
print(tech_high)
# 🌟 多条件:北京或上海的员工
beijing_shanghai = df[df['城市'].isin(['北京', '上海'])]
print(beijing_shanghai)
# 🌟 多条件:年龄在 25 到 30 之间
age_range = df[(df['年龄'] >= 25) & (df['年龄'] <= 30)]
print(age_range)
# 🌟 用 between 更简洁
age_range = df[df['年龄'].between(25, 30)]
print(age_range)
1.7.3 条件筛选 + 选择列
python
# 🌟 先筛选,再选择列
result = df.loc[df['月薪'] > 18000, ['姓名', '城市', '月薪']]
print(result)
# 🌟 复杂条件
result = df.loc[
(df['部门'] == '技术') & (df['年龄'] < 30),
['姓名', '年龄', '月薪']
]
print(result)
1.8 排序
1.8.1 按列排序
python
# 🌟 按单列排序(升序)
df_sorted = df.sort_values('年龄')
print(df_sorted)
# 🌟 按单列排序(降序)
df_sorted = df.sort_values('月薪', ascending=False)
print(df_sorted)
# 🌟 按多列排序
# 先按部门排序,部门相同再按月薪降序
df_sorted = df.sort_values(['部门', '月薪'], ascending=[True, False])
print(df_sorted)
1.8.2 按索引排序
python
# 🌟 按索引排序
df_indexed = df.set_index('姓名')
df_sorted = df_indexed.sort_index()
print(df_sorted)
1.9 常用属性和方法速查
1.9.1 属性
| 属性 | 说明 | 示例 |
|---|---|---|
df.shape |
形状(行, 列) | (6, 5) |
df.index |
行索引 | RangeIndex(0, 6) |
df.columns |
列名 | Index(['姓名', ...]) |
df.values |
数据值(NumPy 数组) | array([...]) |
df.dtypes |
每列的数据类型 | 姓名 object, 年龄 int64 |
df.T |
转置(行变列,列变行) | DataFrame |
1.9.2 方法
| 方法 | 说明 | 示例 |
|---|---|---|
df.head(n) |
前 n 行 | df.head(3) |
df.tail(n) |
后 n 行 | df.tail(2) |
df.info() |
详细信息 | df.info() |
df.describe() |
统计摘要 | df.describe() |
df.isnull() |
判断缺失值 | df.isnull().sum() |
df.notnull() |
判断非缺失值 | df.notnull().sum() |
df.dropna() |
删除缺失值 | df.dropna() |
df.fillna(x) |
填充缺失值 | df.fillna(0) |
df.drop() |
删除行/列 | df.drop(columns=['A']) |
df.rename() |
重命名 | df.rename(columns={'A':'B'}) |
df.sort_values() |
按值排序 | df.sort_values('年龄') |
df.sort_index() |
按索引排序 | df.sort_index() |
df.set_index() |
设置索引 | df.set_index('姓名') |
df.reset_index() |
重置索引 | df.reset_index() |
df.copy() |
复制 DataFrame | df.copy() |
1.10 实战案例:员工信息管理
场景
你是 HR,需要管理员工信息并进行分析。
python
import pandas as pd
import numpy as np
# 创建员工数据
employees = pd.DataFrame({
'工号': ['E001', 'E002', 'E003', 'E004', 'E005', 'E006', 'E007', 'E008'],
'姓名': ['张三', '李四', '王五', '赵六', '钱七', '孙八', '周九', '吴十'],
'部门': ['技术', '产品', '技术', '销售', '设计', '技术', '销售', '产品'],
'年龄': [25, 30, 28, 35, 22, 40, 29, 33],
'城市': ['北京', '上海', '广州', '深圳', '杭州', '成都', '武汉', '南京'],
'月薪': [15000, 20000, 18000, 25000, 12000, 30000, 19000, 22000],
'入职年份': [2020, 2019, 2021, 2018, 2022, 2017, 2021, 2019]
})
print("=== 员工信息总览 ===")
print(employees)
print("\n=== 数据统计 ===")
print(employees.describe())
print("\n=== 各部门人数 ===")
print(employees['部门'].value_counts())
print("\n=== 各城市人数 ===")
print(employees['城市'].value_counts())
print("\n=== 平均月薪 by 部门 ===")
dept_salary = employees.groupby('部门')['月薪'].agg(['mean', 'min', 'max', 'count'])
print(dept_salary)
print("\n=== 高薪员工(月薪>20000)===")
high_earners = employees[employees['月薪'] > 20000][['姓名', '部门', '月薪']]
print(high_earners)
print("\n=== 技术部员工详情 ===")
tech_employees = employees[employees['部门'] == '技术']
print(tech_employees[['姓名', '年龄', '城市', '月薪']])
print("\n=== 新增年薪列 ===")
employees['年薪'] = employees['月薪'] * 12
print(employees[['姓名', '月薪', '年薪']])
print("\n=== 新增司龄列 ===")
current_year = 2024
employees['司龄'] = current_year - employees['入职年份']
print(employees[['姓名', '入职年份', '司龄']])
print("\n=== 按月薪排序 ===")
print(employees.sort_values('月薪', ascending=False)[['姓名', '部门', '月薪']])
print("\n=== 统计:技术部平均月薪 vs 全公司平均月薪 ===")
tech_avg = employees[employees['部门'] == '技术']['月薪'].mean()
company_avg = employees['月薪'].mean()
print(f"技术部平均月薪:{tech_avg:.0f}")
print(f"全公司平均月薪:{company_avg:.0f}")
print(f"技术部溢价:{(tech_avg/company_avg - 1)*100:.1f}%")
1.11 小结
核心要点
✅ DataFrame 是什么:二维表格 = 多个 Series 共享行索引
✅ 创建方式:字典、列表的列表、NumPy 数组、字典的列表
✅ 核心属性 :index(行索引)、columns(列名)、values(数据)
✅ 查看数据 :head(), tail(), info(), describe(), shape
✅ 列操作:
- 选择:
df['列名'],df[['列1', '列2']] - 新增:
df['新列'] = ... - 删除:
df.drop(columns=[...]) - 修改:
df['列'] = ...
✅ 行操作:
- 选择:
df.loc[],df.iloc[] - 新增:
df.loc[len(df)] = ...,pd.concat() - 删除:
df.drop(index=[...])
✅ 条件筛选 :df[条件], df.loc[条件, 列]
✅ 排序 :df.sort_values(), df.sort_index()