计算机系统应用教程网站

网站首页 > 技术文章 正文

数据预处理-分类变量数据编码 分类变量数据分析

btikc 2024-10-12 13:23:16 技术文章 15 ℃ 0 评论

在数据分析中,分类变量(categorical variables)需要经过适当的处理,以便能够被大多数机器学习算法所接受。以下是几种常用的处理方法,每种方法都有其独特的优点和缺点↓

【数据介绍】

创建包含数值变量(访客数,单价,支付人数)和分类变量(性别,产品颜色)的数据集↓

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LinearRegression
from sklearn.metrics import r2_score, mean_squared_error
from statsmodels.api import OLS, add_constant
import category_encoders as ce
n_samples = 200
visitors = np.random.randint(50, 1000, size=n_samples)
price = np.random.uniform(5, 100, size=n_samples)
paying_customers = np.random.randint(1, 50, size=n_samples)
gender = np.random.choice(['男', '女'], size=n_samples)
product_color = np.random.choice(['红', '蓝', '白'], size=n_samples)
sales = (0.5 * visitors + 2 * price + 3 * paying_customers) * (np.where(gender == '男', 2, 1)) + np.random.normal(0, 50, n_samples)
df = pd.DataFrame({
    '访客数': visitors,
    '单价': price,
    '支付人数': paying_customers,
    '性别': gender,
    '产品颜色': product_color,
    '销售额': sales
})


【标签编码(Label Encoding)】

标签编码是将每个类别映射到一个唯一的整数。例如,假设有一个包含三种类别的变量 ['红', '蓝', '绿'],标签编码可能将其转换为 [0, 1, 2]。

优点:

  • 简单易用,特别适用于有序的分类变量;
  • 不会增加维度,数据集的大小保持不变。

缺点:

  • 对于无序的分类变量(例如,颜色),标签编码可能引入错误的顺序关系,导致模型误解这些数据的本质。
encoder = ce.OrdinalEncoder(cols=['性别', '产品颜色'])
X = df.drop(columns=['销售额'])
y = df['销售额']
X_encoded = encoder.fit_transform(X, y)
X_train, X_test, y_train, y_test = train_test_split(X_encoded, y, test_size=0.2, random_state=0)
model = LinearRegression()
model.fit(X_train, y_train)
y_pred = model.predict(X_test)
r2 = r2_score(y_test, y_pred)
mse = mean_squared_error(y_test, y_pred)
X_const = add_constant(X_train)
ols_model = OLS(y_train, X_const).fit()

数据结果指标这里就不详细介绍了,这里模型的结果还是很优秀的,具体的可以参考之前几篇回归分析的内容,里面有详细的介绍。

plt.figure(figsize=(10, 6))
plt.scatter(y_test, y_pred, alpha=0.6, color='b')
plt.plot([y_test.min(), y_test.max()], [y_test.min(), y_test.max()], 'r--', lw=2)
plt.xlabel('实际销售额')
plt.ylabel('预测销售额')
plt.title(f'标签编码\nR2: {r2:.4f}, MSE: {mse:.4f}')
plt.show()

从图形上看,整体效果也还不错。接下来就使用模型进行新数据的预测↓

new_visitors = np.random.randint(50, 1000, size=20)
new_price = np.random.uniform(5, 100, size=20)
new_paying_customers = np.random.randint(1, 50, size=20)
new_gender = np.random.choice(['男', '女'], size=20)
new_product_color = np.random.choice(['红', '蓝', '白'], size=20)
new_data = pd.DataFrame({
    '访客数': new_visitors,
    '单价': new_price,
    '支付人数': new_paying_customers,
    '性别': new_gender,
    '产品颜色': new_product_color
})
# 对新数据进行标签编码
new_data_encoded = encoder.transform(new_data)
# 对新数据进行预测
new_data['预测销售额'] = model.predict(new_data_encoded)
plt.figure(figsize=(10, 6))
plt.scatter(y_test, y_pred, alpha=0.6, color='b')
plt.plot([y_test.min(), y_test.max()], [y_test.min(), y_test.max()], 'r--', lw=2)
plt.xlabel('实际销售额')
plt.ylabel('预测销售额')
plt.title(f'独热编码\nR2: {r2:.4f}, MSE: {mse:.4f}')
plt.show()

【独热编码(One-Hot Encoding)】

独热编码为每个类别创建一个新的二进制变量。例如,对于变量 ['红', '蓝', '绿'],会生成三个新的变量:红, 蓝, 绿,并用 0 或 1 表示某个类别是否存在。

优点:

  • 消除了类别之间的顺序关系,非常适合无序的分类变量;
  • 广泛支持,大多数机器学习算法都能处理这种编码方式。

缺点:

  • 当类别数较多时,会显著增加数据集的维度,可能导致计算资源和时间的消耗增加。
