Skip to content

Python数据处理和分析库 ​

NumPy ​

NumPy简介 ​

NumPy(Numerical Python)是Python中用于科学计算的基础包,它提供了:

  • 强大的N维数组对象
  • 广播功能
  • 线性代数、傅里叶变换和随机数生成等功能
  • 与C/C++和Fortran代码集成的工具

NumPy是许多Python科学计算库的基础,如Pandas、SciPy、Matplotlib等。

安装NumPy ​

bash
pip install numpy
pip install numpy

NumPy数组(ndarray) ​

NumPy的核心是ndarray对象,它是一个同构的多维数组,所有元素必须是相同类型。

python
import numpy as np

# 创建一维数组
a = np.array([1, 2, 3, 4, 5])
print(a)  # [1 2 3 4 5]
print(type(a))  # <class 'numpy.ndarray'>

# 创建二维数组
b = np.array([[1, 2, 3], [4, 5, 6]])
print(b)  # [[1 2 3]
          #  [4 5 6]]

# 数组的属性
print(a.shape)  # (5,) - 一维数组,5个元素
print(b.shape)  # (2, 3) - 二维数组,2行3列
print(a.dtype)  # int64 - 元素类型
print(a.ndim)  # 1 - 数组维度
print(a.size)  # 5 - 元素总数
import numpy as np

# 创建一维数组
a = np.array([1, 2, 3, 4, 5])
print(a)  # [1 2 3 4 5]
print(type(a))  # <class 'numpy.ndarray'>

# 创建二维数组
b = np.array([[1, 2, 3], [4, 5, 6]])
print(b)  # [[1 2 3]
          #  [4 5 6]]

# 数组的属性
print(a.shape)  # (5,) - 一维数组,5个元素
print(b.shape)  # (2, 3) - 二维数组,2行3列
print(a.dtype)  # int64 - 元素类型
print(a.ndim)  # 1 - 数组维度
print(a.size)  # 5 - 元素总数

创建数组的方法 ​

python
# 创建全零数组
zeros = np.zeros((2, 3))
print(zeros)  # [[0. 0. 0.]
              #  [0. 0. 0.]]

# 创建全一数组
ones = np.ones((2, 3))
print(ones)  # [[1. 1. 1.]
             #  [1. 1. 1.]]

# 创建指定值的数组
full = np.full((2, 3), 5)
print(full)  # [[5 5 5]
             #  [5 5 5]]

# 创建单位矩阵
eye = np.eye(3)
print(eye)  # [[1. 0. 0.]
            #  [0. 1. 0.]
            #  [0. 0. 1.]]

# 创建等差数列
arange = np.arange(0, 10, 2)
print(arange)  # [0 2 4 6 8]

# 创建等间隔数列
linspace = np.linspace(0, 10, 5)
print(linspace)  # [ 0.   2.5  5.   7.5 10. ]

# 创建随机数组
random = np.random.rand(2, 3)  # 0-1之间的随机数
print(random)  # [[0.123 0.456 0.789]
               #  [0.321 0.654 0.987]]

random_int = np.random.randint(0, 10, (2, 3))  # 0-10之间的随机整数
print(random_int)  # [[1 2 3]
                   #  [4 5 6]]
# 创建全零数组
zeros = np.zeros((2, 3))
print(zeros)  # [[0. 0. 0.]
              #  [0. 0. 0.]]

# 创建全一数组
ones = np.ones((2, 3))
print(ones)  # [[1. 1. 1.]
             #  [1. 1. 1.]]

# 创建指定值的数组
full = np.full((2, 3), 5)
print(full)  # [[5 5 5]
             #  [5 5 5]]

# 创建单位矩阵
eye = np.eye(3)
print(eye)  # [[1. 0. 0.]
            #  [0. 1. 0.]
            #  [0. 0. 1.]]

# 创建等差数列
arange = np.arange(0, 10, 2)
print(arange)  # [0 2 4 6 8]

# 创建等间隔数列
linspace = np.linspace(0, 10, 5)
print(linspace)  # [ 0.   2.5  5.   7.5 10. ]

# 创建随机数组
random = np.random.rand(2, 3)  # 0-1之间的随机数
print(random)  # [[0.123 0.456 0.789]
               #  [0.321 0.654 0.987]]

random_int = np.random.randint(0, 10, (2, 3))  # 0-10之间的随机整数
print(random_int)  # [[1 2 3]
                   #  [4 5 6]]

数组索引和切片 ​

python
# 一维数组索引和切片
arr = np.array([0, 1, 2, 3, 4, 5])
print(arr[0])  # 0
print(arr[-1])  # 5
print(arr[1:4])  # [1 2 3]
print(arr[::2])  # [0 2 4]

# 二维数组索引和切片
arr2d = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
print(arr2d[0, 0])  # 1
print(arr2d[1, 2])  # 6
print(arr2d[:, 0])  # [1 4 7] - 第一列
print(arr2d[0, :])  # [1 2 3] - 第一行
print(arr2d[0:2, 1:3])  # [[2 3]
                        #  [5 6]] - 前两行,后两列
