Python数据分析利器:Pandas入门学习指南

191 阅读26分钟

初识Pandas

概述

Pandas是一个基于Python的数据处理和分析工具包,它提供了快速、灵活和富有表现力的数据结构来处理结构化(表格、Excel电子表格)和非结构化(时间序列数据)数据。

Pandas的主要数据结构是DataFrame和Series。其中,DataFrame是二维表格,其中每个列可以是不同的数据类型(如,数字、字符串、布尔值等),而Series是一维标记数组,可以存储任何数据类型。

功能

Pandas在数据科学中得到了广泛的应用,特别是在数据清洗和预处理方面。

在实际工作中,Pandas可以帮助你完成以下任务:

数据处理任务具体应用场景
数据清洗处理缺失值、删除重复数据、修正数据类型
数据转换重塑数据结构、聚合计算、透视表生成
数据筛选条件查询、复杂过滤、数据切片
数据分析统计描述、相关性分析、分组操作
数据可视化与Matplotlib结合绘制图表展示数据

安装

安装Pandas

pip install pandas numpy

使用Pandas需要导入Pandas库

import pandas as pd
# 数据处理常常还需要NumPy库配合
import numpy as np

数据结构

Pandas 的核心数据结构是 Series 和 DataFrame,它们分别用于处理 一维 和 二维 数据。此外,更高维的数据(如三维)可以通过 MultiIndex 或外部库(如 xarray)实现。

Series

创建一个Series,其存储了五位学生的考试分数,每个分数都有对应的学生姓名作为索引

import pandas as pd

# 创建一个简单的Series
s = pd.Series([88, 96, 72, 84, 91], index=['小明', '小红', '小张', '小李', '小王'])
print(s)

输出结果:

小明    88
小红    96
小张    72
小李    84
小王    91
dtype: int64

DataFrame

DataFrame是Pandas最常用的数据结构,它就像一个Excel表格,有行有列

创建一个DataFrame,其包含学生的基本信息和各科成绩,横向是不同的属性(列),纵向是不同的学生记录(行)。

# 创建一个学生信息DataFrame
data = {
    '姓名': ['小明', '小红', '小张', '小李', '小王'],
    '年龄': [15, 16, 15, 17, 16],
    '性别': ['男', '女', '男', '男', '女'],
    '语文': [88, 95, 71, 76, 94],
    '数学': [90, 88, 82, 84, 80],
    '英语': [85, 96, 73, 90, 92]
}

df = pd.DataFrame(data)
print(df)

输出结果:

   姓名  年龄 性别  语文  数学  英语
0  小明  15  男  88  90  85
1  小红  16  女  95  88  96
2  小张  15  男  71  82  73
3  小李  17  男  76  84  90
4  小王  16  女  94  80  92

更高维数据

Pandas 早期支持 Panel(三维数据结构),但已弃用。替代方案:使用 MultiIndex DataFrame与 xarray

数据结构之间的关系

维度Pandas 数据结构类比适用场景
一维Series带标签的数组/字典单列数据(如时间序列)
二维DataFrameExcel 表格/SQL 表结构化数据(CSV、数据库)
三维+MultiIndex/xarray面板数据/科学数据金融面板、气象数据

Series:带标签的一维数组

概述

Pandas中的Series是一种一维数组的标签化数据结构,它类似于 Excel 中的一列数据,它能够保存任何类型的数据,主要由一组数据和与之相关的索引两部分构成。通过多种方式创建,并提供了丰富的方法来对数据进行访问、操作和分析。

创建Series

pd.Series()是 pandas 中创建一维数据结构(Series)的核心函数,其常用参数如下:

data: 创建Series的数据源,可以是列表、字典、标量值等

index: 指定Series的索引标签,长度需要与 data 一致。默认会自动创建一个从0-N的整数索引

dtype: 指定Series的数据类型

name: 给Series起一个名称

copy: 是否创建数据的副本

fastpath: 内部优化参数,通常无需手动设置

Series的创建方式非常灵活,以下是几种常见的创建方式:

# 1. 从列表创建,默认索引(从0开始)
s1 = pd.Series([1, 2, 3, 4, 5])
print(s1)
# 输出:
# 0    1
# 1    2
# 2    3
# 3    4
# 4    5
# dtype: int64

# 2. 从列表创建,自定义索引
s2 = pd.Series([1, 2, 3, 4, 5], index=['a', 'b', 'c', 'd', 'e'])
print(s2)
# 输出:
# a    1
# b    2
# c    3
# d    4
# e    5
# dtype: int64

