MENU

scikit-learn 实现 ebay 数据分析

January 30, 2019 • Read: 8840 • 数据挖掘与机器学习阅读设置

ebay 在线拍卖数据分析展开目录

ebay 在线拍卖数据展开目录

ebay在线拍卖数据的数据集下载地址为 Ebay Data Set

raw.tar.gz 中包括 TrainingSet.csvTestSet.csvTrainingSubset.csvTestSubset.csv 这四个数据文件,下表列出了这四个文件的内容简介

数据名数据描述
TrainingSet2013 年 4 月的所有拍卖
TestSet2013 年 5 月第一个周的所有拍卖
TrainingSubset2013 年 4 月成功交易的所有拍卖
TestSubset2013 年 5 月第一周成功交易的所有拍卖

数据中的特征名及其对应描述:

特征名特征描述
Prices 最终交易金额
StartingBid 拍卖的最低交易金额
BidCount 此项拍卖获得的投标数
Title 交易标题
QuantitySold 成功销售的数量(0 或 1 表示)
SellerRating 卖家在 ebay 上的评级
StartDate 拍卖开始的日期
EndDate 拍卖结束的日期
PositiveFeedbackPercent 卖家收到的正反馈百分比(占所有反馈)
HasPicture 是否有实物图(0 或 1)
MemberSince 卖家创建其在 ebay 上的账户日期
HasStore 卖家是否有 ebay 店铺(0 或 1)
SellerCountry 卖家所在的国家
BuyitNowPrice 立即购买该商品的价格
HighBidderFeedbackRating 出价最高的投标者的 ebay 评级
ReturnsAccepted 是否接受退货(0 或 1 表示)
HasFreeShipping 是否包邮(0 或 1 表示)
IsHOF 卖家中是否是名人堂中的玩家(0 或 1 表示)
IsAuthenticated 是否受到工会的认证(0 或 1 表示)
HasInscription 拍卖项目是否有登记过(0 或 1 表示)
AvgPrice 库存中关于这款商品的平均价格
MedianPrice 库存中这款商品价格的中位数
AuctionCount 库存中拍卖的总数
SellerSaleToAveragePriceRatio 这项拍卖商品的价格占平均价格的比例
StateDayOfWeek 拍卖开始时是周几
EndDayOfWeek 拍卖结束时是周几
AuctionDuration 拍卖持续的天数
StartingBidPercent 该商品投标底线占平均交易价格的比例
SellerClosePercent 一个卖家成功交易的拍卖数占所有在线拍卖数的比例
ItemAuctionSellPercent 成功交易的拍卖数占所有在线拍卖数的比例

数据导入及可视化展开目录

实验用的环境是 Jupyter Python3.6

首先导入相关的包:

  • import pandas as pd
  • import numpy as np
  • import seaborn as sns
  • import matplotlib.pyplot as plt
  • %matplotlib inline

读入数据:

  • test_set = pd.read_csv("Data/TestSet.csv")
  • train_set = pd.read_csv("Data/TrainingSet.csv")
  • test_subset = pd.read_csv("Data/TestSubset.csv")
  • train_subset = pd.read_csv("Data/TrainingSubset.csv")

输出查看 train_set 的数据:

  • train_set.info() # Output train_set data

也可以使用 head() 查看前 5 条数据

  • train_set.head()

第一列属性 EbayID 为每条拍卖纪录的 ID 号,与预测拍卖是否成功没有联系,因此在模型训练时应该将该特征去除。QuantitySold 属性为 1 代表拍卖成功,为 0 代表拍卖失败,其中 SellerName 拍卖卖方的名字与预测拍卖是否成功也没有关系,因此在训练时也应将该特征去除

  • train_data = train_set.drop(['EbayID','QuantitySold','SellerName'],axis = 1)
  • train_target = train_set['QuantitySold']
  • # Gets the total number of features
  • n_trainSamples, n_features = train_data.shape

这里再解释一下,为什么要删除 QuantitySold 这个特征。因为我们要将样本数据分成两部分,一是纯的特征数据,二是对应的标签,上面的 train_data 就是特征数据,train_target 就是特征标签(是否成功拍卖)

可视化数据,取出一部分数据,两两组成对看数据在这个 2 维平面上的分布情况

  • # isSold: Auction success is 1, auction failure is 0
  • df = pd.DataFrame(np.column_stack((train_data, train_target)), columns = list(range(n_features)) + ['isSold'])
  • sns.pairplot(df[:50], vars = [2,3,4,10,13], hue = 'isSold', size = 1.5)