# 一维数组索引和切片
arr = np.array([0, 1, 2, 3, 4, 5])
print(arr[0])  # 0
print(arr[-1])  # 5
print(arr[1:4])  # [1 2 3]
print(arr[::2])  # [0 2 4]

# 二维数组索引和切片
arr2d = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
print(arr2d[0, 0])  # 1
print(arr2d[1, 2])  # 6
print(arr2d[:, 0])  # [1 4 7] - 第一列
print(arr2d[0, :])  # [1 2 3] - 第一行
print(arr2d[0:2, 1:3])  # [[2 3]
                        #  [5 6]] - 前两行,后两列

数组操作 ​

python
# 数组形状变换
arr = np.array([[1, 2, 3], [4, 5, 6]])
print(arr.shape)  # (2, 3)

# 转置
transposed = arr.T
print(transposed.shape)  # (3, 2)

# 重塑
reshaped = arr.reshape(3, 2)
print(reshaped.shape)  # (3, 2)

# 展平
flattened = arr.flatten()
print(flattened)  # [1 2 3 4 5 6]

# 数组拼接
arr1 = np.array([[1, 2], [3, 4]])
arr2 = np.array([[5, 6], [7, 8]])

# 水平拼接
hstack = np.hstack((arr1, arr2))
print(hstack)  # [[1 2 5 6]
               #  [3 4 7 8]]

# 垂直拼接
vstack = np.vstack((arr1, arr2))
print(vstack)  # [[1 2]
               #  [3 4]
               #  [5 6]
               #  [7 8]]

# 数组分割
arr = np.array([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]])

# 水平分割
hsplit = np.hsplit(arr, 2)
print(hsplit)  # [array([[ 1,  2],
               #       [ 5,  6],
               #       [ 9, 10]]),
               #  array([[ 3,  4],
               #       [ 7,  8],
               #       [11, 12]])]

# 垂直分割
vsplit = np.vsplit(arr, 3)
print(vsplit)  # [array([[1, 2, 3, 4]]), array([[5, 6, 7, 8]]), array([[ 9, 10, 11, 12]])]
# 数组形状变换
arr = np.array([[1, 2, 3], [4, 5, 6]])
print(arr.shape)  # (2, 3)

# 转置
transposed = arr.T
print(transposed.shape)  # (3, 2)

# 重塑
reshaped = arr.reshape(3, 2)
print(reshaped.shape)  # (3, 2)

# 展平
flattened = arr.flatten()
print(flattened)  # [1 2 3 4 5 6]

# 数组拼接
arr1 = np.array([[1, 2], [3, 4]])
arr2 = np.array([[5, 6], [7, 8]])

# 水平拼接
hstack = np.hstack((arr1, arr2))
print(hstack)  # [[1 2 5 6]
               #  [3 4 7 8]]

# 垂直拼接
vstack = np.vstack((arr1, arr2))
print(vstack)  # [[1 2]
               #  [3 4]
               #  [5 6]
               #  [7 8]]

# 数组分割
arr = np.array([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]])

# 水平分割
hsplit = np.hsplit(arr, 2)
print(hsplit)  # [array([[ 1,  2],
               #       [ 5,  6],
               #       [ 9, 10]]),
               #  array([[ 3,  4],
               #       [ 7,  8],
               #       [11, 12]])]

# 垂直分割
vsplit = np.vsplit(arr, 3)
print(vsplit)  # [array([[1, 2, 3, 4]]), array([[5, 6, 7, 8]]), array([[ 9, 10, 11, 12]])]

数组运算 ​

python
# 基本运算
arr1 = np.array([1, 2, 3])
arr2 = np.array([4, 5, 6])

print(arr1 + arr2)  # [5 7 9]
print(arr1 - arr2)  # [-3 -3 -3]
print(arr1 * arr2)  # [4 10 18]
print(arr1 / arr2)  # [0.25 0.4  0.5 ]
print(arr1 ** 2)  # [1 4 9]

# 广播
arr3 = np.array([[1, 2, 3], [4, 5, 6]])
scalar = 2
print(arr3 + scalar)  # [[3 4 5]
                      #  [6 7 8]]

# 矩阵乘法
mat1 = np.array([[1, 2], [3, 4]])
mat2 = np.array([[5, 6], [7, 8]])
print(np.dot(mat1, mat2))  # [[19 22]
                           #  [43 50]]

# 或者使用 @ 运算符
print(mat1 @ mat2)  # 同上

# 元素级函数
arr = np.array([0, np.pi/2, np.pi])
print(np.sin(arr))  # [0.0000000e+00 1.0000000e+00 1.2246468e-16]
print(np.cos(arr))  # [ 1.000000e+00  6.123234e-17 -1.000000e+00]
print(np.exp(arr))  # [ 1.          4.81047738 23.14069263]
print(np.log(arr + 1))  # [0.         0.69314718 1.09861229]
# 基本运算
arr1 = np.array([1, 2, 3])
arr2 = np.array([4, 5, 6])

print(arr1 + arr2)  # [5 7 9]
print(arr1 - arr2)  # [-3 -3 -3]
print(arr1 * arr2)  # [4 10 18]
print(arr1 / arr2)  # [0.25 0.4  0.5 ]
print(arr1 ** 2)  # [1 4 9]

# 广播
arr3 = np.array([[1, 2, 3], [4, 5, 6]])
scalar = 2
print(arr3 + scalar)  # [[3 4 5]
                      #  [6 7 8]]

# 矩阵乘法
mat1 = np.array([[1, 2], [3, 4]])
mat2 = np.array([[5, 6], [7, 8]])
print(np.dot(mat1, mat2))  # [[19 22]
                           #  [43 50]]

# 或者使用 @ 运算符
print(mat1 @ mat2)  # 同上

# 元素级函数
arr = np.array([0, np.pi/2, np.pi])
print(np.sin(arr))  # [0.0000000e+00 1.0000000e+00 1.2246468e-16]
print(np.cos(arr))  # [ 1.000000e+00  6.123234e-17 -1.000000e+00]
print(np.exp(arr))  # [ 1.          4.81047738 23.14069263]
print(np.log(arr + 1))  # [0.         0.69314718 1.09861229]

统计函数 ​

python
arr = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])

print(np.min(arr))  # 1 - 最小值
print(np.max(arr))  # 9 - 最大值
print(np.mean(arr))  # 5.0 - 平均值
print(np.median(arr))  # 5.0 - 中位数
print(np.std(arr))  # 2.581988897471611 - 标准差
print(np.var(arr))  # 6.666666666666667 - 方差
print(np.sum(arr))  # 45 - 总和

# 按轴计算
print(np.sum(arr, axis=0))  # [12 15 18] - 按列求和
print(np.sum(arr, axis=1))  # [ 6 15 24] - 按行求和
arr = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])

print(np.min(arr))  # 1 - 最小值
print(np.max(arr))  # 9 - 最大值
print(np.mean(arr))  # 5.0 - 平均值
print(np.median(arr))  # 5.0 - 中位数
print(np.std(arr))  # 2.581988897471611 - 标准差
print(np.var(arr))  # 6.666666666666667 - 方差
print(np.sum(arr))  # 45 - 总和

# 按轴计算
print(np.sum(arr, axis=0))  # [12 15 18] - 按列求和
print(np.sum(arr, axis=1))  # [ 6 15 24] - 按行求和

Pandas ​

Pandas简介 ​

Pandas是一个强大的数据分析和处理库,提供了:

  • 快速、灵活的数据结构(Series和DataFrame)
  • 用于读取和写入数据的工具
  • 数据清洗和预处理功能
  • 数据聚合和分组操作
  • 时间序列分析功能

Pandas是数据科学和机器学习中常用的库,特别适合处理结构化数据。

安装Pandas ​

bash
pip install pandas
pip install pandas

Pandas的核心数据结构 ​

Pandas有两种核心数据结构:

  1. Series:一维标记数组,类似于带索引的列表
  2. DataFrame:二维表格数据结构,由多个Series组成

Series ​

python
import pandas as pd

# 创建Series
s = pd.Series([1, 3, 5, np.nan, 6, 8])
print(s)  # 0    1.0
          # 1    3.0
          # 2    5.0
          # 3    NaN
          # 4    6.0
          # 5    8.0
          # dtype: float64

# 带有索引的Series
s2 = pd.Series([1, 2, 3], index=['a', 'b', 'c'])
print(s2)  # a    1
          # b    2
          # c    3
          # dtype: int64

# 使用字典创建Series
s3 = pd.Series({'a': 1, 'b': 2, 'c': 3})
print(s3)  # 同上

# Series的属性
print(s.index)  # RangeIndex(start=0, stop=6, step=1)
print(s.values)  # [ 1.  3.  5. nan  6.  8.]
print(s.dtype)  # float64

# Series的索引和切片
print(s2['a'])  # 1
print(s2[['a', 'c']])  # a    1
                        # c    3
                        # dtype: int64
print(s2[:2])  # a    1
               # b    2
               # dtype: int64
import pandas as pd

# 创建Series
s = pd.Series([1, 3, 5, np.nan, 6, 8])
print(s)  # 0    1.0
          # 1    3.0
          # 2    5.0
          # 3    NaN
          # 4    6.0
          # 5    8.0
          # dtype: float64

# 带有索引的Series
s2 = pd.Series([1, 2, 3], index=['a', 'b', 'c'])
print(s2)  # a    1
          # b    2
          # c    3
          # dtype: int64

# 使用字典创建Series
s3 = pd.Series({'a': 1, 'b': 2, 'c': 3})
print(s3)  # 同上

# Series的属性
print(s.index)  # RangeIndex(start=0, stop=6, step=1)
print(s.values)  # [ 1.  3.  5. nan  6.  8.]
print(s.dtype)  # float64

# Series的索引和切片
print(s2['a'])  # 1
print(s2[['a', 'c']])  # a    1
                        # c    3
                        # dtype: int64
