zoukankan      html  css  js  c++  java
  • Bike Sharing Analysis(一)- 探索数据

    1. Bike Sharing Analysis

    在这章主要介绍如何分析共享单车服务数据,以及如何基于时间、天气状态特征来识别单车的使用模式。除此之外,我们还会引入可视化分析,假设检验、以及时间序列分析的概念与方法。

    共享单车是城市里较为快速的通勤方式,了解用户使用共享单车所考虑的因素,对于公司和用户来说都是必须的。

    从公司的角度来看,了解某一个时间段某一区域里,用户对共享单车的需求,可以显著地提升业绩以及用户满意度。同时也可以优化未来的运营成本。从用户的角度来看,可能最重要的因素是:在最短的时间内满足对单车的需求。这点与公司的利益是一致的。

    在这篇文章中,我们会分析来自于华盛顿Capital Bikeshare 的单车共享数据,时间跨度从2011年1月1日,到2012年12月31日,数据以小时级别进行了聚合。也就是说, 数据中不包含单次骑行的起始与终止的位置,而是仅仅每小时的骑行次数。除此之外,数据集中还有额外的天气信息,可作为一个影响因素,影响在某个特定时间点对骑行的需求总数(天气比较差的时候可能会对骑行需求有较大的影响)。

    1.1. Note

    源数据获取地址:https://archive.ics.uci.edu/ml/datasets/Bike+Sharing+Dataset#

    若是对此topic 比较感兴趣,可以进一步阅读论文:Fanaee-T, Hadi, and Gama, Joao, 'Event labeling combining ensemble detectors and background knowledge', Progress in Artificial Intelligence (2013): pp. 1-15, Springer Berlin Heidelberg.

    虽然这个主题仅是对共享单车进行分析,但是提供的技术可以很容易应用到其他不同的共享商业模型,例如共享汽车或是共享摩托车等。

    2. 理解数据

    我们首先加载数据并做初始的分析。这里的目标主要是:

    1. 对数据有基本的了解
    2. 各个特征是如何分布
    3. 是否有缺失值
    import pandas as pd
    import seaborn as sns
    import matplotlib.pyplot as plt
    import numpy as np
    import s3fs
    
    %matplotlib inline
    
    # load hourly data
    hourly_data = pd.read_csv('s3://tang-sagemaker/workshop/bike_sharing/hour.csv')
    print(f"Shape of data: {hourly_data.shape}")
    print(f"Number of missing values in the data: {hourly_data.isnull().sum().sum()}")
    
    Res: 
    Shape of data: (17379, 17)
    Number of missing values in the data: 0
    

    查看一下统计数据:

    结合Readme.txt 里对数据的说明,我们可以了解以下几点:

    1. instant 为 id 索引,对预测无帮助
    2. 离散型特征有:season,yr,mnth,hr,holiday,weekday,workingday,weathersit
    3. 数值型特征有:temp,atemp,hum,windspeed,casual,registered,cnt
    4. 数值型特征中temp、atemp,hum,windspeed 均已做标准化处理,casual,registered,cnt未做标准化处理
    5. cnt 是 casual 与 registered 相加之和,可以由这两个属性计算出
    6. dteday为时间特征

    按照特征描述分类,可以分为3大类:

    1. 时间相关,包含条目注册时的时间:dteday,season,yr,mnth,hr,holiday,weekday,workingday
    2. 天气相关,包含天气条件:weathersit,temp,atemp,hun以及windspeed
    3. 条目本身相关,包含指定小时内,总records数:casual,registered以及cnt

    3. 数据预处理

    为了适应机器学习算法的需求,使预测结果更为准去,我们需要对数据做预处理。偶尔也会为了可视化的目的进行预处理展示。

    3.1. 处理时间与天气特征

    这里对时间与天气特征进行处理,主要不是为了方便机器学习训练,而是为了方便人可读。在数据集中,有部分特征已经被编码过,我们再次将这些特征进行编码,方便人可读:

    • season 特征,它的值为1到4,分别对应的是 Spring、Summer、Fall和 Winter;
    • yr特征,它的值为 0和1,分别对应2011 和 2012;
    • weekday特征,值为0到6,分别对应一周的每天(0: Sunday,6: Saturday)
    • weathersit特征,值为1到4,分别对应的是clear, cloudy, light_rain_snow, heavy_rain_snow
    • hum特征,被缩放到了0到1区间内,原始应为0到100区间内
    • windspeed特征,被缩放到了0到1区间内,原始应为0到67区间内

    首先处理 season、yr以及weekday 特征:

    preprocessed_data = hourly_data.copy()
    
    # temperal features
    seasons_map = {1:"Spring", 2:"Summer", 3:"Fall", 4:"Winter"}
    yr_map = {0:2011, 1:2012}
    weekday_mapping = {0:'Sunday', 1:'Monday', 2:'Tuesday', 3:'Wednesday', 4:'Thursday', 5:'Friday', 6:'Saturday'}
    
    preprocessed_data['season'] = preprocessed_data['season'].apply(lambda x: seasons_map[x])
    preprocessed_data['yr'] = preprocessed_data['yr'].apply(lambda x: yr_map[x])
    preprocessed_data['weekday'] = preprocessed_data['weekday'].apply(lambda x: weekday_mapping[x])
    

    继续处理weathersit、hum以及windspeed特征。其中hum以及wind的原始范围分别为 [0, 100] 以及[0, 67],已经被缩放为[0, 1]:

    # weather features
    weather_mapping = {1: 'clear', 2: 'cloudy', 
                       3: 'light_rain_snow', 4: 'heavy_rain_snow'}
    
    preprocessed_data['weathersit'] = preprocessed_data['weathersit'].apply(lambda x: weekday_mapping[x])
    preprocessed_data['hum'] = preprocessed_data['hum'] * 100
    preprocessed_data['windspeed'] = preprocessed_data['windspeed'] * 67
    

    验证转换效果:

    # validate
    cols = ['season', 'yr', 'weekday', 'weathersit', 'hum', 'windspeed']
    preprocessed_data[cols].sample(10, random_state=42)
    

     

    3.2. Registered versus Casual分析

    根据数据说明,registered + casual = cnt,我们可以验证一下:

    assert (preprocessed_data['registered'] + preprocessed_data['casual'] == preprocessed_data['cnt']).all(), 'not all are equal'

    首先对这2个特征进行分析的话,可以看一下它们的分布,这里会使用到seaborn,它是基于标准matplotlib构建的可视化库,为不同的统计图提供了更高级的接口。下面我们看一下registered 与 casual 骑行的分布:

    # plot distribution of registered and casual
    sns.distplot(preprocessed_data['registered'], label='registered')
    sns.distplot(preprocessed_data['casual'], label='casual')
    
    plt.legend()
    plt.xlabel('rides')
    plt.title("Rides Distribution")
    plt.savefig('figs/rides_distributions.png', format='png')
    

     

    从分布图我们可以了解到:

    1. 两者的分布均为正倾斜
    2. 骑行的registered 用户远多于casual 用户

    下面我们探索一下随时间变化的骑行数,以天为单位:

    # plot evolotion of ride over time
    plot_data = preprocessed_data[['registered', 'casual', 'dteday']]
    
    ax = plot_data.groupby('dteday').sum().plot(figsize=(10, 6))
    ax.set_xlabel("time")
    ax.set_ylabel("number of rides per day")
    plt.savefig('figs/rides_daily.png', format='png')
    

    从这个图可以看到:

    1. registered 用户的骑行次数,基本每天都是要明显超出casual 用户非常多
    2. 冬季骑行数会少下降

    但是这个图中,两个时间之间的差比非常大,所以有很高的的抖动(毛刺)。有一个平滑毛刺的方法是:使用滚动平均值与滚动标准差来替换所需要可视化的值,以及它们的期望标准差情况。

    plot_data = preprocessed_data[['registered', 'casual', 'dteday']]
    plot_data = plot_data.groupby('dteday').sum()
    
    # define window for computing the rolling mean and standard deviation
    window = 7
    rolling_means = plot_data.rolling(window).mean()
    rolling_deviation = plot_data.rolling(window).std()
    
    ax = rolling_means.plot(figsize=(10, 6))
    ax.fill_between(rolling_means.index,
                    rolling_means['registered'] + 2*rolling_deviation['registered'],
                    rolling_means['registered'] - 2*rolling_deviation['registered'],
                    alpha=0.2)
    ax.fill_between(rolling_means.index,
                    rolling_means['casual'] + 2*rolling_deviation['casual'],
                    rolling_means['casual'] - 2*rolling_deviation['casual'],
                    alpha=0.2)
    
    ax.set_xlabel("time")
    ax.set_ylabel("number of rides per day")
    plt.savefig('figs/rides_aggregated.png', format='png')
    

     

    下面我们继续关注一下骑行请求随一天中不同小时、以及一周中不同天的分布情况。我们预期是会有随时间变化的骑行请求数,因为直觉来看,骑行的请求数应该在一天中某几个特定小时,以及一周中的特定天是有关的。

    # select relevant columns
    plot_data = preprocessed_data[['hr', 'weekday', 'registered', 'casual']]
    plot_data = plot_data.melt(id_vars=['hr', 'weekday'], var_name='type', value_name='count')
    
    grid = sns.FacetGrid(plot_data, row='weekday', col='type', height=2.5, aspect=2.5,
                         row_order = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'])
    
    grid.map(sns.barplot, 'hr', 'count', alpha=0.5)
    grid.savefig('figs/weekday_hour_distributions.png', format='png')
    

     

    从这个图我们可以了解到:

    1. 在工作日,用户骑行时间主要分布在早上8点到下午6点之间,符合我们的预期;
    2. Registered 用户为共享单车的主要使用者
    3. Casual 用户在工作日使用共享单车有限
    4. 在休息日,可以明显看到对于registered 与 casual 用户骑行的分布有变化,但registered 用户仍占主要使用者大部分;两者的分布基本一致,在早上11点AM 到 6点PM的分布类似于均匀分布

    总的来说,我们可以得出结论:大部分的单车使用在工作日,一般为工作时间内(如早9晚5)。

    3. 天气对骑行影响分析

    下面我们继续探索天气对骑行的影响。

    plot_data = plot_data.melt(id_vars=['hr', 'season'], var_name='type', value_name='count')
    grid = sns.FacetGrid(plot_data, row='season', col='type', height=2.5, aspect=2.5,
                         row_order = ['Spring', 'Summer', 'Fall', 'Winter'])
    
    grid.map(sns.barplot, 'hr', 'count', alpha=0.5)
    

     

    从四个季度来看,分布基本一致,其中春季的骑行需求稍低。

    再从weekday方面进一步探索:

    plot_data = preprocessed_data[['weekday', 'season', 'registered', 'casual']]
    plot_data = plot_data.melt(id_vars=['weekday', 'season'], var_name='type', value_name='count')
    
    grid = sns.FacetGrid(plot_data, row='season', col='type', height=2.5, aspect=2.5,
                         row_order = ['Spring', 'Summer', 'Fall', 'Winter'])
    
    grid.map(sns.barplot, 'weekday', 'count', alpha=0.5,
             order=['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'])
    

    从这个图我们可以看到:对于registered 用户来说,工作日使用量高于休息日使用量;对于casual 用户来说,休息日使用量高于工作日使用量。

    据此,我们可能会提出初始的假设:registered 用户用共享单车主要是为了通勤,而casual用户主要在周末偶尔使用共享单车。

    当然,这个假设结论不能仅基于可视化图像观察,还需要有背后的统计测试进行支持。也就是我们下一节要讨论的问题。

  • 相关阅读:
    libevent的问题
    mysql homedir迁移
    mysql海量数据的优化
    Spark(Hive) SQL中UDF的使用(Python)【转】
    Spark SQL inferSchema实现原理探微(Python)【转】
    Spark SQL利器:cacheTable/uncacheTable【转】
    Spark使用CombineTextInputFormat缓解小文件过多导致Task数目过多的问题【转】
    ExecutorService-10个要诀和技巧【转】
    漫游Kafka之过期数据清理【转】
    kafka多线程消费及处理和手动提交处理方案设计[转]
  • 原文地址:https://www.cnblogs.com/zackstang/p/13963876.html
Copyright © 2011-2022 走看看