numpy 中矩阵列合并有两个函数,一是 hstack(),另一个是这里用到的 column_stack,这两者的区别在于:如果合并的矩阵中有某一个矩阵是稀疏矩阵(有很多 0),则最好用 column_stack

从第 3,9,12,16 维特征的散列图及柱状图可看出,这几个维度并没有很好的区分度,横纵坐标的值分别代表不同维度之间的负相关性,为了查看数据特征之间的相关性,及不同特征与类别 isSold 之间的关系,我们可以利用 seaborn 中的热度图来显示其俩俩组队之间的相关性

  • train = train_set.drop(['EbayID','SellerName'],axis = 1)
  • plt.figure(figsize = (10,10))
  • # The correlation matrix of the data is calculated
  • corr = train.corr()
  • # produce keep out the heat map triangle part of the mask, because the heat the graph is symmetric matrix
  • # so you just output the lower triangular part
  • mask = np.zeros_like(corr, dtype = np.bool)
  • mask[np.triu_indices_from(mask)] = True
  • # Produces the corresponding color change in the heat map
  • cmap = sns.diverging_palette(220, 10, as_cmap = True)
  • # Call the heat in seanborn to create a heat map
  • sns.heatmap(corr, cmap = cmap, mask = mask, vmax = .3,
  • square = True, xticklabels = 5, yticklabels = 2,
  • linewidths = .5, cbar_kws = {'shrink':.5})
  • # Rotate yticks into the horizontal direction for easy viewing
  • plt.yticks(rotation = 0)
  • plt.show()

颜色越偏红,相关性越大,越偏蓝相关性越小且负相关,白色即两个特征之间没有多大的关联,通过第一列可看出,不同维的属性与类别 isSold 之间的关系,其中第 3,9,12,16 维特征与拍卖是否会成功有很强的正相关性,其中 3,9,12,16 分别对应属性 SellerClosePercentHitCountSellerSaleAvgPriceRatioBestOffer,表示当这些属性的值越大时越有可能拍卖成功,其中第 6 维特征 StartingBid 与成功拍卖 isSold 之间呈现较大的负相关性,可看出当拍卖投标的底价越高,则这项拍卖的成功性就越低

通过这副热度图的第二列我们还可以看出不同特征与价格 Price 之间的相关性

利用数据预测拍卖是否会成功展开目录

由于数据量比较大,且特征维度也不是特别少,因此一开始做 baseline 时,就不利用 SVM支持向量机这些较简单的模型,因为当数据量比较大,且维度较高时,有些简单的机器学习算法并不高效,且可能训练到最后都不收敛

根据 scikit-learn 提供的机器学习算法使用图谱

scikit-learn 官方介绍(国内进不去)

图谱推荐先使用 SGDClassifier,其全称为 Stochastic Gradient Descent 随机梯度下降,通过梯度下降法在训练过程中没有用到所有的训练样本,而是随机从训练样本中选取一部分进行训练,但是 SGD 对特征值的大小比较敏感,而通过上面的数据站视,可以知道在我们的数据集里有数值较大的数据,如 Category。因此我们需要先使用 sklearn.preprocessing 提供的 StandardScaler 对数据进行预处理,使其每个属性的波动幅度不要太大,有助于训练时函数收敛

下面是使用 sklearn 中的 SGDClassifier 实现拍卖是否成功的模型训练代码

  • from sklearn.linear_model import SGDClassifier
  • from sklearn.preprocessing import StandardScaler
  • # The results of mini_batch learning for SGDClassifier in the training process were drawn
  • def plot_learning(clf,title):
  • plt.figure()
  • # Record the prediction of the last training result in this training
  • validationScore = []
  • # Record the forecast situation after adding this training result
  • trainScore = []
  • # Minimum training frequency
  • mini_batch = 1000
  • for i in range(int(np.ceil(n_trainSamples / mini_batch))):
  • x_batch = train_data[i * mini_batch : min((i + 1) * mini_batch, n_trainSamples)]
  • y_batch = train_target[i * mini_batch: min((i + 1) * mini_batch, n_trainSamples)]
  • if i > 0:
  • validationScore.append(clf.score(x_batch, y_batch))
  • clf.partial_fit(x_batch, y_batch, classes = range(5))
  • if i > 0:
  • trainScore.append(clf.score(x_batch, y_batch))
  • plt.plot(trainScore, label = "train_score")
  • plt.plot(validationScore, label = "validation_score")
  • plt.xlabel("Mini_batch")
  • plt.ylabel("Score")
  • plt.grid()
  • plt.title(title)
  • plt.savefig('test.jpg')
  • # Normalized data
  • scaler = StandardScaler()
  • train_data = scaler.fit_transform(train_data.drop(['EndDay'], axis = 1))
  • # Create SGDClassifier
  • clf = SGDClassifier(penalty = 'l2', alpha = 0.001)
  • plot_learning(clf, 'SGDClassifier')

