• 如何进行数据科学竞赛?(EDA)


    作者|Kemal Erdem
    编译|VK
    来源|Towards Data Science

    本文基于我在Driventa平台上参加DengAI(登革热)竞赛的文章。我的排名在前0.2%(截至2020年6月2日为14/9069)。我在这里提出的一些想法是严格为这样的比赛设计的,可能在现实生活中不是特别有用。

    在开始之前,我必须警告你,对于更高级的数据工程师来说,有些部分可能是显而易见的,这是一篇非常长的文章。你可以一节一节地读,只需选出你感兴趣的部分。

    问题描述

    首先,我们需要讨论竞赛本身。DengAI的目标是(目前是,因为Driventa管理局决定将其设为“持续的”比赛,因此你可以现在加入)根据天气数据和地点预测特定一周的登革热病例数。

    每个参与者都得到了一个训练数据集和测试数据集(不是验证数据集)。MAE(平均绝对误差)是一种用于计算分数的指标,训练数据集涵盖了2个城市(1456周)28年的每周的数值。测试数据较小,有跨越5年和3年的(这取决于城市)。

    登革热是一种蚊子传播的疾病,发生在世界热带和亚热带地区。因为它是由蚊子携带的,该疾病的传播与气候、天气变化有关。

    训练数据集:https://github.com/burnpiro/dengai-predicting-disease-spread/blob/master/dengue_features_train.csv

    测试数据集:https://github.com/burnpiro/dengai-predicting-disease-spread/blob/master/dengue_features_test.csv

    数据集

    如果我们看一下训练数据集,它有多个特征:

    城市和日期指标

    • city :sj代表San Juan(圣胡安),iq代表Iquitos
    • week_start_date -以yyyy-mm-dd格式给出的日期

    NOAA的GHCN每日气候数据气象站测量:

    • station_max_temp_c-最高温度
    • station_min_temp_c-最低温度
    • station_avg_temp_c-平均温度
    • station_precip_mm-总降水量
    • station_diur_temp_rng_c-昼间温度范围

    卫星降水测量(0.25x0.25度的标度)

    • precipitation_amt_mm-总降水量

    NOAA的NCEP气候预报系统分析测量(0.5x0.5度的标度)

    • reanalysis_sat_precip_amt_mm-总降水量
    • reanalysis_dew_point_temp_k-平均露点温度
    • reanalysis_air_temp_k-平均气温
    • reanalysis_relative_humidity_percent-平均相对湿度
    • reanalysis_specific_humidity_g_per_kg-平均特定湿度
    • reanalysis_precip_amt_kg_per_m2-总降水量
    • reanalysis_max_air_temp_k=最大空气温度
    • reanalysis_min_air_temp_k-最低空气温度
    • reanalysis_avg_temp_k-平均气温
    • reanalysis_tdtr_k-白天温度范围

    NOAA的CDR归一化差异植被指数(NDVI)(0.5x0.5度的标度)

    • ndvi_se-城市质心东南的NVDI
    • ndvi_sw-城市质心西南的NVDI
    • ndvi_ne-城市质心东北的NVDI
    • ndvi_nw-城市中心西北的NVDI

    此外,我们还有每周总病例数的信息。

    很容易发现,对于数据集中的每一行,我们都有多个描述类似数据的特征。有四类:

    • 温度

    • 降水

    • 湿度

    • ndvi(这四个特征指的是城市的不同点,因此它们不是完全相同的数据)

    因此,我们应该能够从输入中删除一些冗余数据。

    输入示例:

    week_start_date 1994-05-07
    total_cases 22
    station_max_temp_c 33.3
    station_avg_temp_c 27.7571428571
    station_precip_mm 10.5
    station_min_temp_c 22.8
    station_diur_temp_rng_c 7.7
    precipitation_amt_mm 68.0
    reanalysis_sat_precip_amt_mm 68.0
    reanalysis_dew_point_temp_k 295.235714286
    reanalysis_air_temp_k 298.927142857
    reanalysis_relative_humidity_percent 80.3528571429
    reanalysis_specific_humidity_g_per_kg 16.6214285714
    reanalysis_precip_amt_kg_per_m2 14.1
    reanalysis_max_air_temp_k 301.1
    reanalysis_min_air_temp_k 297.0
    reanalysis_avg_temp_k 299.092857143
    reanalysis_tdtr_k 2.67142857143
    ndvi_location_1 0.1644143
    ndvi_location_2 0.0652
    ndvi_location_3 0.1321429
    ndvi_location_4 0.08175
    

    提交格式:

    city,year,weekofyear,total_cases
    sj,1990,18,4
    sj,1990,19,5
    ...
    

    评分评价:

    数据分析

    在开始设计模型之前,我们需要查看原始数据并加以修正。为了达到这个目的,我们将使用pandas库。通常,我们可以直接导入.csv文件并处理导入的数据帧,但有时(特别是当第一行没有列描述时),我们必须提供列列表。

    import pandas as pd
    pd.set_option("display.precision", 2)
    
    df = pd.read_csv('./dengue_features_train_with_out.csv')
    df.describe()
    

    Pandas有一个名为describe的内置方法,它显示数据集中列的基本统计信息。

    当然,这种方法只适用于数值数据。如果我们有非数值列,我们必须先做一些预处理。在我们的例子中,唯一的列是city。这个列只包含sj和iq两个值,我们稍后将处理它。

    回到主表。每行包含不同类型的信息:

    • count -描述非NaN值的个数
    • mean -整列的平均值(用于标准化)
    • std -标准差(也可用于标准化)
    • min ->max -显示包含值的范围(用于缩放)

    让我们从计数开始。知道数据集中有多少条记录丢失了(一个或多个)并决定如何处理这些数据是很重要的。如果你看ndvi_nw值,它在13.3%的情况下是空的。如果你决定用诸如0之类的任意值替换缺少的值,这可能是个性能问题。通常,有两种常见的解决方案:

    • 设置平均值

    • 插值法

    插值(处理缺失数据)

    当处理序列数据时(就像我们这个场景一样),从它的邻域中插值(仅从邻域中取平均值)值,而不是用整个集合中的平均值来代替它,这样比较合理。

    通常,序列数据在序列中的值之间有一定的相关性,使用邻域可以得到更好的结果。我给你举个例子。

    假设你正在处理温度数据,并且你的整个数据集由一月到十二月的值组成。全年的平均值将是一年中大部分时间缺失天数的无效替代值。

    如果从7月开始计算,则可能会有[28,27,-,-,30]。如果在伦敦的话,年平均气温是11摄氏度(或52华氏度)。在这种情况下使用11作为温度填充就是错误的。这就是为什么我们应该使用插值而不是平均值。

    有了插值(即使在有更大差距的情况下),我们应该能够获得更好的结果。如果你计算这些值,你应该得到(27+30)/2=28.5和(28.5+30)/2=29.25,所以最终我们的数据集看起来像是[28,27,28.5,29.25,30],远远好于[28,27,11,11,30]。

    将数据集拆分为城市

    因为我们已经讨论了一些重要的事情,所以我们可以定义一种方法,该方法允许我们将分类列(city)重新定义为二进制列向量并对数据进行插值:

    def extract_data(train_file_path, columns, categorical_columns=CATEGORICAL_COLUMNS, categories_desc=CATEGORIES,
                     interpolate=True):
        # 读取csv文件并返回
        all_data = pd.read_csv(train_file_path, usecols=columns)
        if categorical_columns is not None:
            # 将分类映射到列
            for feature_name in categorical_columns:
                mapping_dict = {categories_desc[feature_name][i]: categories_desc[feature_name][i] for i in
                                range(0, len(categories_desc[feature_name]))}
                all_data[feature_name] = all_data[feature_name].map(mapping_dict)
    
            # 将映射的分类数据更改为0/1列
            all_data = pd.get_dummies(all_data, prefix='', prefix_sep='')
    
        # 修复丢失的数据
        if interpolate:
            all_data = all_data.interpolate(method='linear', limit_direction='forward')
    
        return all_data
    

    所有常量(如分类列)都在这个要点中定义:https://gist.github.com/burnpiro/30610b5cf9fd685905fe36a0572ab292

    此函数返回一个数据集,其中包含两个名为sj和iq的二进制列,它们具有布尔值,其中city被设置为sj或iq。

    绘制数据

    绘制数据图以直观地了解值在序列中的分布是很重要的。我们将使用一个名为Seaborn的库来帮助我们绘制数据。

    sns.pairplot(dataset[["precipitation_amt_mm", "reanalysis_sat_precip_amt_mm", "station_precip_mm"]], diag_kind="kde")
    

    这里我们只有一个数据集的特征,我们可以清楚地区分季节和城市(平均值从297下降到292)。

    另一件有用的事情是不同特征之间的关联。这样我们就可以从数据集中删除一些冗余的特征。

    正如你所注意到的,我们可以立即删除其中一个降水特征。一开始这可能是无意的,但因为我们有来自不同来源的数据,同一类数据(如降水量)并不总是完全相关的。这可能是由于不同的测量方法或其他原因造成的。

    数据相关性

    当我们使用很多特征时,我们不需要为每一对都绘制成对图。另一个选择就是计算所谓的相关性分数。不同类型的数据有不同类型的相关性。我们使用corr()的方法生成数据集的数值相关性。

    如果有不应该被视为二进制的分类列,你可以计算Cramer的V关联度量来找出它们和其他数据之间的“相关性”。

    import pandas as pd
    import seaborn as sns
    
    # 导入我们的提取函数
    from helpers import extract_data
    from data_info import *
    
    train_data = extract_data(train_file, CSV_COLUMNS)
    
    # 获取“sj”city的数据并删除两个二进制列
    sj_train = train_data[train_data['sj'] == 1].drop(['sj', 'iq'], axis=1)
    
    # 生成热图
    corr = sj_train.corr()
    mask = np.triu(np.ones_like(corr, dtype=np.bool))
    plt.figure(figsize=(20, 10))
    ax = sns.heatmap(
        corr, 
         mask=mask, 
        vmin=-1, vmax=1, center=0,
        cmap=sns.diverging_palette(20, 220, n=200),
        square=True
    )
    ax.set_title('Data correlation for city "sj"')
    ax.set_xticklabels(
        ax.get_xticklabels(),
        rotation=45,
        horizontalalignment='right'
    );
    

    你可以对iq 城做同样的事情,并将两者进行比较(相关性是不同的)。

    如果你看看这张热图,很明显可以看出哪些特征相互关联,哪些不相关。你应该知道有正相关和负相关(深蓝色和深红色)。没有相关性的特征是白色的。

    有几组正相关的特征,毫不奇怪,它们指的是同一类型的测量(例如station_min_temp_c和station_avg_temp_c之间的相关性)。但在不同的特征之间也存在相关性(比如reanalysis_specific_humidity_g_per_kg和reanalysis_dew_point_temp_k)。我们还应该关注total_cases和其他特征之间的相关性,因为这是我们需要预测的。

    这次我们走运了,因为没有什么东西和我们的目标有很强的相关性。但是我们仍然应该能够为我们的模型选择最重要的特征。现在看热图没什么用,让我切换到条形图。

    sorted_y = corr.sort_values(by='total_cases', axis=0).drop('total_cases')
    plt.figure(figsize=(20, 10))
    ax = sns.barplot(x=sorted_y.total_cases, y=sorted_y.index, color="b")
    ax.set_title('Correlation with total_cases in "sj"')
    

    通常,在为模型选择特征时,我们选择的特征与目标的绝对相关值最高。这取决于你决定你选择多少特征,你甚至可以选择所有的特征,但这通常不是最好的主意。

    观察目标值在数据集中是如何分布的也是很重要的。我们可以很容易地用pandas做到:

    平均一周的病例数相当少。只是偶尔(一年一次),案件总数会跳到某个更高的数值。在设计我们的模型时,我们需要记住这一点,因为即使我们设法找到了“跳跃”,我们可能会在几周内得分损失惨重,因为跳跃这种情况几乎没有样本。

    什么是NDVI值?

    本文最后要讨论的是NDVI指数(归一化差异植被指数)。这个指数是植被的指标。高负值对应于水,接近0的值表示岩石/沙子/雪,值接近1热带森林。在给定的数据集中,每个城市有4个不同的NDVI值(每个值对应于地图上不同的角落)。

    即使总体NDVI指数对于了解我们正在处理的地形类型非常有用。如果我们需要为多个城市设计一个模型,地形类型的特征可能会很有用,但在这种情况下,我们只已知两个城市的气候和位置。我们不需要训练我们的模型来判断我们所处的环境,相反,我们可以为每个城市训练不同的模型。

    我花了一段时间尝试使用这些值(尤其是在这种情况下插值很困难,因为我们在这个过程中使用了大量的信息)。使用NDVI指数也可能产生误导,因为数值的变化并不一定与植被过程的变化相对应。

    结论

    现在,你应该知道我们的数据集是什么样子的。我们甚至还没有开始设计第一个模型,但已经知道有些特征不如其他特征重要,有些只是重复的数据。如果你需要从这整篇文章中得到一件事,那就是“首先试着理解你的数据!”.

    参考文献:

    DengAI: Predicting Disease Spread https://www.drivendata.org/competitions/44/dengai-predicting-disease-spread/

    原文链接:https://towardsdatascience.com/dengai-how-to-approach-data-science-competitions-eda-22a34158908a

    欢迎关注磐创AI博客站:
    http://panchuang.net/

    sklearn机器学习中文官方文档:
    http://sklearn123.com/

    欢迎关注磐创博客资源汇总站:
    http://docs.panchuang.net/

  • 相关阅读:
    严蔚敏数据结构习题3.14
    Effective C++ Item 34 Differentiate between inheritance of interface and inheritance of implementation
    Effective C++ Item 33 Avoid hiding inherited names
    Effective C++ Item 19 Treat class design as type design
    Effective C++ Item 18 Make interfaces easy to use correctly and hard to use incorrectly
    Effective C++ Item 17 Store newed objects in smart pointer in standalone statements
    Effective C++ Item 16 Use the same form in corresponding uses of new and delete
    Effective C++ Item 15 Provide access to raw resources in resource-managing classes
    Effective C++ Item 14 Think carefully about copying behavior in resource-managing classe
    Effective C++ Item 13 Use object to manage resources
  • 原文地址:https://www.cnblogs.com/panchuangai/p/13587293.html
Copyright © 2020-2023  润新知