print(s2[:2])  # a    1
               # b    2
               # dtype: int64

DataFrame ​

python
# 创建DataFrame
# 方法1:使用字典
 data = {
    'name': ['张三', '李四', '王五', '赵六'],
    'age': [20, 25, 30, 35],
    'city': ['北京', '上海', '广州', '深圳']
}

 df = pd.DataFrame(data)
 print(df)
 #    name  age city
 # 0   张三   20   北京
 # 1   李四   25   上海
 # 2   王五   30   广州
 # 3   赵六   35   深圳

# 方法2:使用numpy数组
df2 = pd.DataFrame(np.random.rand(3, 4), columns=['a', 'b', 'c', 'd'])
print(df2)

# DataFrame的属性
print(df.shape)  # (4, 3)
print(df.columns)  # Index(['name', 'age', 'city'], dtype='object')
print(df.index)  # RangeIndex(start=0, stop=4, step=1)
print(df.dtypes)  # name    object
                  # age      int64
                  # city    object
                  # dtype: object

# DataFrame的索引和切片
# 选择列
print(df['name'])  # 选择name列,返回Series
print(df[['name', 'age']])  # 选择多列,返回DataFrame

# 选择行
print(df.loc[0])  # 选择第一行,返回Series
print(df.loc[[0, 2]])  # 选择多行,返回DataFrame

# 选择行和列
print(df.loc[0, 'name'])  # 张三
print(df.loc[:, ['name', 'age']])  # 选择所有行和name、age列
print(df.loc[0:2, ['name', 'age']])  # 选择前3行和name、age列

# 使用iloc进行位置索引
print(df.iloc[0, 0])  # 张三
print(df.iloc[:, 0:2])  # 选择所有行和前2列

# 布尔索引
print(df[df['age'] > 25])  # 选择age大于25的行
print(df[(df['age'] > 20) & (df['city'] == '上海')])  # 选择age大于20且city为上海的行
# 创建DataFrame
# 方法1:使用字典
 data = {
    'name': ['张三', '李四', '王五', '赵六'],
    'age': [20, 25, 30, 35],
    'city': ['北京', '上海', '广州', '深圳']
}

 df = pd.DataFrame(data)
 print(df)
 #    name  age city
 # 0   张三   20   北京
 # 1   李四   25   上海
 # 2   王五   30   广州
 # 3   赵六   35   深圳

# 方法2:使用numpy数组
df2 = pd.DataFrame(np.random.rand(3, 4), columns=['a', 'b', 'c', 'd'])
print(df2)

# DataFrame的属性
print(df.shape)  # (4, 3)
print(df.columns)  # Index(['name', 'age', 'city'], dtype='object')
print(df.index)  # RangeIndex(start=0, stop=4, step=1)
print(df.dtypes)  # name    object
                  # age      int64
                  # city    object
                  # dtype: object

# DataFrame的索引和切片
# 选择列
print(df['name'])  # 选择name列,返回Series
print(df[['name', 'age']])  # 选择多列,返回DataFrame

# 选择行
print(df.loc[0])  # 选择第一行,返回Series
print(df.loc[[0, 2]])  # 选择多行,返回DataFrame

# 选择行和列
print(df.loc[0, 'name'])  # 张三
print(df.loc[:, ['name', 'age']])  # 选择所有行和name、age列
print(df.loc[0:2, ['name', 'age']])  # 选择前3行和name、age列

# 使用iloc进行位置索引
print(df.iloc[0, 0])  # 张三
print(df.iloc[:, 0:2])  # 选择所有行和前2列

# 布尔索引
print(df[df['age'] > 25])  # 选择age大于25的行
print(df[(df['age'] > 20) & (df['city'] == '上海')])  # 选择age大于20且city为上海的行

数据读取和写入 ​

python
# 读取CSV文件
df = pd.read_csv('data.csv')

# 读取Excel文件
df = pd.read_excel('data.xlsx', sheet_name='Sheet1')

# 读取JSON文件
df = pd.read_json('data.json')

# 读取SQL数据库
import sqlite3
conn = sqlite3.connect('database.db')
df = pd.read_sql_query('SELECT * FROM table', conn)

# 写入CSV文件
df.to_csv('output.csv', index=False)

# 写入Excel文件
df.to_excel('output.xlsx', sheet_name='Sheet1', index=False)

# 写入JSON文件
df.to_json('output.json', orient='records')
# 读取CSV文件
df = pd.read_csv('data.csv')

# 读取Excel文件
df = pd.read_excel('data.xlsx', sheet_name='Sheet1')

# 读取JSON文件
df = pd.read_json('data.json')

# 读取SQL数据库
import sqlite3
conn = sqlite3.connect('database.db')
df = pd.read_sql_query('SELECT * FROM table', conn)

# 写入CSV文件
df.to_csv('output.csv', index=False)

# 写入Excel文件
df.to_excel('output.xlsx', sheet_name='Sheet1', index=False)

# 写入JSON文件
df.to_json('output.json', orient='records')

数据清洗和预处理 ​

