欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

数据分析小白入门篇,MathorCup 无车承运人平台线路定价问题,特征间的相关性分析

程序员文章站 2022-07-13 21:58:37
...

想要看更加舒服的排版、更加准时的推送
关注公众号“不太灵光的程序员”
每日八点有干货推送,微信随时解答你的疑问
公众号“不太灵光的程序员” 同时发布《【MathorCup】2020年 A题 无车承运人平台线路定价问题,特征间的相关性分析》

2020年第十届MathorCup高校数学建模挑战赛题目

A题 无车承运人平台线路定价问题

题目详情附件及源码下载请关注公众号“不太灵光的程序员”回复关键字“MC2020A”

对代码存在疑问,请在公众号留言,我会及时和您取得联系。

问题 1:通过定量分析的方法,研究影响无车承运人平台进行货运线路定价的主要因素有哪些,并说明理由。

问题 2:根据附件 1 数据,通过建立数学模型,对已经成交货运线路历史交易数据中的定价进行评价。

问题 3: 建立关于线路定价的数学模型,给出附件 2 的线路任务的三次报价以及总成本定价,并填充在附件3 的表格中;给出你们的调价策略;评价你们对附件 2 的线路任务所给出的定价。

其中附件 3 的表格以 Excel文件形式,连同论文答卷一起上传至参赛系统,请勿改变附件 3 中各任务ID 的原有顺序。

附件 3 将用于测试报价的准确性,对于某个确定的任务,三次报价中有一次成交,则后续价格将不再考虑。

附件 1:货运线路历史交易数据
附件 2:待定价的货运线路任务单
附件 3:计算结果

三个问题分别是对特征间的相关性、给出定价的评价指标、预测总成本以及三次定价三个方向进行考查的,下面我们先对问题1进行解释。

分析过程

1、数据预处理(数据清理)

数据预处理可以分为以下5个步骤,结合代码我们来说明如何进行相关性分析的:

  • 1.1. 读取给定的数据
  • 1.2 处理特征数据与问题无关的特征
  • 1.3 数据整合
  • 1.4 数据分类编码
  • 1.5 清洗后的数据存储

1.1. 读取给定的数据

从不同存储文件中读取数据集,预览数据集,观察特征数据。

题目提供了附件1:货运线路历史交易数据.xlsx的数据集文件,在我们自己进行分析时,大多情况还需要自己去爬取数据,建议将数据保存成csv文件在进行分析。

做数据分析时基础数据会有多种存储形式。可能是txt、csv 、excel文件。

txt 和 csv 可以使用 pandas.read_csv 进行加载;

excel 可以使用 pandas.read_excel 进行加载,当然也可以借助模块 xlrd 和 xlwt 数据读写。

操作 excel 要比操作 csv 更耗时,推荐使用csv处理中间数据。

源代码:

def data_load(file_name):
    """
    加载数据
    :param file_name:
    :return:
    """
    types = file_name.split('.')
    if types[-1] == 'csv':
        df = pd.read_csv(file_name)
    elif types[-1] == 'txt':
        df = pd.read_csv(file_name)
    elif 'xl' in types[-1]:
        df = pd.read_excel(file_name)
        # 打开文件 需要注意 时间读取的问题
        # data = xlrd.open_workbook(file_name)
        # table = data.sheet_by_index(0)
        # data_lists = []
        # columns = table.row_values(0)
        # for i in range(1, table.nrows):
        #     data_lists.append(table.row_values(i))
        # df = pd.DataFrame(data=data_lists, columns=columns)
    else:
        raise(f"{file_name} 文件类型不支持")
    return df

1.2. 处理特征数据与问题无关的特征

数据中存在缺失值的删除对应行数据,样本数量极少的数据项也可以删除特征。

在基础数据集中会有许多变量数据,部分变量可能是多余的,一方面是变量本身对于每次数据分析有使用价值的,故无法提高模型性能;另一方面过这些多余变量在构建模型时会消耗大量内存和计算能力。

需要根据你对数据关联性的理解或者说是要去探究的相关性时变量值才存在意义。

筛选特征时有两种方式:一种是删除数据集中无关的特征;一种是选择出有意义的特征。

我个人觉得选择出有意义的特征对后续的代码阅读会更方便一点。

pandas 对于 nan 的空值处理

  • df.dropna() 删除含有空值的行或列
  • df.fillna() 给缺失值赋予默认值
  • df.isnull() 判断是否是空值
  • df.isna() 判断是否是空值

