MENU

scikit-learn实现ebay数据分析

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

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没事,能帮到你就行