python
# 检查缺失值
print(df.isnull())  # 检查每个元素是否为缺失值
print(df.isnull().sum())  # 检查每列的缺失值数量

# 处理缺失值
# 删除缺失值
df.dropna()  # 删除包含缺失值的行
df.dropna(axis=1)  # 删除包含缺失值的列

# 填充缺失值
df.fillna(0)  # 用0填充缺失值
df.fillna(method='ffill')  # 向前填充
df.fillna(method='bfill')  # 向后填充
df.fillna(df.mean())  # 用平均值填充

# 数据重复
# 检查重复行
print(df.duplicated())  # 检查每行是否重复
print(df.duplicated().sum())  # 重复行的数量

# 删除重复行
df.drop_duplicates()  # 删除重复行
df.drop_duplicates(subset=['name'], keep='last')  # 根据name列删除重复行,保留最后一行

# 数据类型转换
df['age'] = df['age'].astype(str)  # 将age列转换为字符串类型

# 重命名列
df.rename(columns={'name': '姓名', 'age': '年龄'}, inplace=True)

# 重置索引
df.reset_index(drop=True, inplace=True)
# 检查缺失值
print(df.isnull())  # 检查每个元素是否为缺失值
print(df.isnull().sum())  # 检查每列的缺失值数量

# 处理缺失值
# 删除缺失值
df.dropna()  # 删除包含缺失值的行
df.dropna(axis=1)  # 删除包含缺失值的列

# 填充缺失值
df.fillna(0)  # 用0填充缺失值
df.fillna(method='ffill')  # 向前填充
df.fillna(method='bfill')  # 向后填充
df.fillna(df.mean())  # 用平均值填充

# 数据重复
# 检查重复行
print(df.duplicated())  # 检查每行是否重复
print(df.duplicated().sum())  # 重复行的数量

# 删除重复行
df.drop_duplicates()  # 删除重复行
df.drop_duplicates(subset=['name'], keep='last')  # 根据name列删除重复行,保留最后一行

# 数据类型转换
df['age'] = df['age'].astype(str)  # 将age列转换为字符串类型

# 重命名列
df.rename(columns={'name': '姓名', 'age': '年龄'}, inplace=True)

# 重置索引
df.reset_index(drop=True, inplace=True)

数据聚合和分组 ​

python
# 基本统计
print(df.describe())  # 显示数值列的统计信息
print(df.mean())  # 计算平均值
print(df.median())  # 计算中位数
print(df.max())  # 计算最大值
print(df.min())  # 计算最小值

# 分组操作
grouped = df.groupby('city')  # 按city列分组
print(grouped['age'].mean())  # 计算每个城市的平均年龄
print(grouped['age'].agg(['mean', 'sum', 'count']))  # 计算每个城市的平均年龄、总年龄和人数

# 多列分组
grouped = df.groupby(['city', 'gender'])
print(grouped['age'].mean())

# 透视表
pivot_table = df.pivot_table(index='city', columns='gender', values='age', aggfunc='mean')
print(pivot_table)
# 基本统计
print(df.describe())  # 显示数值列的统计信息
print(df.mean())  # 计算平均值
print(df.median())  # 计算中位数
print(df.max())  # 计算最大值
print(df.min())  # 计算最小值

# 分组操作
grouped = df.groupby('city')  # 按city列分组
print(grouped['age'].mean())  # 计算每个城市的平均年龄
print(grouped['age'].agg(['mean', 'sum', 'count']))  # 计算每个城市的平均年龄、总年龄和人数

# 多列分组
grouped = df.groupby(['city', 'gender'])
print(grouped['age'].mean())

# 透视表
pivot_table = df.pivot_table(index='city', columns='gender', values='age', aggfunc='mean')
print(pivot_table)

数据合并和连接 ​

python
# 创建两个DataFrame
df1 = pd.DataFrame({
    'id': [1, 2, 3, 4],
    'name': ['张三', '李四', '王五', '赵六'],
    'city': ['北京', '上海', '广州', '深圳']
})

df2 = pd.DataFrame({
    'id': [1, 2, 3, 5],
    'age': [20, 25, 30, 35],
    'gender': ['男', '女', '男', '女']
})

# 内连接(只保留匹配的行)
inner_join = pd.merge(df1, df2, on='id', how='inner')
print(inner_join)

# 左连接(保留左表的所有行)
left_join = pd.merge(df1, df2, on='id', how='left')
print(left_join)

# 右连接(保留右表的所有行)
right_join = pd.merge(df1, df2, on='id', how='right')
print(right_join)

# 外连接(保留所有行)
outer_join = pd.merge(df1, df2, on='id', how='outer')
print(outer_join)

# 追加行
df3 = pd.DataFrame({
    'id': [6],
    'name': ['钱七'],
    'city': ['杭州']
})
appended = df1.append(df3, ignore_index=True)
print(appended)
# 创建两个DataFrame
df1 = pd.DataFrame({
    'id': [1, 2, 3, 4],
    'name': ['张三', '李四', '王五', '赵六'],
    'city': ['北京', '上海', '广州', '深圳']
})