还有许多情况存在不是 nan 的空值,比如示例数据集中对没有的数据值使用字符串 N 进行填充,也需要删除这些无效数据。

df = df[~df["线路总成本"].isin(["N"])]

怎么知道哪些数据值是占比少的呢?

使用 groupby 分组查看下特征数据的分布情况,对于 “是否续签” 等于 “续签ECP审批驳回或撤销” 的数据就可以考虑删除掉。

print(df.groupby(by="是否续签")["是否续签"].count())
    # 是否续签
    # 已分拨续签           8752
    # 续签ECP审批驳回或撤销       7
    # 非续签             5859

源代码:

def data_preview(df):
    """
    预览数据结构
    查看数据前10行、后10后
    :param df:
    :return:
    """
    if df.empty:
        raise("数据集为空")
    print(df.head())  # 默认返回前10行
    print(df.tail())  # 默认返回后10行
    print(df.columns)


def features_columns(df):
    """
    去除与定价无关的特征
    :param df:
    :return:
    """
    columns = ['总里程', '线路价格(不含税)', '调价比例', '线路指导价(不含税)', '实际到车时间', '交易成功时长', '是否续签',
               '交易成功时间', '交易成功日期', '成交对象', '车辆长度', '车辆吨位', '始发地省份名称',
               '实际靠车时间', '实际结束时间', '实际发车时间', '交易开始时间', '交易结束时间', '地区',
               '线路总成本', '需求紧急程度', '标的_创建时间', '标的_创建日期', "交易对象", "目的地省份名称",
               '计划发车时间', '计划靠车时间', '计划到达时间']

    df = df[columns]
    df = df[~df["线路总成本"].isin(["N"])]
    df = df[~df["线路价格(不含税)"].isin(["N"])]
    df = df[~df["线路指导价(不含税)"].isin(["N"])]
    df = df[~df["始发地省份名称"].isin(["N"])]
    df = df[~df["是否续签"].isin(["续签ECP审批驳回或撤销", "N"])]
    return df

1.3. 数据整合

存在多个特征合并表示一个具体特征含义的需要合并字段

例如:交易日期 + 交易时间 = 交易时间

这里要着重的讲一下为什么会有两种写法,如果你是个新手就认真看。

我们上面提到了数据的存储方式 会有excel、txt、csv等形式。

使用 pandas.read_excel 读取数据时,数据是有类型的,比如时间在 excel 中设置的时间格式,取出的是 datetime64 类型;

使用 xlrd.open_workbook 读取的结果也是不一样的,时间读取的结果可能浮点数;

使用 pandas.read_csv 读取数据时,取出的是 object 类型,可以当做字符串直接拼接的;

这就是为什么很多人 down 的代码不能正常运行的原因了。

源代码:

def merge_columns(df):
    """
    存在 多特征整合表示 一个具体特征含义的 需要合并字段,
    例如 交易日期 + 交易时间 = 交易时间
    :param df:
    :return:
    """
    if df["交易成功日期"].dtype == 'datetime64[ns]':
        df["交易成功时间"] = df.apply(lambda x: x["交易成功日期"].strftime("%Y-%m-%d ") + x["交易成功时间"].isoformat(), axis=1)
        df["标的创建时间"] = df.apply(lambda x: x["标的_创建日期"].strftime("%Y-%m-%d ") + x["标的_创建时间"].isoformat(), axis=1)
    # 对于 read_csv 交易成功日期 object 类型 交易成功时间 object 类型
    else:
        df["交易成功时间"] = df.apply(lambda x: x["交易成功日期"].split(" ")[0] + " " + x["交易成功时间"], axis=1)
        df["标的创建时间"] = df.apply(lambda x: x["标的_创建日期"].split(" ")[0] + " " + x["标的_创建时间"], axis=1)

    return df.drop(["交易成功日期", "标的_创建日期", "标的_创建时间", ], axis=1)

1.4. 数据分类编码

如交易对象 分为 B、C、BC,分别将其记为 1,2,3

源代码:

