赞
踩
昨天下班高维说要和我一起过圣诞节,让我兴奋的一夜都没睡好,只期待今天能早些过完,一定不加班,怀揣着幸福走进了公司,如往常一样打开电脑准备工作但却集中不起精力,满脑子都是如何过好圣诞节的计划,这时座位旁边的路思神神秘秘的把脑袋凑过来说,你知道么?单位说实习期过后要考试,不是咱们几个都能留在公司的,昨晚你走的早不知道,我们看见人事部和王总他们到晚上八点多还在会议室讨论淘汰方案呢。这个消息对我来说很震惊,刚毕业难道就要体验社会残酷了?我刚要想再多了解点情况的时候,一封邮件把我们九个考察期的毕业生都召到了会议室。
“这是一个艰难的决定,爱视达公司一直坚持人才为本,每名员工既是同事也是家人,但作为一家新兴公司,资本方给我们的要求是保持狼性,保持每名员工积极进取之心,只有价值观一致才能揽臂同行,经过这半年的实习,公司对你们九人的能力和态度都是认可的,经过和HR讨论做出以下决定: 一 实习期六个月转正员工,提薪30%,保险及公积金按全额工资上缴。二 公司不再提供集体宿舍,改为每个月三百元租房补贴。 三 本着优胜略汰的原则 进行转正考核,考核方式以发到各位邮箱中,请回去后好好阅读,认真对待”。
“什么啊”,路思忿忿不平的边走边说,“这不明摆着欺负咱们么?原来提供宿舍时说的多好,晚上多学习多工作,单位解决你的后顾之忧,这下好了,把我们习惯培养完了,就把宿舍取消了,300元住地下室都不够。”
“算了吧,公司又不是慈善场所”,我安慰道,虽然我觉得这绝对也是资本家的无耻。但回到座位打开邮件看到了考核要求,这才是让我感到震惊的考核方式。
“
Dear All
愉快的实现期即将接近尾声,HR经过多重考虑制订如下考核要求,请认真阅读后按要求回复。
电商平台数据分析,数据源请到公司网站新人考核的链接中下载
提供该电商平台数据分析,分析内容要求生成图表配合文字进行说明,并附上图表分析的核心代码,并自选机器学习的方法来完成对商品的售价预测
共九人,每两个人一组,自由组队,无法组队的一人单独成组
五组在26日以word方式提交数据分析报告,并提交价格预测部分的代码,经过单位评审组公平公开的评审后,会淘汰最后两组,最后两组也将十分遗憾的结束在爱视达科技的实习。
PS:请在今日中午12:00以前提交分组情况。
Thanks
”
路思这次又怒了,太孙子了,这帮资本家,我跟你说这个最后没有人愿意和他分成一组的同事肯定被公司淘汰的,你想啊,没人和他一组无非就是两个原因,一人缘太差,二水平太差。。。。
后面的话我已经听不太进去了,我得去找高维,她作为新人里面唯一的女孩子,水平并不是很高,这里不论是英雄救美也好,怜香惜玉也好,我要去和她一组,哪怕被公司淘汰。
我突然站起来隔着三排工作座位大声说,高维我和你一组,就这么定了,我给人事回邮件了,哈,这样做男人的感觉真好。
当英雄也得有资本,别最后我连累高维和我一起离开公司,圣诞节计划过过再说吧,搬了椅子坐到高维旁边,说让我们开始吧,一语双关啊。
下载数据完毕,将数据集打开,里面有个readme.txt,先看看里面写的什么。
“
mercari”创建于2013年,拥有针对智能手机的C2C(个人与个人之间的电子商务)二手交易APP,此外还提供针对书籍与CD的“KAURU”以及针对品牌类商品的“MAISONZ”服务平台。
根据数据调查,平台活跃用户中家庭主妇偏多,用户使用Mercari 消化最多的是闲置衣物。但除此之外,大到奢侈名品、3C 数码,小到牙膏和手办,Mercari 充分发挥了 C2C 模式下 SKU 丰富的天然优势,基本什么东西都能找到。上线三年来,产品下载总量已经超过 3200 万次,平台月交易额达到 8800 万美元。
但很多新手在加入mercari后发现最难的是产品定价,定价过高,没人会买你的商品,定价过低,会造成财产损失,并且产品定价不光受成本的影响,例如服装还要受到季节,品牌的影响,产地,产品的型号规格,历史购买价格,客户认可度等多方面影响,这里对于新手实在太难了。本次考核期望建立一个自动建议正确的产品价格的算法。算法将根据用户输入的产品文字说明,包括产品类别名称,品牌名称和商品条件等详细信息,然后给出合理定价。
”
我问高维,单从readme里面你能看出分析的重点有哪些?
高维说:我觉得首先这是个二手交易电商平台,定价没有标准都是根据个人喜好的,从而会产生虚假同感偏差,由于是卖家之前购买的商品,所以肯定是其之前需要或喜欢的,而我们通常又认为自己的爱好和大多数人是一样的,如果你喜欢玩摄影,你可能会高估爱玩摄影的人数,也会高估你喜欢女神的粉丝数等等,这种高估与你的行为及态度有相同特点的人数倾向就是同感偏差,这样的偏差会导致卖家提高自己的商品的卖价导致交易失败,而卖家却发现不了原因,所以我的观点是咱们在预测商品定价时要将价格再打上95折推荐出去肯定才是符合市场的平均售价。
“嗯,嗯,你真是才女啊,一个readme文件你都推测出最后结果的算法了”。我不由得赞赏道,“但你还要考虑到平台的利润啊,没有中间商赚差价的平台是不存在的,你定价过低,平台提取完利润后,卖家可能亏本了啊,先看数据集吧,产品定价的事情根据数据分析后咱们再讨论。”
数据集如图3-1,总共商品数为1482535,这是个拥有上百万商品的数据集:
图3-1 商品数据集列表
我将字段也整理成表3-1进行了标注。
字段 | 说明 |
Train_id | 商品ID |
Name | 商品名称 |
| 销售者提供的型号 |
| 商品所属类别 |
| 商品所属品牌 |
Price | 商品售价,这个字段也就是我们要预测的字段 |
Shipping | 1 : 包邮 0 :不包邮 |
| 商品的完整描述 |
表3-1 数据集字段表
面对这么少的字段数据集,好处是可以直接聚焦影响价格的字段,缺点是可能会造成数据的过拟合,最终的结果过分依赖训练中提供的数据。我思考了一下问高维,作为二手交易平台的数据集,我觉得少了个字段,就是卖家商品的购入价格,目前只有对外的标价我们没法评估呀,跟你说的禀赋效应类似,这样的售价也许都是虚高的,咱们怎么解决这个问题?高维解释道:你说的也对,但如果卖家进入一个开放社区时,面对大量同类的商品时,卖家就会回归理性会将价格趋同的,有时为了更容易卖出去还会故意压低些价格的,这样我们选一类商品去验证一下就会知道我的猜想了。
验证我们的推论先从了解数据集开始,看看商品的价格都集中在哪个区间里,顺便也了解一下日本国的物价。
安逸的手写商品价格统计代码:
# 商品价格统计代码: full_data = pd.read_csv('train.tsv','\t')
def showpricehist(): full_data.hist('price',bins=50,range=[0,250],label='price') plt.title('Price distribution') plt.xlabel('Price') plt.ylabel('Countss') plt.show() |
代码执行显示如图3-2,Y轴是数量,X轴是价格,单位是美金,高维吃惊的说到没想到日本物价这么低,商品的价格都存在与10~50美金的区间。我打击了她一下,这都是二手货好不好。
图3-2价格分布区间
我们要在这么多商品中选择一类有代表性的来验证想法,这类商品特点是数量要多,是大品牌类的,这样便于和原价做对比,来了解卖家意愿。根据图3-1我选择的数据属性是categroy_name和brand_name,两个字段来做甄别,这里的categroy_name的格式是顶级分类/二级分类/三级分类的格式,我选择三级分类+brand_name来筛选出数据量最大的商品。
安逸的手写品牌分类统计代码:
#产品分类 def split_cat(text): try: return text.split("/") except: return ("No Label", "No Label", "No Label")
#重组产品分类,按主分类/二级分类/三级分类重构数据集 full_data['general_cat'], full_data['subcat_1'], full_data['subcat_2'] = zip(*full_data['category_name'].apply(lambda x: split_cat(x)))
def CatelogGoodsStats(): brand_goods = full_data.groupby(['brand_name','subcat_2']).size().to_frame("Counts").sort_values("Counts",ascending=False).reset_index() print(brand_goods)
CatelogGoodsStats() |
我将程序运行起来将品牌与分类的售卖数量从高到低列了出来,Nike的运动球衣竟然是售卖头一名,出人意料啊,二手运动衣在我国捐给山区都不要的,但高维的兴趣点却在排名第二的维多利亚的秘密的Bras,她也很好奇,二手Bra销量这么大?都什么人在买啊,我也坏坏的笑了,可能是趣味不一样吧,不如我们选维秘内衣作为数据分析源吧,也满足你的好奇心和我的恶趣味。
图3-3品牌门类售卖排行榜
“作为二手内衣,什么才是吸引买家上钩的主因呢?应该不会是原味吧?“,我自言自语道,高维脸一红,说道:我认为应该从卖家描述关键字来分析入手,密集出现的关键字应该是吸引买家的主因,情趣内衣属于私密的炫耀型产品,宣传要能引人遐想才会有趣。
这里的卖家描述字段是‘item_description’,里面都是自然语言段落,要先拆分语言到单词,然后再将高频词语列出来,应该就能解决该问题。
安逸的手写词频云图代码:
from wordcloud import WordCloud,STOPWORDS def findVSBrasDesc(): vs_data = full_data[full_data['item_description']!='No description yet'] vs_data = vs_data[vs_data['subcat_2'] == 'Bras'] vs_data = vs_data[vs_data['brand_name'] == "Victoria's Secret"] #包邮 ship_vs_data_desc = vs_data[vs_data.shipping == 1].item_description ship_data_text = ','.join(str(i) for i in ship_vs_data_desc.values) ship_wordcloud = WordCloud(background_color='black',stopwords = STOPWORDS , max_font_size=110, min_font_size=10, mode='RGBA', font_path='source/simhei.ttf').generate(ship_data_text)
#不包邮 noship_vs_data_desc = vs_data[vs_data.shipping == 0].item_description noship_data_text = ','.join(str(i) for i in noship_vs_data_desc.values) noship_wordcloud = WordCloud(background_color='black',stopwords =STOPWORDS , max_font_size=110, min_font_size=10, mode='RGBA', font_path='source/simhei.ttf').generate(noship_data_text)
fig = plt.figure() ship_ax = fig.add_subplot(121) ship_ax.imshow(ship_wordcloud) ship_ax.set_title("包邮词频云图")
noship_ax = fig.add_subplot(122) noship_ax.imshow(noship_wordcloud) noship_ax.set_title("不包邮词频云图")
ship_ax.axis('off') noship_ax.axis('off') plt.show() |
高维看到我的词频云图代码问我,你为什么要区分包邮和不包邮啊?我说按国人的思维,同样售价产品15元的产品,如果写成10元+5元运费和15元包邮,一般人都会选择15元包邮的,总觉得付运费是吃亏了,是为额外服务付费而没有把钱花到产品本身,你这个心理学专家难道没研究过这个问题么?
图3-4 维密词频的秘密
“嗯,嗯,没想到你这个不怎么网购的人还对这个挺有研究啊,词频云图出来了,看着还挺酷么”,高维细心的观察期两者的区别起来,“不包邮的强调了Free Shipping这个特点倒是符合你的思路,在不包邮里面是free shipping,f是小写应该出现在句子的中间,push bra和bra size在不包邮里面没有成为高频词,聚拢型和情趣内衣适合的人群是喜欢包邮的,这是什么思路?”,我过去看了看说,“你没发现么包邮的size都是34B和34C,而不包邮的没有过分强调Size,你能给我个解释么?胸小的还能有折扣么?”,高维没好气的看我一眼,说:你把包邮和不包邮的价格也一并统计出来我看看。
安逸的手写维秘shipping&price 统计代码:
def findVSBrasShipStat(): vs_data = full_data[full_data['item_description']!='No description yet'] vs_data = vs_data[vs_data['subcat_2'] == 'Bras'] vs_data = vs_data[vs_data['brand_name'] == "Victoria's Secret"]
ship_data = vs_data.groupby(["shipping","price"]).size().to_frame("Counts").sort_values("Counts",ascending=False).reset_index()
sns.lineplot(x = 'price',y="Counts",hue = 'shipping',data = ship_data) plt.xticks(rotation=90) plt.show() |
对着我漂亮的统计图,高维指着图3-5说低价格区间里面不包邮的数量远远超过包邮的数量,总体上也是不包邮的占大多数,25美金左右的Bras是售卖最多的价格100美金以后基本就没有销售量了,按照6.5的汇率算,25美金合成人民币在162.5元,而维秘价格普遍在200元人民币左右,也就是说新品打85折是,卖家的普遍心理价位。我说:分析的有道理,但卖家如果是个美女,也许我作为买家会可以接受价格高点。
图3-5 维秘shipping&price 统计
我们初步有了个价格预期,新品85折是卖家的普遍预期,那么为了更好的销售产品要把描述中的最常出现的词也要加入,这里我用了之前培训时处理该方式的两种方法:
1.CountVectorizer:这种方法比较简单,仅考虑每个词汇出现的频率
2.TfidfVectorIzer:除了考虑词汇出现的频率,还考虑在样本总体中出现的总频率数
到底用哪个好呢,作为没有经验的我有些没注意,还是只动嘴不动手的人更容易决定事情,高维说,不知道用哪个好,就都列出结果来,我看看。
安逸的手写CountVectorizer代码:
from sklearn.feature_extraction.text import CountVectorizer import collections from pprint import pprint def DescCountVectorizer(): vs_data = full_data[full_data['item_description']!='No description yet'] vs_data = vs_data[vs_data['subcat_2'] == 'Bras'] vs_data = vs_data[vs_data['brand_name'] == "Victoria's Secret"]
vs_data_desc = vs_data.item_description vs_data_text =[] for value in vs_data_desc.values: vs_data_text.append(value)
c_vec_s = CountVectorizer(analyzer='word',stop_words='english') c_vec_s.fit_transform(np.array(vs_data_text))
sort_dict = collections.OrderedDict(sorted(c_vec_s.vocabulary_.items(),key=lambda t:t[1])) pprint(sort_dict)
|
安逸的手写TfidfVectorizer代码
from sklearn.feature_extraction.text import TfidfVectorizer def DescTFidfVectorizer():
vs_data_desc = full_data.item_description vs_data_text =[] for value in vs_data_desc.values: vs_data_text.append(value)
t_vec_s = TfidfVectorizer(analyzer='word',stop_words='english') weight = t_vec_s.fit_transform(np.array(vs_data_text)).toarray() word = t_vec_s.get_feature_names()
sort_dict = collections.OrderedDict(sorted(t_vec_s.vocabulary_.items(),key=lambda t:t[1])) for i in range(len(weight)): for j in range(len(word)): pprint (word[i] +" : " + str(weight[i][j])) |
CountVectorizer输出结果:
图3-6 CountVectorizer统计图 | 图3-6所示的是左边是单词,右边是单词在描述中出现的次数,这里我截取的最多的一些词汇,比如Zoomed在数据集最长出现的写法是:In the last pic is the 32c bra zoomed in to try to show you that it really is a 32c bra.而剩下的词汇都是一些品牌的名称,而这些品牌名称为什么会大量的出现呢?举个例子
They are all Small 2nd pic: from zumies or pacsun 3rd pic: from Hollister Last pic: white - from zumies or pacsun Pink- from VS Worn a couple times In good condition,Women,Underwear,Bras。
这里就能明白了之前描述了二手交易网站都是一些家庭妇女在用,而她们并不会修图,所以晒的图片都是一些从其它网站找到的样例。 |
我还没有去运行TfidfVectorizer就被高维喊停了,“你这搞得什么呀,这样的分析只适合工程师看,最后评审咱们分析结果的肯定是公司运维部门的,你要直接给他们展示出一个显而易见的答案,我认为你应该把最理想的产品描述直接列出来供用户选择,明白么?”。
“好吧,谁都知道你说的那样好”,我答复并在脑子里回溯之前培训相关部分的知识,每段描述都相当于一个短文,整体描述字段是若干短文集合而成,而Counter和Tfidf可以将词频和词的权重统计出来,我应该把这些高频和高权重的词组合出来就是运维需要的,我需要做的就是将整体描述字段提取10个主题核心,这里有矩阵分解NMF和潜在狄利克雷分配LDA两种算法可以去发现存在的主题,NMF是应用在Tfidf上,而LDA则使用原始计数即可,即使用CountVectorizer来完成LDA。剩下的就是改造之前的代码。
安逸的手写NMF与LDA代码:
def display_topics(model, feature_names, no_top_words): for topic_idx, topic in enumerate(model.components_): print (u"Topic %d:" % (topic_idx)) print (" ".join([feature_names[i] for i in topic.argsort()[:-no_top_words - 1:-1]]))
#NMF & LDA分布 from sklearn.decomposition import NMF, LatentDirichletAllocation #显示20个主题 no_topics = 10 topic_len = 15 def NMFLDATopic(): vs_data_desc = full_data.item_description vs_data_text =[] for value in vs_data_desc.values: vs_data_text.append(value) #tfidf&nmf t_vec_s = TfidfVectorizer(analyzer='word',stop_words='english') tfidf = t_vec_s.fit_transform(np.array(vs_data_text)).toarray() nmf = NMF(n_components=no_topics, random_state=1, alpha=.1, l1_ratio=.5, init='nndsvd').fit(tfidf) print (u"=====NMF TOPIC====") display_topics(nmf,t_vec_s.get_feature_names(),topic_len) print ("\n") #count&lda c_vec_s = CountVectorizer(analyzer='word',stop_words='english') tf = c_vec_s.fit_transform(np.array(vs_data_text)) lda = LatentDirichletAllocation(n_topics=no_topics, max_iter=5, learning_method='online', learning_offset=50.,random_state=0).fit(tf) print (u"====LDA TOPIC====") display_topics(lda,c_vec_s.get_feature_names(),topic_len) |
运行生成了图3-7和图3-8,这里利用LDA与NMF分别抽象出十个主题交给高维看,这时她才满意的点点头。
图3-7 NMF主题抽象
|
图3-8 LDA 主题抽象 |
虽然工作的不错,但我还是有疑问,难道采用词频最多的描述会对买家有影响?二手货卖家描述怎么能千篇一律?高维回答:“你会排队去买一杯又需要排队而且又贵的奶茶么?我想你肯定回答NO,但在实际场景中你可能会受到从众心理或者售价格低于预期的影响而产生购买,尤其当自己不好判断或无法判断时,总是倾向于认同群体的选择,我们选择最多的描述关键词让买家觉得这是个被普遍认同的商品,让买家在阅读商品描述上没有障碍,更利于交易”。我似是而非的点点头,反正我不理解她的时候都是靠点头来完成交流。
我还在思考下一步我应该处理什么的时候,高维又发指令了,“你把用户描述和价格的关系做个算法关联起来,我们最终要推荐售价,数据集每个字段都会对最终售价有影响,具体哪个影响比较多目前还不知道,你先把描述字段单独提出来做关联,然后再做整体字段对价格的关联”。
商品描述与价格的关系,是一个字符串对应数字的映射关系,用回归方式即可,字符串只要变成有意义的数字就可以,可以使Tfidf也可以是Counter,我选择了Tfidf,回归算法采用的GradientBoostingRegressor,选择这个算法主要是基于其可以进行拟合序列曲线,而描述语句本身就是个序列,Boosting则可以将这些对价格毫无关系的弱分类器拟合成一个强分类器,并且能在错误中学习和迭代来降低损失值。
安逸的手写GradientBoostingRegressor代码:
from sklearn.ensemble import GradientBoostingRegressor def vsdesctoprice(): vs_data_desc = full_data.item_description vs_data_text =[] for value in vs_data_desc.values: vs_data_text.append(value) #tfidf&nmf t_vec_s = TfidfVectorizer(analyzer='word',stop_words='english') tfidf = t_vec_s.fit_transform(np.array(vs_data_text)).toarray()
gbr = GradientBoostingRegressor() gbr.fit(tfidf,full_data.price)
gbr_y_pred = gbr.predict(item_desc) full_data['item_description'] = pd.Series(gbr_y_pred) full_data['item_description'].fillna(full_data['item_description'].mean(),inplace=True) |
这里我将商品描述字段与价格进行了预测关联,并将描述字段替换成了自身预测的价格,高维问我,做法虽然巧妙,但如果描述字段预测不准会对最终结果有影响么?我回答:“不会的,因为所有的描述字段都是按照相同算法来替换,准或不准对最终结果产生权重是一样,下面把其它字符字段也一并处理了。我写了快一天的代码了,我要回座位上写日报了,这个简单的任务交给你吧,你把注释加好就行了,防止别人看不懂”。
高维带注释的字段处理代码:
def vsdesctoprice(): full_data = pd.read_csv('vsdata.csv',encoding = "ISO-8859-1") vs_data_desc = full_data.item_description vs_data_text =[] for value in vs_data_desc.values: vs_data_text.append(value) #tfidf&nmf t_vec_s = TfidfVectorizer(analyzer='word',stop_words='english') tfidf = t_vec_s.fit_transform(np.array(vs_data_text)).toarray()
gbr = GradientBoostingRegressor() gbr.fit(tfidf,full_data.price)
gbr_y_pred = gbr.predict(tfidf) full_data['item_description'] = pd.Series(gbr_y_pred) full_data['item_description'].fillna(full_data['item_description'].mean(),inplace=True) #高维:区分训练字段与目标字段 train_col = ['name','item_condition_id','brand_name','shipping','item_description','general_cat','subcat_1','subcat_2'] target_col = ['price'] full_data_col = train_col + target_col
full_data = full_data[full_data_col]
#高维:区分训练集与测试集 train_x,test_x,train_y,test_y = train_test_split(full_data[train_col],full_data[target_col].astype(float)) #高维:使用LabelEncoder编码,将字符串字段转为0~nClass之间唯一编码 le = preprocessing.LabelEncoder() #高维:将所有字符字段进行转化 train_x['name'] = le.fit_transform(train_x['name']).astype(float) train_x['brand_name'] = le.fit_transform(train_x['brand_name']).astype(float) train_x['general_cat'] = le.fit_transform(train_x['general_cat']).astype(float) train_x['subcat_1'] = le.fit_transform(train_x['subcat_1']).astype(float) train_x['subcat_2'] = le.fit_transform(train_x['subcat_2']).astype(float) train_x['item_condition_id'] = le.fit_transform(train_x['item_condition_id']).astype(float)
#高维:使用XGB提升回归数算法进行预测 exported_pipeline = XGBRegressor(learning_rate=0.1, max_depth=2, min_child_weight=6, n_estimators=100, nthread=1, subsample=0.9000000000000001)
exported_pipeline.fit(train_x, train_y) #高维:打印影响价格权重 print("---------------------------------------") pprint(eli5.explain_weights_xgboost(exported_pipeline,top=10)) print(eli5.show_weights(exported_pipeline,vec=train_col,top=10)) results = exported_pipeline.predict(test_x) print (results) #高维:将预测结果输出并保存 pd.to_csv(results) |
当我回到高维身边看到她的代码我又再次感慨她的心里对我还是有道防线,将她的名字写到注释前面,代表我是我,她是她,我坐在那里突然有些尴尬不知道说些什么好,真是最怕空气中突然的安静,我没话找话说,你这个XGBRegressor的参数是怎么确定的?好像很复杂呀,是交叉验证得到的么?
高维骄傲的说当然不是了,之前我对调整参数一直很好奇,教咱们建模的老师就告诉咱们交叉验证一种方式,但对于我这样数学基础薄弱的人实在难以理解,我请教了大师兄王凯,他说别看他的调参厉害,选用模型准确都是靠着一个厉害的工具库TOPT,我刚才也是用TOPT运行了一下才得到了选用模型和参数,我给你写个例子你看看。
高维的TOPT手写代码:
from tpot import TPOTRegressor tpot = TPOTRegressor(generations=5, population_size=50, verbosity=2) tpot.fit(train_x, train_y) tpot.export('tpotopt.py') |
我心里有点不舒服,关于调参问题我也请教过王凯,他却说这是经验和数学基础,对于我这样半路出家的,安心写代码就好,调参交给他就行了。好在程序这时执行完了,缓解了我的尴尬。
我对着图3-9参数权重验证了下,描述字段对最后的报价起到了绝对影响力,而商品分类,品牌名称字段使用都是维秘的相同字段,故权重都是0,其它字段都占到的权重值也都不是很高,符合我对模型的预期。
图 3-9 属性权重输出
松了口气看了下系统托盘时间已经接近晚上九点了,高维说:“有点晚了,安逸你受累把文档写了吧,太晚了我回宿舍害怕,明天早上咱们就提交,这样明天晚上还可以去吃圣诞大餐。”
从单位出来时已经快十二点了,没有公车了,走在寒冷的街道上看着零零星星的过往车辆,虽然很累但心中却对明天充满了希望。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。