df2 = pd.DataFrame({
    'id': [1, 2, 3, 5],
    'age': [20, 25, 30, 35],
    'gender': ['男', '女', '男', '女']
})

# 内连接(只保留匹配的行)
inner_join = pd.merge(df1, df2, on='id', how='inner')
print(inner_join)

# 左连接(保留左表的所有行)
left_join = pd.merge(df1, df2, on='id', how='left')
print(left_join)

# 右连接(保留右表的所有行)
right_join = pd.merge(df1, df2, on='id', how='right')
print(right_join)

# 外连接(保留所有行)
outer_join = pd.merge(df1, df2, on='id', how='outer')
print(outer_join)

# 追加行
df3 = pd.DataFrame({
    'id': [6],
    'name': ['钱七'],
    'city': ['杭州']
})
appended = df1.append(df3, ignore_index=True)
print(appended)

时间序列分析 ​

python
# 创建时间序列
dates = pd.date_range('2023-01-01', periods=7)
print(dates)  # DatetimeIndex(['2023-01-01', '2023-01-02', '2023-01-03', '2023-01-04',
              #              '2023-01-05', '2023-01-06', '2023-01-07'],
              #             dtype='datetime64[ns]', freq='D')

# 创建带时间索引的DataFrame
df = pd.DataFrame(np.random.randn(7, 3), index=dates, columns=['a', 'b', 'c'])
print(df)

# 时间索引的使用
print(df.loc['2023-01-01'])  # 选择2023-01-01的行
print(df.loc['2023-01-01':'2023-01-03'])  # 选择2023-01-01到2023-01-03的行
print(df.loc['2023-01'])  # 选择2023年1月的行

# 时间序列的频率转换
daily_df = pd.DataFrame(
    {'value': np.random.randn(365)}, 
    index=pd.date_range('2023-01-01', periods=365, freq='D')
)

# 转换为月度频率
monthly_df = daily_df.resample('M').mean()
print(monthly_df)

# 移动平均
daily_df['ma7'] = daily_df['value'].rolling(window=7).mean()
print(daily_df)
# 创建时间序列
dates = pd.date_range('2023-01-01', periods=7)
print(dates)  # DatetimeIndex(['2023-01-01', '2023-01-02', '2023-01-03', '2023-01-04',
              #              '2023-01-05', '2023-01-06', '2023-01-07'],
              #             dtype='datetime64[ns]', freq='D')

# 创建带时间索引的DataFrame
df = pd.DataFrame(np.random.randn(7, 3), index=dates, columns=['a', 'b', 'c'])
print(df)

# 时间索引的使用
print(df.loc['2023-01-01'])  # 选择2023-01-01的行
print(df.loc['2023-01-01':'2023-01-03'])  # 选择2023-01-01到2023-01-03的行
print(df.loc['2023-01'])  # 选择2023年1月的行

# 时间序列的频率转换
daily_df = pd.DataFrame(
    {'value': np.random.randn(365)}, 
    index=pd.date_range('2023-01-01', periods=365, freq='D')
)

# 转换为月度频率
monthly_df = daily_df.resample('M').mean()
print(monthly_df)

# 移动平均
daily_df['ma7'] = daily_df['value'].rolling(window=7).mean()
print(daily_df)

数据可视化 ​

Matplotlib ​

Matplotlib是Python中最常用的数据可视化库,它提供了广泛的绘图功能。

bash
pip install matplotlib
pip install matplotlib
python
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd

# 折线图
x = np.linspace(0, 10, 100)
y = np.sin(x)

plt.plot(x, y)
plt.title('正弦函数')
plt.xlabel('x')
plt.ylabel('sin(x)')
plt.grid(True)
plt.show()

# 柱状图
data = {'A': 10, 'B': 15, 'C': 7, 'D': 12}
plt.bar(data.keys(), data.values())
plt.title('柱状图')
plt.xlabel('类别')
plt.ylabel('值')
plt.show()

# 散点图
x = np.random.rand(50)
y = np.random.rand(50)
colors = np.random.rand(50)
sizes = 1000 * np.random.rand(50)

plt.scatter(x, y, c=colors, s=sizes, alpha=0.5, cmap='viridis')
plt.colorbar()
plt.title('散点图')
plt.show()

# 使用pandas绘图
df = pd.DataFrame(np.random.rand(10, 4), columns=['a', 'b', 'c', 'd'])
df.plot.bar()
plt.title('DataFrame柱状图')
plt.show()

df.plot.line()
plt.title('DataFrame折线图')
plt.show()
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd

# 折线图
x = np.linspace(0, 10, 100)
y = np.sin(x)

plt.plot(x, y)
plt.title('正弦函数')
plt.xlabel('x')
plt.ylabel('sin(x)')
plt.grid(True)
plt.show()

# 柱状图
data = {'A': 10, 'B': 15, 'C': 7, 'D': 12}
plt.bar(data.keys(), data.values())
plt.title('柱状图')
plt.xlabel('类别')
plt.ylabel('值')
plt.show()