def encode_columns(df):
    """
    如交易对象 分为 B、C、BC,分别将其记为 1,2,3
    :param df:
    :return:
    """
    df["成交对象"] = df["成交对象"].map({"B": 1, "C": 2, "BC": 3})
    df["交易对象"] = df["交易对象"].map({"B": 1, "C": 2, "BC": 3})
    df["是否续签"] = df["是否续签"].map({"已分拨续签": 1, "非续签": 2})
    df["需求紧急程度"] = df["需求紧急程度"].map({"常规订单": 1, "特急订单": 2, "紧急订单": 3})

    # '计划发车时间', '计划靠车时间', '计划到达时间'
    if df["计划发车时间"].dtype == 'datetime64[ns]':
        pass
    else:
        df["计划发车时间"] = pd.to_datetime(df["计划发车时间"])
        df["计划靠车时间"] = pd.to_datetime(df["计划靠车时间"])
        df["计划到达时间"] = pd.to_datetime(df["计划到达时间"])

    df["计划发车时间"] = df["计划发车时间"].dt.hour
    df["计划靠车时间"] = df["计划靠车时间"].dt.hour
    df["计划到达时间"] = df["计划到达时间"].dt.hour
    df["计划发车时间"] = df["计划发车时间"].apply(lambda x: 1 if 8 <= x <= 20 else 0)
    df["计划靠车时间"] = df["计划靠车时间"].apply(lambda x: 1 if 8 <= x <= 20 else 0)
    df["计划到达时间"] = df["计划到达时间"].apply(lambda x: 1 if 8 <= x <= 20 else 0)

    df["省内/外"] = df.apply(lambda x: 1 if x["始发地省份名称"] == x["目的地省份名称"] else 0, axis=1)
    return df.drop(["始发地省份名称", "目的地省份名称", ], axis=1)

1.5. 清洗后的数据存储

存储到新的文件中

df.to_csv(f"car_new.csv", index=False, encoding="utf-8")

2、相关性分析

2.1. 统计学之三大相关性系数

由于统计使用到相关系数比较频繁,先简单介绍一下这些系数。

相关系数:考察两个事物(在数据里我们称之为变量)之间的相关程度。

如果有两个变量:X、Y,最终计算出的相关系数的含义可以有如下理解:

  • 1、当相关系数为0时,X和Y两变量无关系。
  • 2、当X的值增大/减小,Y值增大/减小,两个变量为正相关,相关系数在0.00与1.00之间。
  • 3、当X的值增大/减小,Y值减小/增大,两个变量为负相关,相关系数在-1.00与0.00之间。

相关系数的绝对值越大,相关性越强,相关系数越接近于1或-1,相关度越强,相关系数越接近于0,相关度越弱。

通常情况下通过以下取值范围判断变量的相关强度:

分数 相关性
0.8-1.0 极强相关
0.6-0.8 强相关
0.4-0.6 中等程度相关
0.2-0.4 弱相关
0.0-0.2 极弱相关或无相关

2.2. 统计学之三大相关性系数

Pearson皮尔逊相关系、Spearman秩相关系数、kendall等级相关系数

2.2.1. pearson

当两个变量的标准差都不为零时,相关系数才有定义,皮尔逊相关系数

适用范围:

  • 两个变量之间是线性关系,都是连续数据。
  • 两个变量的总体是正态分布,或接近正态的单峰分布。
  • 两个变量的观测值是成对的,每对观测值之间相互独立。

2.2.2. spearman

等级相关系数用来估计两个变量X、Y之间的相关性,其中变量间的相关性可以使用单调函数来描述。

适用范围:斯皮尔曼等级相关系数对数据条件的要求没有皮尔逊相关系数严格,只要两个变量的观测值是成对的等级评定资料,或者是由连续变量观测资料转化得到的等级资料,不论两个变量的总体分布形态、样本容量的大小如何,都可以用斯皮尔曼等级相关系数来进行研究。

2.2.3. kendall

是一个用来测量两个随机变量相关性的统计值

2.2 pandas 相关系数 DataFrame.corr 函数

要在Python完成三种相关性分析只需要调用 corr 函数就可以实现,参数说明:

  • method:可选值为 pearson、kendall、spearman
  • pearson:相关系数来衡量两个数据集合是否在一条线上面,即针对线性数据的相关系数计算,针对非线性数据便会有误差
  • kendall:用于反映分类变量相关性的指标,即针对无序序列的相关系数,非正态分布的数据
  • spearman:非线性的,非正态分析的数据的相关系数
  • min_periods:样本最少的数据量
  • 返回值:各类型之间的相关系数DataFrame表格

2.3 相关信息数据可视化

源代码:

df = pd.read_csv("car_new.csv")
score = df.corr(method="spearman")
plt.rcParams["font.sans-serif"] = ["SimHei"]
plt.rcParams["axes.unicode_minus"] = False
plt.figure(figsize=(10, 8))
plt.title("各因素与线路间隔的相关性")
plt.barh(score.columns, score["线路价格"])
plt.show()

数据分析小白入门篇,MathorCup 无车承运人平台线路定价问题,特征间的相关性分析

推荐阅读: