每天学一点python我要成为一名优秀的数据科学家程序员

python数据分析告诉你ofo多久退押金

2019-03-23  本文已影响26人  跌跌撞撞小红豆

ofo是国内共享单车模式的开创者,引航者。在18年之前的单车风口上一时风头无两,曾花费一千万购买行星命名权,可见其财大气粗。后来潮水褪去,ofo疯狂烧钱挤占市场倾轧对手的策略最终搁浅,风光不再,连退押金都成了问题。

现如今,退押金需要排队,很多人都已经排到了一千多万名的队伍。我爬取了某用户数天内的排名变化情况,进而推测到底该用户何时才能拿到押金。
数据集大致如图1所示。


图1

该数据集一共有两列,163行。记录了从2019/3/16-2019/3/22这七天的排名变化情况。先利用该数据集画一个最简单的散点图,观察大致的数据情况。

import pandas as pd
from pandas import DataFrame,Series
from dateutil import parser
from matplotlib import pyplot as plt
data = pd.read_csv('ofo.csv')
times = []
for i in range(len(data.loc[:,'time'])):                       
    data.loc[i,'time'] = parser.parse(data.loc[i,'time'])#将每个字符串类型转换为时间类型
print(data)                                  #输出data观察数据
plt.plot_date(data['time'],data['rank'])
plt.savefig('散点图.png', bbox_inches='tight')    #保存图片并去掉周围空白
plt.xticks(rotation=45)                        #旋转横坐标
plt.show()

效果如图2所示。


图2

可以看出虽然该数据集时间范围包含了七天,可是从20号开始点才比较密集,因此缩小时间范围,重新观察。通过9.2图,发现22号的点最密集,覆盖时间范围也最完整,因此把时间范围缩减到22号这一点继续进行分析。

#引入包
import pandas as pd
from pandas import DataFrame,Series
from dateutil import parser
from matplotlib import pyplot as plt
data = pd.read_csv('ofo.csv')
times = []
for i in range(len(data.loc[:,'time'])):
    data.loc[i,'time'] = parser.parse(data.loc[i,'time'])
data.set_index(['time'],inplace=True)           #把时间列变为索引
ofo = data['2019-03-22']                      #利用时间索引类型切片22号这天的时间序列
ofo['time'] = ofo.index                        #再将索引变为列
plt.plot_date(ofo['time'],ofo['rank'])             #绘制散点图
plt.savefig('散点图.png', bbox_inches='tight')    #保存图片并去掉周围空白
plt.xticks(rotation=45)                        #旋转横坐标
plt.show()

这里要提及一下代码逻辑。第一步通过循环把每个类型为字符串的时间点变为时间类型,然后把存有该类型的列变为索引。第二步通过索引提取时间序列切片。第三步再把索引变回列。最后第四步使用plot_date函数绘制散点图。

为什么这么麻烦?兜了一个圈子把时间这列变来变去。首先,只有当索引为时间类型时才可以如此切片,即直接传入’2019-03-22’就可以得到时间范围是该天的时间序列。其次,绘制散点图不可以用Series,只可以使用Dataframe。如果把时间列当为索引,该数据类型只有一列rank,就变成了Series。所以要把时间列再变回去,成为Dataframe才可以。

还有要注意一点的是,当使用传统的scatter函数时绘制散点图会失败。这是因为该函数绘制散点图时只接受x轴为数字。这里需要使用plot_date函数,该函数专门应对绘制时间序列的散点图。

代码效果如图3所示。


图3

从该图中可以大致发现一些规律。在22号这天,早上十点之前,晚上八点以后,排名是没有变化的,推测这应该是ofo的下班时间。(一天工作10个小时,还算可以了,哈哈)然后在工作时间内,基本随着时间呈线性下降趋势。所以进一步研究,截取10:00-20:00这段时间来观察。
代码基本同上,做如下改动即可:

ofo = data['2019-03-22 10:00':'2019-03-22 20:00']

效果如图4所示。


图4

通过这张图以及结合原始数据集可知,ofo界面每五分钟刷新一次,更改一次退押金排队位置。可以大致看出,一天内十个小时,rank的变化速率比较均匀,基本是在匀速降低。接下来,具体求解到底这个速率是多少。

引入包上面已经给出

data = pd.read_csv('ofo.csv')
times = []
for i in range(len(data.loc[:,'time'])):
    data.loc[i,'time'] = parser.parse(data.loc[i,'time'])
data.set_index(['time'],inplace=True)
ofo = data['2019-03-22 10:00':'2019-03-22 20:00']
ofo_shift = ofo.shift(1)                            #滞后,方便后面进行减法运算
ofo_speed_rank = ofo_shift – ofo                #两个Series相减
ofo_speed_rank.plot()
plt.show()

通过shift函数实现时间序列的滞后(函数参数正为滞后,负为超前),方便了之后两个Series之间相减,然后绘制简单的折线图,观察每五分钟,排名变化情况。
效果如图5所示。


image.png

结合图4可以发现如下几个问题。
一、有几个时间段没有数据(数据缺失),造成中间间隔较大,并不是每五分钟都有数据的。二、有几个时间点速率为0,这是由于间隔点太密集造成排名无变化造成的。通过折线图大致看出正常情况下每五分钟rank降低个数都不会超过200,因此,利用数据过滤功能,重新绘图。效果如图6。
代码改动如下:

ofo_speed_rank = ofo_speed_rank[ofo_speed_rank['rank']<200]
ofo_speed_rank = ofo_speed_rank[ofo_speed_rank['rank']>0]
ofo_speed_rank['time'] = ofo_speed_rank.index
print(ofo_speed_rank)
plt.plot_date(ofo_speed_rank['time'],ofo_speed_rank['rank'])
plt.show()

从该散点图中可以大致看出,点主要集中在40-100之间。可以添加直线更加直观得观察。如图7所示。

plt.plot_date(ofo_speed_rank['time'],ofo_speed_rank['rank'])
x = pd.date_range(start='2019-3-22 09:00', periods=13, freq='H')
y1=[]
y2=[]
for i in range(13):
    y1.append(40)
    y2.append(100)
plt.plot(x,y1)
plt.plot(x,y2)
plt.show()
图7

而且在40-100之间分布得相当均匀,并且从时间线上来观察,也相当得均匀,可以推测ofo是通过机器来管控押金退款的,而不是人工管理。接下来求落在40-100之间的点的平均值即可。

ofo_speed_rank = ofo_speed_rank[ofo_speed_rank['rank']<100] 
ofo_speed_rank = ofo_speed_rank[ofo_speed_rank['rank']>40]
print(ofo_speed_rank.describe())
结果如下。
            rank
count  63.000000
mean   66.365079
std    14.520621
min    44.000000
25%    53.000000
50%    66.000000
75%    77.500000
max    99.000000

最大值99,最小值44,平均值66,标准差14.52。就以每五分钟处理66个押金退款来说,每天工作十小时可处理7920个退款。像文中使用者一千五百万的排名,需要等1894天,也就是大约五年的时间。
未来可期!

上一篇下一篇

猜你喜欢

热点阅读