Pandas DataFrame 数据结构详解

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()

相关推荐
SilentSamsara2 小时前
Pandas 工程化:多层索引、分组聚合与窗口函数的进阶用法
开发语言·python·青少年编程·pandas
起个破名想半天了2 小时前
算法与数据结构之Dijkstra算法
数据结构·dijkstra·单源最短路径·迪杰斯特拉算法
啦啦啦啦啦zzzz3 小时前
数据结构:哈夫曼编码
数据结构·c++·哈夫曼编码
牵牛花主人3 小时前
【无标题】
python·pandas
ChillCoding3 小时前
更新中:C++ STL库,查找排序(基础算法),数据结构,数学算法,竞赛相关基础
数据结构·c++·算法
apcipot_rain4 小时前
计科八股20260606——二叉树、PCA、图深度学习、进程上下文、C语言预编译、文件读写、单精度浮点数
c语言·数据结构·算法·pca·图神经网络
落羽的落羽4 小时前
【项目】JsonRpc框架——开发实现2(业务层)
linux·数据结构·c++·人工智能·算法·json·动态规划
SHARK_pssm4 小时前
【数据结构——单链表】
数据结构·经验分享·笔记
lightqjx4 小时前
【算法】数据结构_并查集
数据结构·算法·并查集