# 3. 从字典创建(字典的键自动成为索引)
s3 = pd.Series({'a': 10, 'b': 20, 'c': 30, 'd': 40})
print(s3)
# 输出:
# a    10
# b    20
# c    30
# d    40
# dtype: int64

# 4. 使用NumPy数组创建
s4 = pd.Series(np.arange(5))
print(s4)
# 输出:
# 0    0
# 1    1
# 2    2
# 3    3
# 4    4
# dtype: int64

# 5. 创建带名称的Series
s5 = pd.Series([1, 2, 3], index=['a', 'b', 'c'], name='示例Series')
print(s5)
# 输出:
# a    1
# b    2
# c    3
# Name: 示例Series, dtype: int64

访问和修改Series

# 通过索引访问
print(s1[0])  # 输出 1
s1[0] = 10   # 修改第一个元素的值为 10

# 通过标签访问
print(s2['a'])  # 输出 1
s2['a'] = 100  # 修改 'a' 标签对应的值为 100

常用方法和属性

# 创建一个Series用于演示
s = pd.Series([10, 20, 30, 40, 50], index=['a', 'b', 'c', 'd', 'e'], name='演示')

# 访问Series的值
print(s.values)  # 输出: [10 20 30 40 50]

# 访问Series的索引
print(s.index)   # 输出: Index(['a', 'b', 'c', 'd', 'e'], dtype='object')

# 获取Series的名称
print(s.name)    # 输出: 演示

# 获取Series的数据类型
print(s.dtype)   # 输出: int64

# 获取Series的长度
print(s.size)    # 输出: 5

# 查看Series的前几个元素
print(s.head(2)) # 输出: a    10
                 #       b    20
                 # Name: 演示, dtype: int64

# 查看Series的后几个元素
print(s.tail(2)) # 输出: d    40
                 #       e    50
                 # Name: 演示, dtype: int64

# 使用索引访问元素
print(s['a'])    # 输出: 10

# 使用位置访问元素
print(s.iloc[0]) # 输出: 10

# 切片操作
print(s[1:3])    # 输出: b    20
                 #       c    30
                 # Name: 演示, dtype: int64

# 条件筛选
print(s[s > 30]) # 输出: d    40
                 #       e    50
                 # Name: 演示, dtype: int64

# 修改元素值
s['a'] = 100
print(s)         # 'a'的值变为100

# 基本统计
print(s.mean())  # 输出平均值
print(s.min())   # 输出最小值
print(s.max())   # 输出最大值
print(s.sum())   # 输出总和

DataFrame:二维表格数据结构

概述

DataFrame是一个二维的表格型数据结构,每个列可以是不同的数据类型(数值、字符串、布尔值等),类似于Excel中的电子表格或SQL中的表。每一行代表数据集中的一个样本,每一列则代表一个特征。

DataFrame结构和属性

DataFrame是一种二维的、表格型的数据结构,可以看做是由多个Series组成的字典。它的每一行代表一个样本,每一列代表一种特征,可以同时存放不同类型的数据。DataFrame结构通常用于数据分析和数据处理中,提供了许多方便的函数和方法以支持数据的操作和转换。

结构构成:

行索引(index):表明不同行,每一行的标识符,横向索引,叫index,0轴,axis=0。可以自定义或者由Pandas自动生成

列索引(columns):表名不同列,每一列的标识符,纵向索引,叫columns,1轴,axis=1。可以自定义或者由Pandas自动生成

数据(data):实际存储在DataFrame中的数据,可以是整数、浮点数、字符串等各种类型的数据

属性(attributes):包括数据类型、形状(行数和列数)、值等信息

方法(methods):包括对数据进行增删改查、统计计算、重塑变换等各种操作的函数

在这里插入图片描述 在这里插入图片描述

创建方式

字典创建

从字典创建DataFrame:将一个字典转换为DataFrame结构

import pandas as pd

data = {'name': ['Tom', 'Jack', 'Mary'],
        'age': [18, 20, 22],
        'gender': ['M', 'M', 'F']}

df = pd.DataFrame(data)

二维数组或列表创建

从二维数组或列表创建DataFrame:将一个二维数组或嵌套列表转换为DataFrame结构

# 从列表创建DataFrame
students = [
    ['小明', 15, '男', 88, 90, 85],
    ['小红', 16, '女', 95, 88, 96],
    ['小张', 15, '男', 71, 82, 73]
]
columns = ['姓名', '年龄', '性别', '语文', '数学', '英语']

df1 = pd.DataFrame(students, columns=columns)
print(df1)

NumPy创建

从二维NumPy数组创建DataFrame