# 独热编码
encoder = ce.OneHotEncoder(cols=['性别', '产品颜色'], use_cat_names=True)
X_encoded = encoder.fit_transform(X, y)
X_train, X_test, y_train, y_test = train_test_split(X_encoded, y, test_size=0.2, random_state=0)
model = LinearRegression()
model.fit(X_train, y_train)
y_pred = model.predict(X_test)
r2 = r2_score(y_test, y_pred)
mse = mean_squared_error(y_test, y_pred)
X_const = add_constant(X_train)
ols_model = OLS(y_train, X_const).fit()
# 对新数据进行独热编码
new_data_encoded = encoder.transform(new_data)
# 对新数据进行预测
new_data['预测销售额'] = model.predict(new_data_encoded)

【二进制编码(Binary Encoding)】

二进制编码首先将类别转换为整数,然后再将整数转换为二进制表示。例如,对于变量 ['红', '蓝', '绿'],假设它们对应的整数编码为 [1, 2, 3],它们的二进制编码则为 [01, 10, 11]。

优点:

  • 比独热编码更节省空间,减少维度;
  • 结合了标签编码和独热编码的优点,适用于大类别集合。

缺点:

  • 仍然可能引入一定的顺序关系,虽然较弱;
  • 复杂度较高,理解和实现上比前两种方法稍难。
encoder = ce.BinaryEncoder(cols=['性别', '产品颜色'])
X_encoded = encoder.fit_transform(X, y)
X_train, X_test, y_train, y_test = train_test_split(X_encoded, y, test_size=0.2, random_state=0)
model = LinearRegression()
model.fit(X_train, y_train)
y_pred = model.predict(X_test)
r2 = r2_score(y_test, y_pred)
mse = mean_squared_error(y_test, y_pred)
X_const = add_constant(X_train)
ols_model = OLS(y_train, X_const).fit()
plt.figure(figsize=(10, 6))
plt.scatter(y_test, y_pred, alpha=0.6, color='b')
plt.plot([y_test.min(), y_test.max()], [y_test.min(), y_test.max()], 'r--', lw=2)
plt.xlabel('实际销售额')
plt.ylabel('预测销售额')
plt.title(f'二进制编码\nR2: {r2:.4f}, MSE: {mse:.4f}')
plt.show()
new_data_encoded = encoder.transform(new_data)
# 对新数据进行预测
new_data['预测销售额'] = model.predict(new_data_encoded)

【频率编码(Frequency Encoding)】

频率编码是用类别出现的频率或概率来替代类别标签。例如,如果红出现了 100次,蓝出现了50次,绿出现了30次,则它们的编码可以是[100, 50, 30]。

优点:

  • 保持数据集的大小不变;
  • 捕捉类别的重要性(出现频率)。

缺点:

  • 频率可能随数据集变化而变化,具有一定的局限性;
  • 如果某类别频率过高或过低,可能会引入偏差。
encoder = ce.CountEncoder(cols=['性别', '产品颜色'])
X_encoded = encoder.fit_transform(X, y)
X_train, X_test, y_train, y_test = train_test_split(X_encoded, y, test_size=0.2, random_state=0)
model = LinearRegression()
model.fit(X_train, y_train)
y_pred = model.predict(X_test)
r2 = r2_score(y_test, y_pred)
mse = mean_squared_error(y_test, y_pred)
X_const = add_constant(X_train)
ols_model = OLS(y_train, X_const).fit()
plt.figure(figsize=(10, 6))
plt.scatter(y_test, y_pred, alpha=0.6, color='b')
plt.plot([y_test.min(), y_test.max()], [y_test.min(), y_test.max()], 'r--', lw=2)
plt.xlabel('实际销售额')
plt.ylabel('预测销售额')
plt.title(f'频率编码\nR2: {r2:.4f}, MSE: {mse:.4f}')
plt.show()
new_data_encoded = encoder.transform(new_data)
# 对新数据进行预测
new_data['预测销售额'] = model.predict(new_data_encoded)

【目标编码(Target Encoding)】

目标编码用每个类别对应的目标变量(通常是标签)的平均值来替代类别标签。例如,对于一个分类任务,如果 红 类别的目标变量平均值为 0.8,蓝 类别为 0.5,绿 类别为 0.3,则它们的编码为 [0.8, 0.5, 0.3]。

优点:

  • 保留了类别与目标变量之间的关系,有助于提高模型性能;
  • 维度不增加,适用于大类别集合。

缺点:

  • 容易引入目标泄露(target leakage),需要谨慎处理(例如,通过交叉验证);
  • 需要更复杂的处理和理解。
