day 16 5.10

知识点:

  1. numpy数组的创建:简单创建、随机创建、遍历、运算
  2. numpy数组的索引:一维、二维、三维
  3. SHAP值的深入理解

## NumPy 数组基础笔记

### 1. 理解数组的维度 (Dimensions)

NumPy 数组的**维度 (Dimension)** 或称为 **轴 (Axis)** 的概念,与我们日常理解的维度非常相似。

*   **直观判断:** 数组的维度层数通常可以通过打印输出时**中括号 `[]` 的嵌套层数**来初步确定:
    *   一层 `[]`: **一维 (1D)** 数组。
    *   两层 `[]`: **二维 (2D)** 数组。
    *   三层 `[]`: **三维 (3D)** 数组,依此类推。

### 2. NumPy 数组与深度学习 Tensor 的关系

在后续进行频繁的数学运算时,尤其是在深度学习领域,对 NumPy 数组的理解非常有帮助,因为 PyTorch 或 TensorFlow 中的 **Tensor** 张量本质上可以视为**支持 GPU 加速**和**自动微分**的 NumPy 数组。掌握 NumPy 的基本操作,能极大地降低学习 Tensor 的门槛。关于 NumPy 更深入的性质,我们留待后续探讨。

### 3. 一维数组 (1D Array)

一维数组在结构上与 Python 中的列表(List)非常相似。它们的主要区别在于:

*   **打印输出格式:** 当使用 `print()` 函数输出时:
    *   NumPy 一维数组的元素之间默认使用**空格**分隔。
    *   Python 列表的元素之间使用**逗号**分隔。

    *   **示例 (一维数组输出):**
        ```
        [7 5 3 9]
        ```

### 4. 二维数组 (2D Array)

二维数组可以被看作是“数组的数组”或者一个矩阵。其结构由两个主要维度决定:

*   **行数:** 代表整个二维数组中**包含多少个一维数组**。
*   **列数:** 代表**每个一维数组(也就是每一行)中包含多少个元素**。

值得注意的是,二维数组**不一定**是正方形(即行数等于列数),它可以是任意的 `n * m` 形状,其中 `n` 是行数,`m` 是列数。

### 5. 数组的创建

NumPy 的 `array()` 函数非常灵活,可以接受各种“序列型”对象作为输入参数来创建数组。这意味着你可以将 Python 的**列表 (List)**、**元组 (Tuple)**,甚至其他的 NumPy **数组**等数据结构直接传递给 `np.array()` 来创建新的 NumPy 数组。

简单数组的建立
 

# 简单创建
arr1 = np.array([1, 2, 3, 4, 5])  # 一维数组
arr2 = np.array([[1, 2, 3], [4, 5, 6]])  # 二维数组
arr3 = np.array([[[1, 2], [3, 4]], [[5, 6], [7, 8]]])  # 三维数组
 
print("一维数组:")
print(arr1)
print("\n二维数组:")
print(arr2)
print("\n三维数组:")
print(arr3)
 
random_arr1 = np.random.randint(0, 10, 5)  # 生成[0,10)区间的一维随机数组
random_arr2 = np.random.randint(-5, 5, (3, 3))  # 生成[-5,5)区间的3x3二维随机数组
random_arr3 = np.random.randint(10, 20, (2, 2, 2))  # 生成[10,20)区间的2x2x2三维随机数组
 
print("\n随机一维数组:")
print(random_arr1)
print("\n随机二维数组:")
print(random_arr2)
print("\n随机三维数组:")
print(random_arr3)
print("一维数组遍历:")
for num in arr1:
    print(num)
 
print("\n二维数组遍历:")
for row in arr2:
    for num in row:
        print(num)
 
print("\n三维数组遍历:")
for matrix in arr3:
    for row in matrix:
        for num in row:
            print(num)
# 矩阵运算
mat_a = np.array([[1, 2], [3, 4]])  # 2x2矩阵
mat_b = np.array([[5, 6], [7, 8]])  # 2x2矩阵
 
# 矩阵乘法
mat_mul = np.matmul(mat_a, mat_b)  # 或者使用 @ 运算符: mat_a @ mat_b
print("\n矩阵乘法结果:")
print(mat_mul)
 
# 矩阵点乘 (逐元素相乘)
mat_dot = np.multiply(mat_a, mat_b)  # 或者使用 * 运算符: mat_a * mat_b
print("\n矩阵点乘结果:")
print(mat_dot)
 
# 矩阵转置
mat_transpose = np.transpose(mat_a)  # 或者使用 .T 属性: mat_a.T
print("\n矩阵转置结果:")
print(mat_transpose)
 
# 矩阵求逆 (必须是方阵且可逆)
mat_inv = np.linalg.inv(mat_a)
print("\n矩阵逆矩阵结果:")
print(mat_inv)
 
# 矩阵行列式 (必须是方阵)
mat_det = np.linalg.det(mat_a)
print("\n矩阵行列式结果:")
print(mat_det)
 
# 基本运算
sum_arr = arr1 + 10  # 每个元素加10
print("\n数组每个元素加10:")
print(sum_arr)
 
mul_arr = arr2 * 2  # 每个元素乘2
print("\n数组每个元素乘2:")
print(mul_arr)
 
dot_product = np.dot(arr1, np.array([2, 2, 2, 2, 2]))  # 点积
print("\n数组点积结果:")
print(dot_product)
numpy数组的索引
# 一维
print("\n一维数组索引:")
print(arr1[0])  # 第一个元素
print(arr1[-1])  # 最后一个元素
 