# 散点图
x = np.random.rand(50)
y = np.random.rand(50)
colors = np.random.rand(50)
sizes = 1000 * np.random.rand(50)

plt.scatter(x, y, c=colors, s=sizes, alpha=0.5, cmap='viridis')
plt.colorbar()
plt.title('散点图')
plt.show()

# 使用pandas绘图
df = pd.DataFrame(np.random.rand(10, 4), columns=['a', 'b', 'c', 'd'])
df.plot.bar()
plt.title('DataFrame柱状图')
plt.show()

df.plot.line()
plt.title('DataFrame折线图')
plt.show()

Seaborn ​

Seaborn是基于Matplotlib的高级数据可视化库,提供了更美观的默认样式和更高级的绘图功能。

bash
pip install seaborn
pip install seaborn
python
import seaborn as sns
import numpy as np
import pandas as pd

# 加载示例数据
tips = sns.load_dataset('tips')

# 箱线图
sns.boxplot(x='day', y='total_bill', data=tips)
sns.despine(offset=10, trim=True)
plt.title('箱线图')
plt.show()

# 热力图
corr = np.corrcoef(np.random.randn(10, 200))
sns.heatmap(corr, vmax=.3, center=0, square=True, linewidths=.5, cbar_kws={"shrink": .5})
plt.title('热力图')
plt.show()

# 成对关系图
sns.pairplot(tips, hue="size")
plt.title('成对关系图')
plt.show()
import seaborn as sns
import numpy as np
import pandas as pd

# 加载示例数据
tips = sns.load_dataset('tips')

# 箱线图
sns.boxplot(x='day', y='total_bill', data=tips)
sns.despine(offset=10, trim=True)
plt.title('箱线图')
plt.show()

# 热力图
corr = np.corrcoef(np.random.randn(10, 200))
sns.heatmap(corr, vmax=.3, center=0, square=True, linewidths=.5, cbar_kws={"shrink": .5})
plt.title('热力图')
plt.show()

# 成对关系图
sns.pairplot(tips, hue="size")
plt.title('成对关系图')
plt.show()

Polars(高性能 DataFrame) ​

Polars 是一个 Rust 编写的 DataFrame 库,性能远超 Pandas(3-20 倍),2024-2025 年增长最快的 Python 数据处理库。

安装 Polars ​

bash
pip install polars
pip install polars

核心概念 ​

  • DataFrame / Series:与 Pandas 类似的数据结构
  • Expr:表达式系统,Polars 的核心抽象,所有操作都是表达式
  • Lazy / Eager API:惰性查询优化 vs 立即执行
  • 无索引:Polars 没有 Pandas 的行索引概念,更简洁

基本使用 ​

python
import polars as pl

# 创建 DataFrame
df = pl.DataFrame({
    "name": ["张三", "李四", "王五", "赵六"],
    "age": [20, 25, 30, 35],
    "city": ["北京", "上海", "广州", "深圳"],
    "salary": [10000, 15000, 20000, 25000],
})
print(df)
# shape: (4, 4)
# ┌──────┬─────┬──────┬────────┐
# │ name ┆ age ┆ city ┆ salary │
# │ ---  ┆ --- ┆ ---  ┆ ---    │
# │ str  ┆ i64 ┆ str  ┆ i64    │
# ╞══════╪═════╪══════╪════════╡
# │ 张三 ┆ 20  ┆ 北京 ┆ 10000  │
# │ 李四 ┆ 25  ┆ 上海 ┆ 15000  │
# │ 王五 ┆ 30  ┆ 广州 ┆ 20000  │
# │ 赵六 ┆ 35  ┆ 深圳 ┆ 25000  │
# └──────┴─────┴──────┴────────┘

# 读取 CSV
df = pl.read_csv("data.csv")

# 读取 JSON
df = pl.read_json("data.json")

# 读取 Parquet(Polars 强项)
df = pl.read_parquet("data.parquet")
import polars as pl

# 创建 DataFrame
df = pl.DataFrame({
    "name": ["张三", "李四", "王五", "赵六"],
    "age": [20, 25, 30, 35],
    "city": ["北京", "上海", "广州", "深圳"],
    "salary": [10000, 15000, 20000, 25000],
})
print(df)
# shape: (4, 4)
# ┌──────┬─────┬──────┬────────┐
# │ name ┆ age ┆ city ┆ salary │
# │ ---  ┆ --- ┆ ---  ┆ ---    │
# │ str  ┆ i64 ┆ str  ┆ i64    │
# ╞══════╪═════╪══════╪════════╡
# │ 张三 ┆ 20  ┆ 北京 ┆ 10000  │
# │ 李四 ┆ 25  ┆ 上海 ┆ 15000  │
# │ 王五 ┆ 30  ┆ 广州 ┆ 20000  │
# │ 赵六 ┆ 35  ┆ 深圳 ┆ 25000  │
# └──────┴─────┴──────┴────────┘

# 读取 CSV
df = pl.read_csv("data.csv")

# 读取 JSON
df = pl.read_json("data.json")

# 读取 Parquet(Polars 强项)
df = pl.read_parquet("data.parquet")

数据操作(Polars 风格) ​

python
# 选择列
df.select("name", "salary")

# 使用表达式
df.select([
    pl.col("name"),
    pl.col("salary") / 10000,  # 列运算
])

# 过滤
df.filter(pl.col("age") > 25)
df.filter(pl.col("city").is_in(["北京", "上海"]))

# 添加/修改列
df.with_columns(
    (pl.col("salary") * 1.1).alias("salary_after_raise")
)

# 分组聚合
df.group_by("city").agg([
    pl.col("salary").mean().alias("avg_salary"),
    pl.col("age").max().alias("max_age"),
    pl.len().alias("count"),  # 行数
])
# 选择列
df.select("name", "salary")

# 使用表达式
df.select([
    pl.col("name"),
    pl.col("salary") / 10000,  # 列运算
])

# 过滤
df.filter(pl.col("age") > 25)
df.filter(pl.col("city").is_in(["北京", "上海"]))

# 添加/修改列
df.with_columns(
    (pl.col("salary") * 1.1).alias("salary_after_raise")
)

# 分组聚合
df.group_by("city").agg([
    pl.col("salary").mean().alias("avg_salary"),
    pl.col("age").max().alias("max_age"),
    pl.len().alias("count"),  # 行数
])

惰性计算(LazyFrame) ​

python
# 使用 lazy() 启动惰性查询
q = (
    df.lazy()
    .filter(pl.col("age") > 25)
    .group_by("city")
    .agg(pl.col("salary").mean())
    .sort("salary", descending=True)
)

# 查看执行计划
print(q.explain())

# 执行查询
result = q.collect()

# 自动优化:谓词下推、投影下推等
# 使用 lazy() 启动惰性查询
q = (
    df.lazy()
    .filter(pl.col("age") > 25)
    .group_by("city")
    .agg(pl.col("salary").mean())
    .sort("salary", descending=True)
)

# 查看执行计划
print(q.explain())

# 执行查询
result = q.collect()

# 自动优化:谓词下推、投影下推等

Polars vs Pandas 对比 ​

特性PandasPolars
底层语言Python / CRust
索引有行索引无索引
表达式系统链式调用Expr 表达式
惰性计算不支持原生原生支持
多线程有限(单线程为主)全量多线程
内存效率较高内存占用高(零拷贝)
API 风格命令式表达式驱动
迁移学习成本-较低(概念类似)

从 Pandas 迁移 ​

python
import pandas as pd
import polars as pl

# pandas_df → polars_df
polars_df = pl.from_pandas(pandas_df)

# polars_df → pandas_df
pandas_df = polars_df.to_pandas()

# 常用操作对照
pl.col("col").mean()        # df["col"].mean()
pl.col("col").alias("新名")  # df["col"].rename("新名")
df.filter(cond)              # df[cond]
df.with_columns(...)         # df.assign(...)
df.group_by("col").agg(...)  # df.groupby("col").agg(...)
import pandas as pd
import polars as pl

# pandas_df → polars_df
polars_df = pl.from_pandas(pandas_df)

# polars_df → pandas_df
pandas_df = polars_df.to_pandas()

# 常用操作对照
pl.col("col").mean()        # df["col"].mean()
pl.col("col").alias("新名")  # df["col"].rename("新名")
df.filter(cond)              # df[cond]
df.with_columns(...)         # df.assign(...)
df.group_by("col").agg(...)  # df.groupby("col").agg(...)

适用场景 ​

  • 大数据集(百万到亿级行):Polars 优势明显
  • ETL 管道:惰性计算 + 自动优化
  • Parquet 文件处理:原生高性能支持
  • 需要高性能的生产环境

数据处理的最佳实践 ​

  1. 使用NumPy进行数值计算:NumPy的数组操作比Python列表快得多,适合大规模数值计算
  2. 使用Pandas处理结构化数据:Pandas提供了强大的数据结构和数据处理功能,适合处理结构化数据
  3. 优先使用向量化操作:避免使用Python循环,尽可能使用NumPy和Pandas的向量化操作
  4. 注意内存使用:对于大型数据集,使用适当的数据类型(如float32代替float64),并及时释放不再使用的变量
  5. 数据清洗和预处理:在进行数据分析前,确保数据质量,处理缺失值、重复值和异常值
  6. 使用适当的可视化方式:根据数据类型和分析目的选择合适的可视化方式
  7. 文档和注释:为代码添加文档和注释,便于理解和维护
  8. 测试代码:编写测试用例,确保代码的正确性

总结 ​

NumPy和Pandas是Python中最重要的数据处理和分析库,它们提供了强大的数据结构和数据处理功能。NumPy是科学计算的基础,适合处理数值数据和数组操作;Pandas则适合处理结构化数据,提供了数据读取、清洗、预处理、聚合和可视化等功能。

通过学习和掌握这些库,我们可以更高效地处理和分析数据,为数据科学和机器学习项目打下坚实的基础。