data_array = np.array([
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9]
])
df2 = pd.DataFrame(data_array, columns=['A', 'B', 'C'])
print(df2)

文件创建

从CSV文件中读取数据创建DataFrame

import pandas as pd

df = pd.read_csv('data.csv')

从Excel文件中读取数据创建DataFrame

import pandas as pd

df = pd.read_excel('data.xlsx')

属性和方法

# 创建一个DataFrame
df = pd.DataFrame({
    'A': [1, 2, 3],
    'B': [4, 5, 6],
    'C': ['a', 'b', 'c']
})

# shape属性返回DataFrame的形状(行数,列数)
print(df.shape)  # 输出: (3, 3)

# columns属性返回列索引
print(df.columns)  # 输出: Index(['A', 'B', 'C'], dtype='object')

# index属性返回行索引
print(df.index)  # 输出: RangeIndex(start=0, stop=3, step=1)

# dtypes属性返回每列的数据类型
print(df.dtypes)  
# 输出:
# A      int64
# B      int64
# C     object
# dtype: object

# values属性返回DataFrame中的数据数组
print(df.values)
# 输出:
# [[1 4 'a']
#  [2 5 'b']
#  [3 6 'c']]

# T属性返回转置后的DataFrame
print(df.T)
# 输出:
#    0  1  2
# A  1  2  3
# B  4  5  6
# C  a  b  c

# 返回前n行数据,默认为5行,也可以指定返回的行数。
print(df.head(2))

# 返回后n行数据,默认为5行,也可以指定返回的行数
print(df.tail(2))


# 查看DataFrame的基本统计信息(仅数值列)
print(df.describe())

# 查看DataFrame的简要信息
print(df.info())

访问数据

访问DataFrame的数据非常灵活,可以按列、按行或按条件选择:

# 访问单列数据(返回Series)
ages = df['年龄']
print(ages)

# 访问多列数据(返回DataFrame)
scores = df[['语文', '数学', '英语']]
print(scores)

# 按位置访问行(使用iloc)
first_student = df.iloc[0]  # 第一行
print(first_student)

# 访问特定位置的数据
cell_value = df.iloc[1, 2]  # 第2行第3列
print(cell_value)

# 访问特定区域
subset = df.iloc[0:2, 1:4]  # 前2行,第2-4列
print(subset)

# 按标签访问行(使用loc)
if '小明' in df['姓名'].values:
    student_ming = df.loc[df['姓名'] == '小明']
    print(student_ming)

# 按行索引访问(如果设置了索引)
df_indexed = df.set_index('姓名')
print(df_indexed.loc['小明'])

# 条件筛选
female_students = df[df['性别'] == '女']
print(female_students)

excellent_math = df[df['数学'] >= 85]
print(excellent_math)

# 复杂条件筛选(使用 & 和 |)
target_students = df[(df['年龄'] > 15) & (df['语文'] > 90)]
print(target_students)

索引操作

使用默认的整数索引

DataFrame会自动为每一行分配一个整数索引,从0开始递增。

import pandas as pd

df = pd.DataFrame({'A': [1, 2, 3], 'B': [4, 5, 6]})
print(df)

输出结果:

   A  B
0  1  4
1  2  5
2  3  6

使用指定的列作为索引

set_index()允许将一个或多个现有列设置为索引,并返回一个新的数据框。该方法允许在不创建新对象的情况下修改原始数据帧。

可选参数:

keys: 用于指定要设置为索引的列名列表

drop: 默认为 True,表示删除指定的列,否则保留这些列。如果指定的列不在数据框中,则会引发 KeyError

append: 默认为 False,如果为True,则将新的索引追加到现有索引之后(对于具有多级索引的数据框)

inplace: 默认值为False,如果为True,则在原始数据框上直接进行更改并返回None,否则返回新数据框
import pandas as pd

df = pd.DataFrame({'A': [1, 2, 3], 'B': [4, 5, 6]})
df.set_index('A', inplace=True)
print(df)
   B
A   
1  4
2  5
3  6

同时设置多列为索引

import pandas as pd

df = pd.DataFrame({'A': [1, 2, 3], 'B': [4, 5, 6], 'C': [7, 8, 9]})
df.set_index(['A', 'B'], inplace=True)
print(df)
     C
A B   
1 4  7
2 5  8
3 6  9

直接指定索引

可以在创建DataFrame时直接指定索引。

import pandas as pd

df = pd.DataFrame({'A': [1, 2, 3], 'B': [4, 5, 6]}, index=['a', 'b', 'c'])
print(df)
   A  B
a  1  4
b  2  5
c  3  6

修改行列索引值