# 二维
print("\n二维数组索引:")
print(arr2[0, 1])  # 第一行第二列
print(arr2[:, 1])  # 所有行的第二列
 
# 三维
print("\n三维数组索引:")
print(arr3[0, 1, 0])  # 第一个矩阵的第二行第一列
print(arr3[1, :, 0])  # 第二个矩阵的所有行的第一列

import numpy as np
a = np.array([2,4,6,8,10,12]) # 创建一个一维数组
b = np.array([[2,4,6],[8,10,12]]) # 创建一个二维数组
print(a)
print(b)

SHAP值的深入理解

# 先运行之前预处理好的代码
import pandas as pd
import pandas as pd    #用于数据处理和分析,可处理表格数据。
import numpy as np     #用于数值计算,提供了高效的数组操作。
import matplotlib.pyplot as plt    #用于绘制各种类型的图表
import seaborn as sns   #基于matplotlib的高级绘图库,能绘制更美观的统计图形。
import warnings
warnings.filterwarnings("ignore")
 
 # 设置中文字体(解决中文显示问题)
plt.rcParams['font.sans-serif'] = ['SimHei']  # Windows系统常用黑体字体
plt.rcParams['axes.unicode_minus'] = False    # 正常显示负号
data = pd.read_csv('data.csv')    #读取数据


# 先筛选字符串变量 
discrete_features = data.select_dtypes(include=['object']).columns.tolist()
# Home Ownership 标签编码
home_ownership_mapping = {
    'Own Home': 1,
    'Rent': 2,
    'Have Mortgage': 3,
    'Home Mortgage': 4
}
data['Home Ownership'] = data['Home Ownership'].map(home_ownership_mapping)

# Years in current job 标签编码
years_in_job_mapping = {
    '< 1 year': 1,
    '1 year': 2,
    '2 years': 3,
    '3 years': 4,
    '4 years': 5,
    '5 years': 6,
    '6 years': 7,
    '7 years': 8,
    '8 years': 9,
    '9 years': 10,
    '10+ years': 11
}
data['Years in current job'] = data['Years in current job'].map(years_in_job_mapping)

# Purpose 独热编码,记得需要将bool类型转换为数值
data = pd.get_dummies(data, columns=['Purpose'])
data2 = pd.read_csv("data.csv") # 重新读取数据,用来做列名对比
list_final = [] # 新建一个空列表,用于存放独热编码后新增的特征名
for i in data.columns:
    if i not in data2.columns:
       list_final.append(i) # 这里打印出来的就是独热编码后的特征名
for i in list_final:
    data[i] = data[i].astype(int) # 这里的i就是独热编码后的特征名



# Term 0 - 1 映射
term_mapping = {
    'Short Term': 0,
    'Long Term': 1
}
data['Term'] = data['Term'].map(term_mapping)
data.rename(columns={'Term': 'Long Term'}, inplace=True) # 重命名列
continuous_features = data.select_dtypes(include=['int64', 'float64']).columns.tolist()  #把筛选出来的列名转换成列表
 
 # 连续特征用中位数补全
for feature in continuous_features:     
    mode_value = data[feature].mode()[0]            #获取该列的众数。
    data[feature].fillna(mode_value, inplace=True)          #用众数填充该列的缺失值,inplace=True表示直接在原数据上修改。

# 最开始也说了 很多调参函数自带交叉验证,甚至是必选的参数,你如果想要不交叉反而实现起来会麻烦很多
# 所以这里我们还是只划分一次数据集
from sklearn.model_selection import train_test_split
X = data.drop(['Credit Default'], axis=1)  # 特征,axis=1表示按列删除
y = data['Credit Default'] # 标签
# 按照8:2划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)  # 80%训练集,20%测试集

from sklearn.ensemble import RandomForestClassifier #随机森林分类器

from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score # 用于评估分类器性能的指标
from sklearn.metrics import classification_report, confusion_matrix #用于生成分类报告和混淆矩阵
import warnings #用于忽略警告信息
warnings.filterwarnings("ignore") # 忽略所有警告信息
# --- 1. 默认参数的随机森林 ---
# 评估基准模型,这里确实不需要验证集
print("--- 1. 默认参数随机森林 (训练集 -> 测试集) ---")
import time # 这里介绍一个新的库,time库,主要用于时间相关的操作,因为调参需要很长时间,记录下会帮助后人知道大概的时长
start_time = time.time() # 记录开始时间
rf_model = RandomForestClassifier(random_state=42)
rf_model.fit(X_train, y_train) # 在训练集上训练
rf_pred = rf_model.predict(X_test) # 在测试集上预测
end_time = time.time() # 记录结束时间

print(f"训练与预测耗时: {end_time - start_time:.4f} 秒")
print("\n默认随机森林 在测试集上的分类报告:")
print(classification_report(y_test, rf_pred))
print("默认随机森林 在测试集上的混淆矩阵:")
print(confusion_matrix(y_test, rf_pred))

此时可以理解为什么shap.summary_plot中第一个参数是所有样本对预测类别的shap值了。

传入的 SHAP 值 (shap_values[:, :, 0]) 和特征数据 (X_test) 在维度上需要高度一致和对应。

- shap_values[:, :, 0] 的每一行代表的是 一个特定样本每个特征对于预测类别的贡献值(SHAP 值)。缺乏特征本身的值
- X_test 的每一行代表的也是同一个特定样本的特征值。

这二者组合后,就可以组合(特征数,特征值,shap值)构成shap图的基本元素

上面这些就是我对于shap图的理解,去年很多同学在这里经常和我说是借助ai没办法对这里debug,实际上是因为没有理解shap

你可能感兴趣的:(python)