训练结果如下图,由于 SGDClassifier 是在所有的训练样本中抽取一部分作为本次训练集,因此这里不适用 Cross Validation(交叉验证)
可以看到 SGDClassifier 的训练效果还不错,准确率几乎达到 92%。我们可以继续使用 scikit-learn 中封装的一些降维方法,这里我们使用三种方法进行降维 ——RandomProjectionPCAT-SNE embedding

  • from sklearn import manifold, decomposition, random_projection
  • from matplotlib import offsetbox
  • from time import time
  • images = []
  • images.append([
  • [0., 0., 5., 13., 9., 1., 0., 0.],
  • [0., 0., 13., 15., 10., 15., 5., 0.],
  • [0., 3., 15., 2., 0., 11., 8., 0.],
  • [0., 4., 12., 0., 0., 8., 8., 0.],
  • [0., 5., 8., 0., 0., 9., 8., 0.],
  • [0., 4., 11., 0., 1., 12., 7., 0.],
  • [0., 2., 14., 5., 10., 12., 0., 0.],
  • [0., 0., 6., 13., 10., 0., 0., 0.]
  • ])
  • images.append([
  • [0., 0., 0., 12., 13., 5., 0., 0.],
  • [0., 0., 0., 11., 16., 9., 0., 0.],
  • [0., 0., 3., 15., 16., 6., 0., 0.],
  • [0., 7., 15., 16., 16., 2., 0., 0.],
  • [0., 0., 1., 16., 16., 3., 0., 0.],
  • [0., 0., 1., 16., 16., 6., 0., 0.],
  • [0., 0., 1., 16., 16., 6., 0., 0.],
  • [0., 0., 0., 11., 16., 10., 0., 0.]
  • ])
  • # 1000 pieces of data were selected for visual display
  • show_instances = 1000
  • # define the drawing function
  • def plot_embedding(X, title = None):
  • x_min, x_max = np.min(X, 0), np.max(X, 0)
  • X = (X - x_min) / (x_max - x_min)
  • plt.figure()
  • ax = plt.subplot(111)
  • for i in range(X.shape[0]):
  • plt.text(X[i,0], X[i,1], str(train_target[i]),
  • color = plt.cm.Set1(train_target[i] / 2.),
  • fontdict = {'weight':'bold','size':9})
  • if hasattr(offsetbox, 'AnnotationBbox'):
  • shown_images = np.array([
  • [1., 1.]
  • ])
  • for i in range(show_instances):
  • dist = np.sum((X[i] - shown_images) ** 2, 1)
  • if np.min(dist) < 4e-3:
  • # don't show points that are too close
  • continue
  • shown_images = np.r_[shown_images, [X[i]]]
  • auctionbox = offsetbox.AnnotationBbox(
  • offsetbox.OffsetImage(images[train_target[i]], cmap = plt.cm.gray_r), X[i]
  • )
  • ax.add_artist(auctionbox)
  • plt.xticks([]), plt.yticks([])
  • if title is not None:
  • plt.title(title)
  • # Random Projuection
  • start_time = time()
  • rp = random_projection.SparseRandomProjection(n_components = 2,random_state = 50)
  • rp.fit(train_data[:show_instances])
  • train_projected = rp.transform(train_data[:show_instances])
  • plot_embedding(train_projected, "Random Projecion of the auction (time: %.3fs)" % (time() - start_time))
  • # PCA
  • start_time = time()
  • train_pca = decomposition.TruncatedSVD(n_components = 2).fit_transform(train_data[:show_instances])
  • plot_embedding(train_projected, "Pricincipal Components Projection of the auction (time: %.3fs)" % (time() - start_time))
  • # t-sns
  • start_time = time()
  • tsne= manifold.TSNE(n_components = 2, init = 'pca', random_state = 0)
  • train_tsne = tsne.fit_transform(train_data[:show_instances])
  • plot_embedding(train_projected, "T-SNE embedding of the auction (time: %.3fs)" % (time() - start_time))

