河北理工大学データマイニング実験1 データ前処理
1. 実験目的
(1) 完全なデータ キューブの構築とオンライン分析および処理アルゴリズムに精通していること。
(2) 処理対象のデータを参照し、各次元属性で発生する可能性のあるノイズ、欠損値、不一致などを発見し、既存の問題に対処するためのデータ クリーニング、データ変換、およびデータ統合のための具体的なアルゴリズムを策定します。
(3) データクリーニング、データ変換、データ統合などの機能を実装するプログラムを作成します。
(4) プログラム全体をデバッグして、クリーンで一貫性のある統合されたデータを取得し、全体的な最適化に適したパラメーターを選択します。
(5) 実験レポートを書きます。
2. 実験原理
1. データの前処理
現実世界のデータベースは、ノイズの多いデータ、欠落したデータ、不整合なデータの影響を非常に受けやすいため、データの品質を向上させ、それによってマイニング結果の品質を向上させるために、多数のデータ前処理テクノロジが生み出されてきました。データの前処理には、データ クリーニング、データ統合、データ変換、データ削減など、さまざまな方法があります。これらのデータ処理技術はデータ マイニングの前に使用されるため、データ マイニング モデルの品質が大幅に向上し、実際のマイニングに必要な時間が短縮されます。
2. データクリーニング
データ クリーニング ルーチンは、欠損値の埋め込み、ノイズの多いデータの平滑化、外れ値の特定と削除、不一致の解決によってデータを「クリーニング」します。
3. データ統合
データ統合では、複数のソースからのデータを、データ ウェアハウスやデータ キューブなどの一貫したデータ ストアに結合します。
4. データ変換
スムーズな集計、データの一般化、標準化などにより、データをデータマイニングに適した形式に変換します。
5. データ削減
データ削減を使用すると、データ セットが圧縮されて表示され、はるかに小さくなりますが、同じ (またはほぼ同じ) 分析結果が得られます。一般的に使用されるデータ削減戦略には、データ集約、次元削減、データ圧縮、数値削減などが含まれます。
3. 実験内容と手順
1. 実験内容
1) Python プログラミング ツールを使用して、データ クリーニング、データ変換、データ統合などの機能を実装するプログラムを作成し、実験レポートで使用される主な前処理プロセスとメソッドを作成します。
2) クリーンで一貫性のある統合されたデータを生成します。。
2. 実験手順
1) データを注意深く調査およびレビューして、分析に含める必要がある属性またはディメンションを特定し、データ内のエラー、異常な値、および特定の取引記録の不一致を発見します。
2) データ クリーニングを実行して、欠損値、ノイズのあるデータ、一貫性のないデータに対処します。
例:
1. 日付の欠損値は、統一されたシリアル番号に基づいて判断できます。
2. 購入数量をマイナスにすることはできません。
3) データ統合、データ変換、およびデータ削減を実行して、複数のデータ ソースからのデータを統合し、結果として得られるデータのデータの冗長性や不整合を削減または回避します。データをマイニングに適した形式に変換します。
例:
1. データクリーニング後、購入数量、販売価格、および合計金額が相互に関連する項目であることがわかり、合計金額を削除できます。
2. 3 つのスケジュールの日付形式が異なるため、同じ日付形式に統一する必要があります。
3. ドア番号は POS マシン番号と同じで、1 つは削除できます。
4. 追加: 同じ買い物かご内の製品のシリアル番号は、順番に増加する必要があります。
3. プログラムブロック図
4. 実験サンプル
3 つのデータ セット: 1019.txt、1020.txt、および 1021.txt
5. 実験コード
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Copyright (C) 2021 #
# @Time : 2022/5/30 21:27
# @Author : Yang Haoyuan
# @Email : [email protected]
# @File : Exp1.py
# @Software: PyCharm
import argparse
import numpy as np
from sklearn.cluster import KMeans
import matplotlib.pyplot as plt
import pandas as pd
from sklearn.preprocessing import StandardScaler
parser = argparse.ArgumentParser(description='Exp1')
parser.add_argument('-files', nargs='+')
parser.set_defaults(augment=True)
args = parser.parse_args()
print(args)
# 从txt文件中读取数据,并做初步处理,数据以numpy array的格式返回
def load_data():
files = args.files
data = []
sales = []
count = 0
for filename in files:
with open(filename, 'r') as file_to_read:
while True:
lines = file_to_read.readline() # 整行读取数据
if not lines:
break
new_p_tmp = []
p_tmp = [str(i) for i in lines.split(sep="\t")] # 将整行数据分割处理
# p_tmp 为每一条记录的list,其中数据类型均为str
p_tmp[3] = p_tmp[3].replace('/', '', 2).replace('年', '', 1).replace('月', '', 1).replace('日', '', 1)
# p_tmp[3]为日期,对于日期空缺,根据流水单号将其填补
# 变换日期格式,为200304xx格式
# 空缺日期从流水号中补齐
if p_tmp[3] == "###":
p_tmp[3] = p_tmp[0][:8]
else:
p_tmp[3] = p_tmp[3][0:4] + '0' + p_tmp[3][4:len(p_tmp[3])]
# 去除最后的换行符
p_tmp[len(p_tmp) - 1] = p_tmp[len(p_tmp) - 1].strip("\n")
# 去掉'-',是其能够转化为浮点型参与分析运算
# 为每一条记录加一个序号,与其索引一致,方便后期运算
p_tmp[0] = p_tmp[0].replace('-', '')
p_tmp.insert(0, count)
p_tmp[0] = count
count = count + 1
# 将所有数据转换为浮点型,以便后期运算
for i in p_tmp:
i = float(i)
new_p_tmp.append(i)
# 将小于零的购买记录和总价转正
if new_p_tmp[7] < 0:
new_p_tmp[7] = -new_p_tmp[7]
new_p_tmp[9] = -new_p_tmp[9]
# 向sales中添加数据
sales.append([new_p_tmp[0], new_p_tmp[6], new_p_tmp[7]])
data.append(new_p_tmp) # 添加新读取的数据
# 首条数据商品序号为1
if len(data) == 1:
data[0][5] = 1
# 流水号与之前的不同的商品序号为1
if data[len(data) - 1][1] != data[len(data) - 2][1]:
data[len(data) - 1][5] = 1
# 每一个购物篮商品序号从1逐次递增
if (data[len(data) - 1][5] - data[len(data) - 2][5]) > 1 and (
data[len(data) - 1][1] == data[len(data) - 2][1]):
d = data[len(data) - 1][5]
d = d - 1
data[len(data) - 1][5] = d
print(len(data))
return np.array(data), np.array(sales)
# 展示聚类后的情况
def show(label_pred, X, count):
x0 = []
x1 = []
x2 = []
x3 = []
for i in range(len(label_pred)):
if label_pred[i] == 0:
x0.append(X[i])
if label_pred[i] == 1:
x1.append(X[i])
if label_pred[i] == 2:
x2.append(X[i])
if label_pred[i] == 3:
x3.append(X[i])
# print(count[0])
plt.scatter(np.ones((count[0], 1)), np.array(x0), color='blue', label='label0')
plt.scatter(np.ones((count[1], 1)), np.array(x1), color='red', label='label1')
plt.scatter(np.ones((count[2], 1)), np.array(x2), color='yellow', label='label2')
plt.scatter(np.ones((count[3], 1)), np.array(x3), color='black', label='label3')
plt.xlim(0, 2)
plt.ylim(-100, 1200)
plt.legend(loc=2)
plt.savefig("fig_1.png")
plt.clf()
# 进行K-Means聚类,发现单次购买数量的异常点
def K_Means(sale_data, data_raw, data):
sale_data.reshape(len(sale_data), -1)
estimator = KMeans(n_clusters=4) # 构造聚类器,聚4类
estimator.fit(sale_data) # 聚类
labels = estimator.labels_ # 获取聚类标签
sale_class = [[], [], [], []]
count = [0, 0, 0, 0]
# 统计每一类的个数
for i in range(len(sales)):
if labels[i] == 0:
sale_class[0].append(sales[i])
count[0] = count[0] + 1
if labels[i] == 1:
sale_class[1].append(sales[i])
count[1] = count[1] + 1
if labels[i] == 2:
sale_class[2].append(sales[i])
count[2] = count[2] + 1
if labels[i] == 3:
sale_class[3].append(sales[i])
count[3] = count[3] + 1
# 获取数量最少的两个类,视为异常类
count_np = np.array(count)
idx = np.argpartition(count_np, 2)
count_index = idx[0]
count_index_2 = idx[1]
# 每个异常类的数量变为该商品的平均销售数量
for sale in sale_class[count_index]:
filter = np.asarray([sale[1]])
_class = data_raw[np.in1d(data_raw[:, 6], filter)]
data[int(sale[0])][7] = np.array(_class[:, 7]).mean()
# 总价相应变化
data[int(sale[0])][9] = data[int(sale[0])][7] * data[int(sale[0])][8]
for sale in sale_class[count_index_2]:
filter = np.asarray([sale[1]])
_class = data_raw[np.in1d(data_raw[:, 6], filter)]
data[int(sale[0])][7] = np.array(_class[:, 7]).mean()
# 总价相应变化
data[int(sale[0])][9] = data[int(sale[0])][7] * data[int(sale[0])][8]
# 展示聚类结果
show(labels, sale_data, count)
# 更新的数据返回
return data
# 主成分分析
def PCA_(X):
# 标准化
X_std = StandardScaler().fit(X).transform(X)
# 构建协方差矩阵
cov_mat = np.cov(X_std.T)
# 特征值和特征向量
eigen_vals, eigen_vecs = np.linalg.eig(cov_mat)
# 求出特征值的和
tot = sum(eigen_vals)
# 求出每个特征值占的比例
var_exp = [(i / tot) for i in eigen_vals]
cum_var_exp = np.cumsum(var_exp)
# 绘图,展示各个属性贡献量
plt.bar(range(len(eigen_vals)), var_exp, width=1.0, bottom=0.0, alpha=0.5, label='individual explained variance')
plt.step(range(len(eigen_vals)), cum_var_exp, where='post', label='cumulative explained variance')
plt.ylabel('Explained variance ratio')
plt.xlabel('Principal components')
plt.legend(loc='best')
plt.savefig("fig_2.png")
# 返回贡献量最小的属性的索引
return np.argmin(var_exp)
if __name__ == "__main__":
# 装载数据
data_raw, sales = load_data()
print(data_raw.shape)
# 对商品购买情况进行聚类,并消除异常何人噪声
data = K_Means(data_raw=data_raw, data=data_raw, sale_data=sales[:, 2:])
# 进行主成分分析
min_col_index = PCA_(data[:, 2:])
# 根据主成分分析结果去掉多于属性(门店号或者PSNO)
# 去掉总价格这一冗余属性
data = np.hstack((data[:, 1:min_col_index + 1], data[:, min_col_index + 2:9]))
# 保存在CVS文件中
pd.DataFrame(data, columns=["SerNo", "PSNo", "Data", "GoodNo", "GoodID", "Num", "Price"]).to_csv("data.csv",index=False)
4. 実験結果
購買数量に基づくクラスタ分析結果の模式図 クラスタリング結果からlabel1とlabel3が異常データであると推測される
店舗番号から合計金額までの主成分分析結果の模式図解析結果から、属性1は冗長な属性
データである可能性があると推測されます 処理されたCSVファイル
5. 実験分析
この実験は主に、指定されたデータの前処理に焦点を当てています。
実験で最初に遭遇した問題は、データ型と形式の違いです。これには、データ型と形式の不一致を解決する必要があります。後のさまざまな分析操作を容易にするために、すべてのデータを数値浮動小数点型に統一しましたが、実際には、これには意味があります価格や数量などの一部のデータは浮動小数点に変換できますが、日付やシリアル番号などの一部のデータは名目的な属性であり、数値属性に変換するのは適切ではありません。
データに日付が欠落している場合は、シリアル番号から直接完了を取得できますが、下図の商品No.5のように、買い物かご内の実績については、他の属性から直接情報を取得することができません。
より確実な方法は、その日に店内で最も売れた商品を数え、その商品を購入記録として記入することです。ただし、27kデータとなると計算量が膨大になるため、隣接する製品のレコードをそのまま利用して補うことにしました。
データには、購入量や合計価格がマイナスであるなど、明らかな異常やノイズがいくつかありますが、これらはデータを読み取るときに直接処理できます。他の外れ値については、K-Means クラスタリング手法を使用して検出しました。スーパーパラメータを調整することで、4つのカテゴリにクラスタリングする際に、2つのカテゴリの数が少なすぎるため、異常なカテゴリと見なすことができます。
このような異常なデータについては、購入数量を商品の平均購入数量に設定して平滑化します。
データ内の冗長な属性については、まず主成分分析 (PCA) を使用して分析を試み、寄与度の低い属性を見つけようとしました。店舗数から合計価格までの次元で共分散行列を構築し、各属性の固有値の割合を計算します。その結果、「PSNO」属性の「貢献額」は0に近く、冗長な属性であるため、削減できることが分かりました。ただし、もう 1 つの冗長な属性「合計価格」が依然としてかなりの割合を占めています。
この点に関して、私の分析では、PCA メソッドはどの属性がデータ オブジェクトの主要な属性であるか、つまりデータ オブジェクトの記述に最も貢献する属性であるかを分析します。ここでは、各レコードの合計価格がある程度説明されています。