共享单车案例分析 | 一步一步跟我学Python(九)
- 1.本系列基于新生大学课程《Python编程&数据科学入门》和公开的参考资料;
- 2.文章例子基于python3.6,使用Windows系统(除了安装,其余基本没有影响);
- 3.我是纯小白,所以,错误在所难免,体系会逐渐成熟;如果您发现了错误,也烦请帮我指出来,在此先谢过了。
今天我们具体分析一个共享单车的案例。
按照上节课学到的,我们按照分析数据的五步来进行:
- 明确问题
- 数据采集
- 数据清洗
- 数据探索
- 交流结果
这些步骤,因为是现成的案例,比例可能会相差很大,主要是跑流程,让大家知道数据分析的过程是怎样的。
同时,通过案例,这节课主要学习的内容是三个方面:
- 特征过程
- 时间类型的处理
- 相关性分析
Step1:明确问题
这是一个租车数据,问题是:
租车人数是由哪些因素决定的?
Step2:采集数据
这个过程已经有人帮我们做过了。下面开始导入这个数据:
# 先导入数据包
import numpy as np
import pandas as pd
#导入绘图工具
import matplotlib.pyplot as plt
import seaborn as sns
# 导入日期时间变量处理相关的工具包
import calendar
from datetime import datetime
#一些绘图的设置
%matplotlib inline
%config InlineBackend.figure_format = 'retina'
# 读取这个数据
BikeData = pd.read_csv('lesson/bike.csv')
数据上手的三步曲:
- 了解数据大小
- 查看前几行和最后几行的数据
- 查看数据类型和缺失值
# Step1 :看数据的大小(用shape属性,而不是方法)
BikeData.shape
(10886, 12)
# step2:看前几行或后几行的数据
BikeData.head(10)
Step3:查看数据类型和是否有缺失值
BikeData.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 10886 entries, 0 to 10885
Data columns (total 12 columns):
datetime 10886 non-null object
season 10886 non-null int64
holiday 10886 non-null int64
workingday 10886 non-null int64
weather 10886 non-null int64
temp 10886 non-null float64
atemp 10886 non-null float64
humidity 10886 non-null int64
windspeed 10886 non-null float64
casual 10886 non-null int64
registered 10886 non-null int64
count 10886 non-null int64
dtypes: float64(3), int64(8), object(1)
memory usage: 1020.6+ KB
这个数据大部分都是int,有3个是浮点型的,还呀一个object,也就是字符串类型。在这个数据中,没有发现缺失值,数据非常完整。
因为很完整,就不需要进行数据清洗了,我们直接进行数据探索部分。
Step4:数据探索
这一部分是今天学习的重点。主要有三个:
- 特征工程的概念
- 日期型变量的处理
- 相关性分析
1.特征工程
“数据和特征决定了技巧学习的上限,而一个好的模型只是逼近那个上线而已。”
提取特征的过程是:原始数据——提取特征——达到模型优化的效果。
我们的目标是从原始数据中获取尽可能多的信息,一些原始数据本身往往不能直接作为模型的变量。
特征工程的定义:利用数据的相关知识来创建能够使机器学习算法达到最佳性能的特征的过程。
特征构建:从原始数据中人格构建新的特征
特征提取:自动的构建新的特征
特征选择:挑选一些有意义的特征子集
我们的时间变量不能达到效果,所以要获取一些新的特征。
2.日期型变量的处理
我们来看一下这个时间变量:
这个日期太笼统了,我们需要更细分的时间,比如日期、时分秒等。
我们以取datatime中的第一个元素为例.
# datatime的数据类型是字符串,所以可以用split的方法将字符串拆开
ex = BikeData.datetime[1]
ex
'2011-01-01 01:00:00'
看看它的类型:
type(ex)
str
是一个字符串。
# 可以用split方法拆分字符串(默认以空格拆分)
ex.split()
['2011-01-01', '01:00:00']
上面就生成了一个列表
2.1 分别提取日期和时间
# 获取日期
ex.split()[0]
'2011-01-01'
下面对整个数据表进行操作:
# 首先定义一个函数来拆分
def get_date(x):
return(x.split()[0])
# 使用pandas中的apply方法来对这个列进行操作,对datetime使用函数get_date
BikeData['date'] = BikeData.datetime.apply(get_date)
BikeData.head()
这样就从datetime中获取了日期(由于之前敲代码的问题,当时定义的是大写的,请自行更正)。
2.2 生成租车的时间
# 需要进一步拆分
ex.split()[1]
# :是分隔
ex.split()[1].split(':')
['01', '00', '00']
# 获取小时数
ex.split()[1].split(':')[0]
'01'
# 将这样的方法运用到整个列上,采用的方法也是padas的apply方法
def get_hour(x):
return(x.split()[1].split(':')[0])
# 刚才死活出错,重敲一遍就好了。郁闷。
# 运用apply
BikeData['Hour']= BikeData.datetime.apply(get_hour)
BikeData.head()
同样的方法可以获取分钟数。代码就不演示了。
2.3 生成日期对应的星期数
# 首先引入calendar中的day_name,列举了周一到周日
calendar.day_name[:]
['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']
# 获取字符串形式的日期
datestring = ex.split()[0]
dateDT = datetime.strptime(datestring, '%Y-%m-%d')
type(dateDT)
datetime.datetime
# 将字符串转换成datetime之后,用起来很方便
week_day = dateDT.weekday()
week_day
5
# 将星期数映射到对应的名字上
calendar.day_name[week_day]
'Saturday'
下面可以进行批量的转换:
def get_weekday(datestring):
week_day = datetime.strptime(datestring, '%Y-%m-%d').weekday()
return(calendar.day_name[week_day])
# 使用apply方法
BikeData['weekday']= BikeData['date'].apply(get_weekday)
BikeData.head()
2.4 生成月份
# 从datetime中直接用month属性获取月份
dateDT.month
1
#运用到整个列
def get_month(datestring):
return(datetime.strptime(datestring, '%Y-%m-%d').month)
BikeData['month'] = BikeData.date.apply(get_month)
BikeData.head(2)
2.5画图
数据分析最直观的是画出图形.
# 设置图的大小
fig = plt.figure(figsize = (18,10))
#添加一个子图
ax1 = fig.add_subplot(121)
sns.boxplot(data= BikeData, y='count')
ax1.set(ylabel= 'Count', title="Box Plot on Count")
#添加第二个图
#画出租车人数与时间的变化图
ax2 = fig.add_subplot(122)
sns.boxplot(data=BikeData, x = 'Hour', y = 'count')
ax2.set(xlabel='Hour',title= 'Box Plot on Cour and Hour')
#添加第一个子图
ax3 = fig.add_subplot(223)
sns.boxplot(data=BikeData, y='count', x='holiday')
ax3.set(ylabel= 'Count',xlabel='hol00iday',title='Holiday Count')
# 添加第4个子图
ax4 = fig.add_subplot(224)
sns.boxplot(data = BikeData, x = 'month', y= 'count')
ax4.set(xlabel= 'month',title='Month Count')
plt.show()
#ax1 = fig.add_subplot(121)
#sns.boxplot(data= BikeData, y='count')
#ax1.set(ylabel= 'Count', title="Box Plot on Count")
3.相关性
3.1相关系数
反应两个变量之间的相关性及其相关方向,范围[-1,1]
- 0表示没有线性相关性
- 负数表示负相关,一个值变大则另一个值有变小的趋势。-1,表示完全不相关
- 正数表示正相关,一个值变大则另一个值有变大的趋势。1,表示两个东西是一样的。
相关系数的绝对值大小决定了这种线性相关性的强弱。
#计算相关系数 df.corr()
correlation = BikeData[['casual','registered','temp','atemp','humidity','windspeed','count']].corr()
correlation
观察上面的相关矩阵,可以有下面这些结论:
- count和regester、casual高度正相关(大于0.5)
- count和temp、atemp相关性比较小,说明和温度、体感温度相关性比较小。和风俗的相关性更小。
- count和humidity是负相关的,
如果看起来太累了,可以画成图形:
# 画热地图来直观表示相关性矩阵
fig = plt.figure(figsize = (10,10))
sns.heatmap(correlation, vmax=0.8, square=True, annot=True)
色的浓度表示相关性的大小:
- 暖色调表示正相关
- 冷色调表示负相关
非常直观。
3.2 线性回归
定义:利用数理统计中回归分析,来确定两种或两种以上变量间相互依赖的定量关系的一种统计分析方法。
y= a*x + b
简单说,就是拟合一条直线更好的帮助我们查看趋势
他的斜率就是相关系数。
实现的regplot方法:
# count和temp的相关系数是0.39,在下面的斜率中体现出来。
# 使用seaborn中的regplot
sns.regplot(x='casual',y = 'count', data = BikeData)
作业9-2:在同一幅图中画出humidity和count的散点图以及windspeed和count的散点图,并分别加上简单线性回归直线
同样的,用seaborn的一条命令就能实现了。
# 设置图的大小
fig = plt.figure(figsize = (18,10))
#添加一个子图
ax1 = fig.add_subplot(121)
# 画出humidity和count的散点图,加线性回归直线
sns.regplot(x= 'humidity', y= 'count', data=BikeData)
ax1.set(ylabel= 'Count', xlabel='humidity', title="Correlation of count and humidity")
#添加第二个图
ax2 = fig.add_subplot(122)
# 画出windspeed和count的散点图,加线性回归直线
sns.regplot(x='windspeed',y='count',data=BikeData)
ax2.set(xlabel='windspeed',title= 'Correlation of count and windspeed')
4.数据可视化
下面,我们再用一些例子来分析一下这个数据。
4.1 问题:什么样的温度和湿度情况下租车的人数最多?
# 使用上节课讲过的pd.cut()函数来分隔连续温度和湿度。
BikeData['humidity_band'] = pd.cut(BikeData['humidity'],5)
BikeData['temp_band'] = pd.cut(BikeData['temp'],5)
#使用map函数讲holiday数据进行映射。0对应non-holiday,1对应holiday
BikeData['holiday_cat']=BikeData['holiday'].map({0:'non-holiday', 1:'holiday'})
BikeData.head()
#用Facetgrid画图
sns.FacetGrid(data=BikeData, row = 'humidity_band',size=3, aspect=2).\
map(sns.barplot, 'temp_band', 'count', 'holiday_cat', palette='deep', ci=None).\
add_legend()
plt.show()
- FacetGrid创建一个画图的模式。
- size表示高度,aspect表示长宽比
- row相当于hue,按照这个值来进行分类。
4.2 不同季节每个小时平均租车人数的变化
# 使用map将季节进行映射
BikeData['season_label']= BikeData.season.map({1:'spring',2:'summer',3:'fall',4:'winter'})
#绘图
sns.FacetGrid(data=BikeData, size=8, aspect= 1.5).\
map(sns.pointplot, 'Hour', 'count', 'season_label',palette='deep', ci=None).\ #使用pointplotY轴是平均数
add_legend()
#4.3不同天气情况下,每个月的平均租车人数如何变化?
# 将天气进行映射
BikeData['weather_label'] = BikeData.weather.map({1:'sunshine or cloudy',2:'fog or cloudy ', 3:'flurry', 4:'extreme weather'})
sns.FacetGrid(data=BikeData, size= 8, aspect=1.5).\
map(sns.pointplot, 'month', 'count', 'weather_label', palette='deep', ci=None).\
add_legend()
作业9-3:画出按照星期数划分的每小时平均租车数量,并写出自己的洞察结果。
sns.FacetGrid(data=BikeData, size=8, aspect=1.5).\
map(sns.pointplot, 'Hour', 'count', 'weekday', palette='deep', ci=None).\
add_legend()
plt.show()