随机投影效果如下图
PCA 降维效果
T-SNE 降维效果
从上面三幅图中,我们可以看出数字 0 和 1 的重叠情况,判断出数据的可区分度并不是特别大,因此我们训练效果也并没有特别好

分类训练结束后,查看分类器在测试集上的效果

  • from sklearn.metrics import precision_score, recall_score, f1_score
  • train_data = scaler.fit_transform(train_data)
  • train_pred = clf.predict(train_data)
  • print("SGDClassifier training performance on testing dataset:")
  • print("\tPrecision:%1.3f" % precision_score(train_target, train_pred))
  • print("\tRecall:%1.3f" % recall_score(train_target, train_pred))
  • print("\tF1:%1.3f \n" % f1_score(train_target, train_pred))

测试效果:

  • SGDClassifier training performance on testing dataset:
  • Precision:0.875
  • Recall:0.730
  • F1:0.796

预测拍卖最终成交价格展开目录

由于价格 Price 是一个 Numerical 的值,而拍卖是否成功是一个 Category 的值,因此两者做法是不一样的,预测价格是一个回归任务,而判断拍卖是否成功是一个分类任务

同样根据机器学习算法使用图谱,这里我们采取 SGDRegressor,代码如下:

  • from sklearn.linear_model import SGDRegressor
  • import random
  • from sklearn.preprocessing import MinMaxScaler
  • # prepare data
  • test_subset = pd.read_csv('Data/TestSubset.csv')
  • train_subset = pd.read_csv('Data/TrainingSubset.csv')
  • # Training Data
  • train = train_subset.drop(['EbayID','Price','SellerName','EndDay'],axis=1)
  • train_target = train_subset['Price']
  • scaler = MinMaxScaler()
  • train = scaler.fit_transform(train)
  • n_trainSamples, n_features = train.shape
  • # ploting example from scikit-learn
  • def plot_learning(clf,title):
  • plt.figure()
  • validationScore = []
  • trainScore = []
  • mini_batch = 500
  • # define the shuffle index
  • idx = list(range(n_trainSamples))
  • random.shuffle(idx)
  • for i in range(int(np.ceil(n_trainSamples / mini_batch))):
  • x_batch = train[idx[i * mini_batch: min((i + 1) * mini_batch, n_trainSamples)]]
  • y_batch = train_target[idx[i * mini_batch: min((i + 1) * mini_batch, n_trainSamples)]]
  • if i > 0:
  • validationScore.append(clf.score(x_batch, y_batch))
  • clf.partial_fit(x_batch, y_batch)
  • if i > 0:
  • trainScore.append(clf.score(x_batch, y_batch))
  • plt.plot(trainScore, label="train score")
  • plt.plot(validationScore, label="validation socre")
  • plt.xlabel("Mini_batch")
  • plt.ylabel("Score")
  • plt.legend(loc='best')
  • plt.title(title)
  • sgd_regresor = SGDRegressor(penalty='l2',alpha=0.001)
  • plot_learning(sgd_regresor,"SGDRegressor")
  • # 准备测试集查看测试情况
  • test = test_subset.drop(['EbayID','Price','SellerName','EndDay'],axis=1)
  • test = scaler.fit_transform(test)
  • test_target = test_subset['Price']
  • print("SGD regressor prediction result on testing data: %.3f" % sgd_regresor.score(test,test_target))
  • plt.show()

在测试集上的测试结果:SGD regressor prediction result on testing data: 0.936,由于 SGDRegressor 回归效果不错,因此就不太需要进一步选择其他的模型进行尝试了

总结展开目录

本篇文章大概讲解了如何使用 scikit-learn 进行数据分析,其实在数据分析过程中,运用到机器学习的算法进行模型训练并不是最重要的,大量的时间花费在数据的预处理上,我不止一次听到很多机器学习大牛说过一句话数据分析,最重要的不是算法,是数据。关于更多 scikit-learn 的机器学习算法,可以查看官方文档,上面有很多例子,可以帮助大家快速入门

Archives Tip
QR Code for this page
Tipping QR Code
Leave a Comment

2 Comments
  1. tk tk

    谢谢您 @(哈哈)

    1. mathor mathor

      @tk 没事,能帮到你就行