import pandas as pd

df = pd.DataFrame({'A': [1, 2, 3], 'B': [4, 5, 6]})

a = ["col_" + str(i) for i in range(df.shape[0])]

# 必须整体全部修改
df.index = a
print(df)
       A  B
col_0  1  4
col_1  2  5
col_2  3  6

重置索引

可以使用reset_index()方法将当前索引重置为默认的整数索引。

可选参数:

drop: 默认为False,如果为True,则删除原始索引并返回只有数字索引的DataFrame或Series

level: 如果DataFrame或Series有多级索引,则可以指定要重置的级别。默认值是所有级别

name: 新索引的名称字符串或一个由名称字符串组成的列表,用于指定在有多个列标签时使用哪个列标签作为新的索引名称。默认情况下,如果数据框有单个列标签,则使用该标签
import pandas as pd

df = pd.DataFrame({'A': [1, 2, 3], 'B': [4, 5, 6]}, index=['a', 'b', 'c'])

df.reset_index(inplace=True)
print(df)
  index  A  B
0     a  1  4
1     b  2  5
2     c  3  6

索引排序

df = pd.DataFrame({'A': [1, 2, 3], 'B': [4, 5, 6]})

# 索引排序
df_sorted_index = df.sort_index(ascending=False)
print("\n索引降序排列:")
print(df_sorted_index)
索引降序排列:
   A  B
0  1  4
1  2  5
2  3  6

MultiIndex操作

多级索引允许在DataFrame中使用多个层次的索引,非常适合处理多维数据

创建MultiIndex

创建一个包含MultiIndex的数据框,其中第一个层级是 '学校',第二个层级是 '年级',第三层是'班级'

# 创建多级索引的DataFrame
import pandas as pd

# 学校学生成绩数据
multi_data = {
    '学校': ['一中', '一中', '一中', '一中', '二中', '二中', '二中', '二中'],
    '年级': ['高一', '高二', '高三', '高二', '高一', '高一', '高二', '高二'],
    '班级': ['1班', '2班', '1班', '2班', '1班', '2班', '1班', '2班'],
    '平均分': [85, 87, 89, 86, 83, 88, 90, 86]
}

df = pd.DataFrame(multi_data)

# 设置多级索引
multi_df = df.set_index(['学校', '年级', '班级'])
print("多级索引DataFrame:")
print(multi_df)

输出结果:

多级索引DataFrame:
          平均分
学校 年级 班级     
一中 高一 1班   85
   高二 2班   87
   高三 1班   89
   高二 2班   86
二中 高一 1班   83
      2班   88
   高二 1班   90
      2班   86

访问数据

# 访问多级索引的数据
print("\n一中的所有数据:")
print(multi_df.loc['一中'])

print("\n一中高二的所有数据:")
print(multi_df.loc[('一中', '高二')])

print("\n一中高一1班的数据:")
print(multi_df.loc[('一中', '高一', '1班')])

# 索引的切片操作
print("\n所有学校的高一数据:")
print(multi_df.xs('高一', level='年级'))

输出结果:

一中的所有数据:
       平均分
年级 班级     
高一 1班   85
高二 2班   87
高三 1班   89
高二 2班   86

一中高二的所有数据:
    平均分
班级     
2班   87
2班   86

一中高一1班的数据:
          平均分
学校 年级 班级     
一中 高一 1班   85

所有学校的高一数据:
       平均分
学校 班级     
一中 1班   85
二中 1班   83
   2班   88

数据处理

数据清洗

在实际工作中,拿到的数据往往不是干净整洁的,需要进行清洗处理。Pandas提供了许多便捷的方法:

# 处理缺失值
import numpy as np

# 创建含缺失值的数据
messy_data = {
    '姓名': ['小明', '小红', '小张', np.nan, '小王'],
    '年龄': [15, np.nan, 15, 17, 16],
    '成绩': [88, 95, np.nan, 76, 94]
}
messy_df = pd.DataFrame(messy_data)
print("原始数据:")
print(messy_df)

# 检查是否有缺失值
print("\n是否有缺失值:")
print(messy_df.isnull().any())

# 检查每列缺失值数量
print("\n缺失值数量:")
print(messy_df.isnull().sum())

# 填充缺失值
filled_df = messy_df.fillna({'姓名': '未知', '年龄': messy_df['年龄'].mean(), '成绩': messy_df['成绩'].median()})
print("\n填充后数据:")
print(filled_df)

# 使用前向填充
forward_filled = messy_df.fillna(method='ffill')
print("\n前向填充后:")
print(forward_filled)