encoder = ce.TargetEncoder(cols=['性别', '产品颜色'])
X_encoded = encoder.fit_transform(X, y)
X_train, X_test, y_train, y_test = train_test_split(X_encoded, y, test_size=0.2, random_state=0)
model = LinearRegression()
model.fit(X_train, y_train)
y_pred = model.predict(X_test)
r2 = r2_score(y_test, y_pred)
mse = mean_squared_error(y_test, y_pred)
X_const = add_constant(X_train)
ols_model = OLS(y_train, X_const).fit()
plt.figure(figsize=(10, 6))
plt.scatter(y_test, y_pred, alpha=0.6, color='b')
plt.plot([y_test.min(), y_test.max()], [y_test.min(), y_test.max()], 'r--', lw=2)
plt.xlabel('实际销售额')
plt.ylabel('预测销售额')
plt.title(f'目标编码\nR2: {r2:.4f}, MSE: {mse:.4f}')
plt.show()
new_data_encoded = encoder.transform(new_data)
# 对新数据进行预测
new_data['预测销售额'] = model.predict(new_data_encoded)

【嵌入编码(Embedding Encoding)】

嵌入编码通常用于深度学习中,通过训练一个嵌入层将类别映射到一个低维的连续向量。例如,一个包含100个类别的变量可以被映射到一个10维的嵌入向量。

优点:

  • 可以捕捉类别之间的复杂关系,适用于大规模类别;
  • 维度降低,通常对大类别集合很有效。

缺点:

  • 需要更多计算资源和训练时间;
  • 需要特定的框架和模型(如神经网络)来实现。
import tensorflow as tf
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Embedding, Flatten
from sklearn.preprocessing import LabelEncoder
# 标签编码
le_gender = LabelEncoder()
le_color = LabelEncoder()
df['性别'] = le_gender.fit_transform(df['性别'])
df['产品颜色'] = le_color.fit_transform(df['产品颜色'])
# 生成特征和目标变量
X = df[['访客数', '单价', '支付人数', '性别', '产品颜色']]
y = df['销售额']
# 训练和测试集划分
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=0)
# 嵌入编码
gender_input = tf.keras.Input(shape=(1,))
gender_embed = Embedding(input_dim=len(le_gender.classes_), output_dim=2)(gender_input)
gender_flatten = Flatten()(gender_embed)
color_input = tf.keras.Input(shape=(1,))
color_embed = Embedding(input_dim=len(le_color.classes_), output_dim=3)(color_input)
color_flatten = Flatten()(color_embed)
other_input = tf.keras.Input(shape=(3,))
concat = tf.keras.layers.Concatenate()([other_input, gender_flatten, color_flatten])
dense1 = Dense(128, activation='relu')(concat)
dense2 = Dense(64, activation='relu')(dense1)
output = Dense(1)(dense2)
model = tf.keras.Model(inputs=[other_input, gender_input, color_input], outputs=output)
model.compile(optimizer='adam', loss='mse')
# 训练模型
model.fit([X_train[['访客数', '单价', '支付人数']], X_train['性别'], X_train['产品颜色']], y_train, epochs=100, batch_size=32, verbose=0)
# 预测
y_pred = model.predict([X_test[['访客数', '单价', '支付人数']], X_test['性别'], X_test['产品颜色']])
y_pred = y_pred.flatten()


r2 = r2_score(y_test, y_pred)
mse = mean_squared_error(y_test, y_pred)
嵌入编码:
R2: 0.9138
MSE: 9804.7672
plt.figure(figsize=(10, 6))
plt.scatter(y_test, y_pred, alpha=0.6, color='b')
plt.plot([y_test.min(), y_test.max()], [y_test.min(), y_test.max()], 'r--', lw=2)
plt.xlabel('实际销售额')
plt.ylabel('预测销售额')
plt.title(f'嵌入编码\nR2: {r2:.4f}, MSE: {mse:.4f}')
plt.show()
new_data = pd.DataFrame({
    '访客数': new_visitors,
    '单价': new_price,
    '支付人数': new_paying_customers,
    '性别': new_gender,
    '产品颜色': new_product_color
})


# 对新数据进行标签编码
new_data['性别'] = le_gender.transform(new_data['性别'])
new_data['产品颜色'] = le_color.transform(new_data['产品颜色'])


# 对新数据进行预测
new_data['预测销售额'] = model.predict([new_data[['访客数', '单价', '支付人数']], new_data['性别'], new_data['产品颜色']]).flatten()

链接是我使用PowerBI整合的历史文章,按类型分类,可以根据需求查询:Microsoft Power BI↓

https://app.powerbi.com/view?r=eyJrIjoiNjI2NWQ3NjktYjU0ZC00ZWZhLTgzMDgtMGI4ZTk1ZDlkODM3IiwidCI6IjI3NDQ3MWQ0LTM4ZDQtNDVlZS1hMmJkLWU1NTVhOTBkYzM4NiJ9

End

Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表