# 使用后向填充
back_filled = messy_df.fillna(method='bfill')
print("\n后向填充后:")
print(back_filled)

# 删除包含缺失值的行
cleaned_df = messy_df.dropna()
print("\n删除缺失值后数据:")
print(cleaned_df)

# 删除全为缺失值的行
cleaned_df2 = messy_df.dropna(how='all')
print("\n删除全为缺失值的行:")
print(cleaned_df2)

# 处理重复数据
dup_data = pd.DataFrame({
    '姓名': ['小明', '小红', '小明', '小张'],
    '年龄': [15, 16, 15, 17]
})

# 检查重复行
print("\n重复行:")
print(dup_data.duplicated())

# 删除重复行
unique_data = dup_data.drop_duplicates()
print("\n删除重复行后:")
print(unique_data)

# 基于特定列删除重复
unique_names = dup_data.drop_duplicates(subset=['姓名'])
print("\n基于姓名删除重复后:")
print(unique_names)

输出结果:

原始数据:
    姓名    年龄    成绩
0   小明  15.0  88.0
1   小红   NaN  95.0
2   小张  15.0   NaN
3  NaN  17.0  76.0
4   小王  16.0  94.0

是否有缺失值:
姓名    True
年龄    True
成绩    True
dtype: bool

缺失值数量:
姓名    1
年龄    1
成绩    1
dtype: int64

填充后数据:
   姓名     年龄    成绩
0  小明  15.00  88.0
1  小红  15.75  95.0
2  小张  15.00  91.0
3  未知  17.00  76.0
4  小王  16.00  94.0

前向填充后:
   姓名    年龄    成绩
0  小明  15.0  88.0
1  小红  15.0  95.0
2  小张  15.0  95.0
3  小张  17.0  76.0
4  小王  16.0  94.0

后向填充后:
   姓名    年龄    成绩
0  小明  15.0  88.0
1  小红  15.0  95.0
2  小张  15.0  76.0
3  小王  17.0  76.0
4  小王  16.0  94.0

删除缺失值后数据:
   姓名    年龄    成绩
0  小明  15.0  88.0
4  小王  16.0  94.0

删除全为缺失值的行:
    姓名    年龄    成绩
0   小明  15.0  88.0
1   小红   NaN  95.0
2   小张  15.0   NaN
3  NaN  17.0  76.0
4   小王  16.0  94.0

重复行:
0    False
1    False
2     True
3    False
dtype: bool

删除重复行后:
   姓名  年龄
0  小明  15
1  小红  16
3  小张  17

基于姓名删除重复后:
   姓名  年龄
0  小明  15
1  小红  16
3  小张  17

数据转换与处理

数据转换是数据分析中的重要环节,Pandas提供了多种方法进行数据转换:


data = {
    '姓名': ['张三', '李四', '王五', '赵六'],
    '性别': ['男', '女','男', '女'],
    '语文': [85, 90, 78, 92],
    '数学': [92, 88, 85, 90],
    '英语': [88, 85, 90, 87]
}
df = pd.DataFrame(data)
print("\n原始数据:")
print(df)

# 给DataFrame添加新列
age =  [15, 22, 15, 17]
df['年龄'] = age

df['总分'] = df['语文'] + df['数学'] + df['英语']
df['平均分'] = df['总分'] / 3


print("\n添加新列:")
print(df)

# 修改列名
df = df.rename(columns={'语文': 'Chinese', '数学': 'Math', '英语': 'English'})
print("\n修改列名:")
print(df)


# 删除行或列的方法,axis=0是删除行(默认值)
df_dropped = df.drop(['总分', '平均分'], axis=1)
print("\n删除行或列:")
print(df_dropped)

# 排序
df_sorted_by_total = df.sort_values('总分', ascending=False)  # 总分降序
print("\n排序:")
print(df_sorted_by_total)

# 多列排序
df_multi_sorted = df.sort_values(['性别', '总分'], ascending=[True, False])
print("\n多列排序:")
print(df_multi_sorted)

# 数据类型转换
df['年龄'] = df['年龄'].astype(float)
print("\n数据类型转换:")
print(df.dtypes)

# 应用函数到数据
def grade_level(score):
    if score >= 90:
        return 'A'
    elif score >= 80:
        return 'B'
    elif score >= 70:
        return 'C'
    elif score >= 60:
        return 'D'
    else:
        return 'F'

df['语文等级'] = df['Chinese'].apply(grade_level)
print(df)
print("\n应用函数到数据:")
print(df)

输出结果:

原始数据:
   姓名 性别  语文  数学  英语
0  张三  男  85  92  88
1  李四  女  90  88  85
2  王五  男  78  85  90
3  赵六  女  92  90  87

添加新列:
   姓名 性别  语文  数学  英语  年龄   总分        平均分
0  张三  男  85  92  88  15  265  88.333333
1  李四  女  90  88  85  22  263  87.666667
2  王五  男  78  85  90  15  253  84.333333
3  赵六  女  92  90  87  17  269  89.666667

修改列名:
   姓名 性别  Chinese  Math  English  年龄   总分        平均分
0  张三  男       85    92       88  15  265  88.333333
1  李四  女       90    88       85  22  263  87.666667
2  王五  男       78    85       90  15  253  84.333333
3  赵六  女       92    90       87  17  269  89.666667

删除行或列:
   姓名 性别  Chinese  Math  English  年龄
0  张三  男       85    92       88  15
1  李四  女       90    88       85  22
2  王五  男       78    85       90  15
3  赵六  女       92    90       87  17

排序:
   姓名 性别  Chinese  Math  English  年龄   总分        平均分
3  赵六  女       92    90       87  17  269  89.666667
0  张三  男       85    92       88  15  265  88.333333
1  李四  女       90    88       85  22  263  87.666667
2  王五  男       78    85       90  15  253  84.333333

多列排序:
   姓名 性别  Chinese  Math  English  年龄   总分        平均分
3  赵六  女       92    90       87  17  269  89.666667
1  李四  女       90    88       85  22  263  87.666667
0  张三  男       85    92       88  15  265  88.333333
2  王五  男       78    85       90  15  253  84.333333

数据类型转换:
姓名          object
性别          object
Chinese      int64
Math         int64
English      int64
年龄         float64
总分           int64
平均分        float64
dtype: object
   姓名 性别  Chinese  Math  English    年龄   总分        平均分 语文等级
0  张三  男       85    92       88  15.0  265  88.333333    B
1  李四  女       90    88       85  22.0  263  87.666667    A
2  王五  男       78    85       90  15.0  253  84.333333    C
3  赵六  女       92    90       87  17.0  269  89.666667    A

应用函数到数据:
   姓名 性别  Chinese  Math  English    年龄   总分        平均分 语文等级
0  张三  男       85    92       88  15.0  265  88.333333    B
1  李四  女       90    88       85  22.0  263  87.666667    A
2  王五  男       78    85       90  15.0  253  84.333333    C
3  赵六  女       92    90       87  17.0  269  89.666667    A

数据筛选

# 筛选出性别为男性的行
sex_rows = df[df['sex']=='M']
print(sex_rows)

# 筛选出身高大于170的行
height_rows = df[df['height']>170]
print(height_rows)

统计分析

计算年龄的平均值、标准差、最小值和最大值

age_mean = df['age'].mean()
age_std = df['age'].std()
age_min = df['age'].min()
age_max = df['age'].max()

print('平均年龄:', age_mean)
print('年龄标准差:', age_std)
print('最小年龄:', age_min)
print('最大年龄:', age_max)

分组与聚合

分组聚合是数据分析中常用的操作,可以帮助我们发现数据的模式和规律:

# 假设有一个包含多个班级的成绩单
class_data = {
    '姓名': ['小明', '小红', '小张', '小李', '小王', '小刚', '小美', '小强'],
    '班级': ['一班', '一班', '一班', '二班', '二班', '二班', '三班', '三班'],
    '语文': [88, 95, 71, 76, 94, 88, 65, 91],
    '数学': [90, 88, 82, 84, 80, 96, 75, 88]
}
class_df = pd.DataFrame(class_data)

# 按班级分组计算每个班的平均成绩
class_mean = class_df.groupby('班级')[['语文', '数学']].mean()
print("各班平均成绩:")
print(class_mean)

# 计算每个班级的最高分
class_max = class_df.groupby('班级')[['语文', '数学']].max()
print("\n各班最高分:")
print(class_max)

# 计算不同班级的人数
class_count = class_df.groupby('班级').size()
print("\n各班人数:")
print(class_count)

# 聚合多个统计量
class_stats = class_df.groupby('班级')[['语文', '数学']].agg(['mean', 'max', 'min', 'std'])
print("\n各班详细统计:")
print(class_stats)

输出结果:

各班平均成绩:
           语文         数学
班级                      
一班  84.666667  86.666667
三班  78.000000  81.500000
二班  86.000000  86.666667

各班最高分:
    语文  数学
班级        
一班  95  90
三班  91  88
二班  94  96

各班人数:
班级
一班    3
三班    2
二班    3
dtype: int64

各班详细统计:
           语文                            数学                  
         mean max min        std       mean max min       std
班级                                                           
一班  84.666667  95  71  12.342339  86.666667  90  82  4.163332
三班  78.000000  91  65  18.384776  81.500000  88  75  9.192388
二班  86.000000  94  76   9.165151  86.666667  96  80  8.326664

数据合并与连接

在实际应用中,我们经常需要合并多个数据源的数据:

# 准备两个相关的DataFrame
students_basic = pd.DataFrame({
    '学号': [1001, 1002, 1003, 1004, 1005],
    '姓名': ['小明', '小红', '小张', '小李', '小王'],
    '性别': ['男', '女', '男', '男', '女']
})

students_scores = pd.DataFrame({
    '学号': [1001, 1002, 1003, 1006, 1007],
    '语文': [88, 95, 71, 96, 84],
    '数学': [90, 88, 82, 94, 76]
})

# 合并DataFrame (类似SQL的JOIN)
# 内连接(只保留共有的学号)
merged_inner = pd.merge(students_basic, students_scores, on='学号', how='inner')
print("内连接结果:")
print(merged_inner)

# 左连接(保留左表的所有学号)
merged_left = pd.merge(students_basic, students_scores, on='学号', how='left')
print("\n左连接结果:")
print(merged_left)

# 右连接(保留右表的所有学号)
merged_right = pd.merge(students_basic, students_scores, on='学号', how='right')
print("\n右连接结果:")
print(merged_right)

# 外连接(保留所有学号)
merged_outer = pd.merge(students_basic, students_scores, on='学号', how='outer')
print("\n外连接结果:")
print(merged_outer)

输出结果:

内连接结果:
     学号  姓名 性别  语文  数学
0  1001  小明  男  88  90
1  1002  小红  女  95  88
2  1003  小张  男  71  82

左连接结果:
     学号  姓名 性别    语文    数学
0  1001  小明  男  88.0  90.0
1  1002  小红  女  95.0  88.0
2  1003  小张  男  71.0  82.0
3  1004  小李  男   NaN   NaN
4  1005  小王  女   NaN   NaN

右连接结果:
     学号   姓名   性别  语文  数学
0  1001   小明    男  88  90
1  1002   小红    女  95  88
2  1003   小张    男  71  82
3  1006  NaN  NaN  96  94
4  1007  NaN  NaN  84  76

外连接结果:
     学号   姓名   性别    语文    数学
0  1001   小明    男  88.0  90.0
1  1002   小红    女  95.0  88.0
2  1003   小张    男  71.0  82.0
3  1004   小李    男   NaN   NaN
4  1005   小王    女   NaN   NaN
5  1006  NaN  NaN  96.0  94.0
6  1007  NaN  NaN  84.0  76.0

Pandas数据分析实践

通过一个完整的案例来展示Pandas的数据分析流程。假设有一份电子商务网站的销售数据,需要分析其销售趋势和产品表现。

准备数据

创建一个模拟的电商销售数据

import pandas as pd
import numpy as np
from datetime import datetime, timedelta

# 生成日期序列
start_date = datetime(2023, 1, 1)
dates = [start_date + timedelta(days=i) for i in range(100)]

# 生成随机销售数据
np.random.seed(42)  # 设置随机种子,使结果可重现
products = ['手机', '笔记本电脑', '平板', '耳机', '智能手表']
categories = ['电子产品', '电子产品', '电子产品', '配件', '配件']
regions = ['华东', '华南', '华北', '西南', '西北']

sales_data = []
for _ in range(500):  # 生成500条销售记录
    date = np.random.choice(dates)
    product = np.random.choice(products)
    category = categories[products.index(product)]  # 对应的类别
    quantity = np.random.randint(1, 10)
    price = np.random.choice([1999, 5999, 3999, 599, 1299])
    region = np.random.choice(regions)
    
    sales_data.append([date, product, category, quantity, price, region])

# 创建DataFrame
sales_df = pd.DataFrame(sales_data, 
                       columns=['日期', '产品', '类别', '数量', '单价', '地区'])

# 添加总销售额列
sales_df['销售额'] = sales_df['数量'] * sales_df['单价']

print("销售数据概览:")
print(sales_df.head())
销售数据概览:
          日期    产品    类别  数量    单价  地区    销售额
0 2023-02-21  智能手表    配件   8  1299  西北  10392
1 2023-03-24    平板  电子产品   8  1299  西南  10392
2 2023-01-24    平板  电子产品   6  1299  华南   7794
3 2023-03-29    耳机    配件   6  5999  西南  35994
4 2023-01-21    手机  电子产品   6  1299  西南   7794

基本信息探索

print("\n数据形状:", sales_df.shape)
print("\n数据类型:")
print(sales_df.dtypes)
print("\n基本统计信息:")
print(sales_df.describe())
数据形状: (500, 7)

数据类型:
日期     datetime64[ns]
产品             object
类别             object
数量              int64
单价              int64
地区             object
销售额             int64
dtype: object

基本统计信息:
                                  日期          数量           单价           销售额
count                            500  500.000000   500.000000    500.000000
mean   2023-02-18 01:12:00.000000256    5.040000  2738.000000  13853.160000
min              2023-01-01 00:00:00    1.000000   599.000000    599.000000
25%              2023-01-24 00:00:00    3.000000  1299.000000   3999.000000
50%              2023-02-17 12:00:00    5.000000  1999.000000   9093.000000
75%              2023-03-15 00:00:00    7.000000  3999.000000  17997.000000
max              2023-04-10 00:00:00    9.000000  5999.000000  53991.000000
std                              NaN    2.568969  1996.655269  13153.359302

按日期统计销售额

# 时间序列分析 - 按日期统计销售额
daily_sales = sales_df.groupby('日期')['销售额'].sum().reset_index()
print("\n每日销售额(部分展示):")
print(daily_sales.head())
每日销售额(部分展示):
          日期     销售额
0 2023-01-01  126660
1 2023-01-02  122264
2 2023-01-03   56163
3 2023-01-04   98566
4 2023-01-05   55958

按产品类别分析销售情况

category_analysis = sales_df.groupby('类别').agg({
    '销售额': ['sum', 'mean'],
    '数量': ['sum', 'mean']
}).reset_index()
print("\n按产品类别分析:")
print(category_analysis)
按产品类别分析:
     类别      销售额                  数量          
             sum          mean   sum      mean
0  电子产品  4326137  14091.651466  1563  5.091205
1    配件  2600443  13473.797927   957  4.958549

地区销售分析

region_analysis = sales_df.groupby('地区')['销售额'].sum().sort_values(ascending=False)
print("\n地区销售排名:")
print(region_analysis)
地区销售排名:
地区
西北    1469452
华南    1425074
西南    1420307
华东    1392112
华北    1219635
Name: 销售额, dtype: int64

最畅销产品分析

top_products = sales_df.groupby('产品').agg({
    '销售额': 'sum',
    '数量': 'sum'
}).sort_values('销售额', ascending=False)
print("\n产品销售排名:")
print(top_products)
产品销售排名:
           销售额   数量
产品                 
手机     1674080  620
智能手表   1466313  487
笔记本电脑  1408144  456
平板     1243913  487
耳机     1134130  470

交叉分析:地区与产品的销售关系

cross_analysis = pd.pivot_table(
    sales_df, 
    values='销售额',
    index='地区',
    columns='产品',
    aggfunc='sum',
    fill_value=0
)
print("\n地区-产品销售交叉表:")
print(cross_analysis)
地区-产品销售交叉表:
产品      平板      手机    智能手表   笔记本电脑      耳机
地区                                        
华东  250803  416274  244918  258711  221406
华北  187520  264995  274978  238728  253414
华南  313886  367256  246917  292009  205006
西北  241180  398477  237706  383387  208702
西南  250524  227078  461794  235309  245602

性能优化

当处理大型数据集时,内存和性能可能会成为瓶颈,以下是一些优化技巧

1.使用适当的数据类型节省内存

def optimize_dtypes(df):
    """优化DataFrame的数据类型以减少内存使用"""
    for col in df.columns:
        # 数值型数据优化
        if df[col].dtype == 'int64':
            if df[col].min() >= 0:
                if df[col].max() < 255:
                    df[col] = df[col].astype('uint8')
                elif df[col].max() < 65535:
                    df[col] = df[col].astype('uint16')
                else:
                    df[col] = df[col].astype('uint32')
        elif df[col].dtype == 'float64':
            df[col] = df[col].astype('float32')
    return df

2.分块读取大文件

def read_csv_in_chunks(filename, chunk_size=10000):
    """分块读取大型CSV文件"""
    chunks = []
    for chunk in pd.read_csv(filename, chunksize=chunk_size):
        # 对每个块进行处理
        processed_chunk = process_chunk(chunk)  # 自定义处理函数
        chunks.append(processed_chunk)
    
    # 合并所有处理后的块
    return pd.concat(chunks)

3.使用过滤器减少内存使用

def filter_before_load(filename, columns_to_use):
    """只加载需要的列"""
    return pd.read_csv(filename, usecols=columns_to_use)