@tianxingjian
2018-09-07T19:31:18.000000Z
字数 32166
阅读 1171
本文是针对“拍照赚钱”APP任务定价问题进行分析,并采用多维度空间任务定价模型、会员抉择概率模型、项目总收益模型、入包威胁矩阵模型等对不同情况下的任务进行合理定价。
针对问题一:首先对使用Geoq平台对任务以及会员数据进行定位,在这些数据中发现有部分的异常数据。在对异常数据进行删除处理之后使用任务-会员空间分布模型对其进行合理分配,从而进行数据整合操作,进而使用Python中的Matplotlib工具绘制多功能图来分析任务定价的规律以及部分任务未完成的原因,最终对任务定价数据进行指数拟合。
针对问题二:在该问题中需要制定新的任务定价方案并与原方案进行比较。在完成任务一的前提下,我们首先使用Matlab工具并结合矩阵相关知识对任务-会员进行再次空间分配,之后使用Z-Score对数据进行标准化,进而逐步构建多维度空间任务定价模型、护院抉择概率模型、项目总收益模型来制定新的方案。我们得到结论:任务完成度提高了 ,项目实际收益提高了 ,总值为 。
针对问题三:在考虑对任务进行打包的情况下,我们考虑到了会员信誉值和预定任务限额对任务定价的影响,对此构建入包威胁矩阵模型来对各项任务进行打包并将多维度空间任务定价模型进行有话啊,最终基于打包方案的优化模型制定新的方案,求解后得到结论:任务完成率再次提高了 ,收益率提高了 ,实际收益总值提高至 。
针对问题四:
关键词: 拟合、Geoq、Z-Score、Python、Matlab、入包威胁。
随着互联网领域的快速发展以及人们对科学技术的不断创新,最近一款称为“拍照赚钱”的服务模式正在逐渐融入到人们的生活中。“拍照赚钱”是移动互联网下的一种新型的自助式服务模式。用户通过下载APP并且注册成为APP的会员之后,就可以在APP平台上领取需要拍照的任务(比如上超市去检查某种商品的上架情况),进而赚取APP对任务所标定的酬金。而这种基于移动互联网的自助式劳务众包平台相对于传统的市场调查方式来说,不仅为企业提供各种商业检查和信息搜集渠道,而且有效地保证了调查数据真实性,缩短了调查的周期,进而可以大大节省人力、物力、财力。因此APP成为该平台运行的核心,而会员接受任务的可能性主要由任务价格决定,故APP中的任务定价是核心要素,而且对于公司的来说起到一个决定性作用。如果任务定价不合理,有的任务就会无人问津,而导致商品检查的失败。相反,若能通过以往数据来对其进行合理性的建模从而确定任务的定价则会取得不凡的结果。
建模命题组给出的附件一是一个已结束项目的任务数据,其中包含了每个任务的所在地的经纬度、任务的标价以及任务的完成情况(“1”表示完成,“0”表示未完成);附件二则是会员的信息数据,其中包含了会员所在地的经纬度、预定任务的限额、预定任务的时间和会员所对应的信誉值,而信誉值往往是通过以往的任务完成情况所直接决定的,所以理论上说会员对应的信誉值越大,越优先开始挑选任务,其配额也就越大(任务分配时实际上是根据预订限额所占比例进行配发);附件三是一个新的检查项目任务数据,其中只有任务所在地的经纬度数据,我们要做的是通过给出的数据来建立一个或若干个模型,进而将此模型应用在这些新项目上从而给平台公司提供一个比较优秀的决策。
APP平台中任务定价的设计问题就是通过已完成任务的数据来合理有效的预测未完成任务的执行情况,并使得最终所有任务完成的总价格尽可能大,从而实现APP平台的最大价值率。对此,首先我们需要通过附录一和附录二中的数据来确定影响任务定价的可能相关因素以及分析最终未完成任务的可能原因,以便为后期的建模提供较优的铺垫;然后,在问题二中,我们需要在完成问题一的情况下,对该项目的任务设计一个新的定价方案并建立较优的数学模型,然后可以通过此模型来比较该新的设计方案与原方案的完成情况,进而总结改进之后方案的优点;此外,在问题三中,命题组提到多个集中的任务可能会对用户的选择造成影响,所以我们需要对之前的设计方案进一步通过将这些集中任务进行联合打包发布的方式来对模型进行优化,进而分析最终任务的完成情况;最后在问题四中,我们需要对通过前三问得出的模型来对附录三中的待定任务进行估价并且评价方案的实施效果。
常言有道:具体问题具体分析。对于问题一来讲,我们可以将任务定价的影响因素归于两个方面,一种是已知提供的因素,另一种则是未知潜在的因素。在未知潜在的因素中有任务本身所对应的初始价值以及公司发布任务所在时间点的经营状况等方面因素决定,然而这些因素所决定的条件及信息我们无从得知,因此在这里我们在该论文中不做分析。而在已知提供的因素中,我们主要可以从命题组给出的附录中出发。在给出的附录一和附录二中,我们可以发现其中具有分析价值的数据,对此我们将影响任务的定价初步归因于任务的地理位置、会员所在地的集中程度、任务发布时间以及任务发布地的经济状况四个因素,后期我们也是从这四个因素出发来确定四个因素与任务定价的真正关系。而任务未完成的原因主要从任务定价的合理性、任务的竞争力两个方向进行分析。
“拍照赚钱”APP的任务定价是一种任务发布者(APP平台)与任务接受者(平台会员)之间所进行的互利模式,PP平台希望在成本一定的情况下使得任务的完成率尽可能的提高,而APP会员则需要在满足某种条件要求下追求收益尽可能大。从实际出发,一份任务理论上可以分配给多个使其相互竞争,而一个会员亦可接受多份任务,这两者在初始阶段是一种“多对多”的关系。而最终的情况是一个任务只能分配给一个会员,而一个会员依然可以接受多份任务,这是一种“多对一”的关系。
在本问中,提到实际情况下多个任务可能会因为位置比较集中而导致用户会争相选择,为此我们需要采用一种打包方式来对原方案进行优化。在采用任务打包方案之后之后虽然能够提高平台的任务发布效率,但是我们必须首先解决以下两个问题:
制订一个合理的方案来对任务进行打包从而进一步提高任务的完成率,即每个打包任务簇中的任务数需要确定。
任务簇中的任务数的确定由哪些因素决定
符号 | 说明 |
---|---|
任务 与会员 的距离,单位千米(km) | |
地球的半径,单位千米(km) | |
任务 的定价 | |
某一城市的GDP所对应的经济系数 | |
会员的信誉值 | |
任务 领域内的会员密度 | |
任务 领域内会员的竞争度 | |
会员 预定任务的限额 | |
任务 的经度值 | |
任务 的纬度值 | |
会员 的经度值 | |
会员 的纬度值 |
说明:表中未提到的符号会在正文中作出解释。
首先我们需要对所有数据中的经纬度信息来进行定位,以便于我们后续的相关分析。在这里,我们选择使用智能GeoQ平台来对附录一和附录二中的数据进行定位以便我们后续的分析,一下是定位之后得到的结果图示:
从定位之后的图示来看,我们可以不难发现以下两个结论:1.该数据主要收集于广东省的佛山市、广州市、东莞市、深圳市四个城市,后续我们也是针对这四个地区进行分析。2.这些数据中含有特殊值,这里我们将其视为异常值,需要进行特别处理。
从我们所总结的因素出发,由于四个因素所在不同的附录之中,所以我们需要将这两个附录文件进行整合。首先通过附录一中的经纬度数据来确定任务的发布位置,然后由Python中的pandas和numpy两个工具包来对两个附录中的数据进行整合,进而完成该数据的预处理模块。
在附件一中,其内含有任务的相关经纬度数据,由于我们在分析任务定价的过程中,将任务发布的地理位置作为主要因素之一,故我们需要确定发布任务的位置与任务所在城市中心之间的距离,以此来具体确认任务定价与任务地理位置的关系。
我们要想计算出任务点与中心位置的距离,则首先需要知道中心位置的经纬度。而由上述的数据定位可知,所有数据整体在佛山、广州、深圳、东莞四个城市中,据资料显示,这是个城市中心的经纬度如下所示:
城市 | 经度 | 纬度 |
---|---|---|
广州 | 113.27 | 23.13 |
佛山 | 113.12 | 23.02 |
深圳 | 114.05 | 22.55 |
东莞 | 113.75 | 23.05 |
既然现在已知城市中心以及所有任务点的经纬度信息,我们即可通过以下式子计算出所有任务点与中心之间的距离:
符号 | 说明 |
---|---|
任务 与城市中心 的距离,单位千米(km) | |
地球的半径,单位千米(km) | |
任务 的经度值 | |
任务 的纬度值 | |
城市 的经度值 | |
城市 的纬度值 |
为此,我们就能由以下相关程序处理附件一中的数据:
def append_length(file_path, jing_value2, wei_value2, file_name, encoding):
df1 = pd.read_csv(file_path, encoding=encoding)
length = []
for index1, each_data1 in df1.iterrows():
jing_value1 = each_data1["任务gps经度"]
wei_value1 = each_data1["任务gps 纬度"]
distance = int(6371 * np.arccos(np.sin(wei_value1)*np.sin(wei_value2)+np.cos(wei_value1)*np.cos(wei_value2)*np.cos(jing_value1-jing_value2)))
length.append(distance)
df1["与中心距离"] = length
df1.to_csv(file_name, index=False)
在附录二中的会员数据里,我们发现其中的经纬度数据信息在同一字段中,为了分析的方便,我们首先使用pandas将该添加对应的经度和纬度两个字段,相关核心代码如下,完整代码可见附录:
def deal_column(data_path, sheet):
data = pd.ExcelFile(data_path)
df = data.parse(sheet)
locations = df["会员位置(GPS)"]
location_jing = [location.split(" ")[1] for location in locations]
location_wei = [location.split(" ")[0] for location in locations]
df["会员gps经度"] = location_jing
df["会员gps纬度"] = location_wei
df.to_csv(d + "/after_deal.csv", index=False)
处理之后的部分数据如下图所示:
在上述操作中,我们发现附录一和附录二中含有异常值,我们需要针对进行异常值进行特殊处理,否则将会对我们后面的模型造成影响。要想具体确认每个经纬度所对应的具体位置,我们需要借助于GPSspg的相关操作。为此,我们首先将附件一和附件二中的各个位置的经纬度提取出来然后进行具体定位,最后导入到新的文件中,具体定位后的部分数据如下图所示:
通过文件中的数据可以发现,除了上述分析的佛山市、深圳市、广州市、东莞市之外还掺杂了其他地区的数据,例如清远市、中山市、别行政、惠州市等。经统计发现总共含有20个异常数据,它们的编号分别是A0297、A0751、B0005、B0006、B0007、B0022、B0033、B0039、B0048、B0082、B0135、B0136、B0472、B1077、B1585、B1627、B1708、B1727、B1820、B1822。它们的编号和具体数据内容可如图五、图六所示:
为了不影响后面模型的精度,这里我们将这些异常数据进行直接进行删除即可,删除的部分核心代码如下所示:
def delete_data(data_path, sheet):
df = pd.read_csv(data_path)
groups = df.groupby("城市")
error_group = pd.DataFrame(columns=["会员编号","会员位置(GPS)","预订任务限额","预订任务开始时间","信誉值","会员gps经度","会员gps纬度","详细地址","城市"])
true_group = pd.DataFrame(columns=["会员编号","会员位置(GPS)","预订任务限额","预订任务开始时间","信誉值","会员gps经度","会员gps纬度","详细地址","城市"])
for city, group in groups:
if city not in ["佛山市"、"广州市"、"深圳市"、"东莞市"]:
error_group = pd.concat([error_group, group])
else:
true_group = pd.concat([true_group, group])
error_group.to_csv("error_data.csv", index=False)
true_group.to_csv("true_data.csv", index=False)
由之前对任务定价的分析可知,我们需要将会员数据与任务数据进行整合,从而进一步分析公司在对任务定价的时候是否考虑到会员的相关因素。在此,我们将两个附件的数据进行整合操作的依据是在一定区域范围之内所含有的会员数目,并以此求相关会员信息的均值来对定价进行分析。我们可以通过以下公式进行计算:
符号 | 说明 |
---|---|
任务 与会员 的距离,单位千米(km) | |
地球的半径,单位千米(km) | |
任务 的经度值 | |
任务 的纬度值 | |
会员 的经度值 | |
会员 的纬度值 |
通过上述距离公式,我们可以借助numpy工具计算每个任务在与会员之间的距离是否在我们规定的范围之内,从而将附件二中的有效数据合并到附件一中,即在附件一中添加四个字段:会员预定任务的限额、会员所在地的集中程度、任务发布时间以及任务发布地的经济状况。其中前三个因素可以在附录一中进行算法提取,而为了分析问题的全面性,这里多考虑到了任务发布地区的经济状况因素,虽然在本题中并未给出该方面的数据,但是我们可以通过查找相关资料进行了解从而进行综合性分析。
通过上述的分析,我们在这规定若任务与会员之间的距离小于 ,则我们将该会员的数据录入到对应的任务中,即:
结合实际情况与各个数据的经纬度的联系,我们在对数据进行整合的时候取 。由于该部分的核心代码较长,出于对论文可读性的考虑,此部分的代码不在此处进行展示,读者可在附录中进行浏览。整合之后的部分相关数据如下:
为了分析出任务点位置对任务定价的具体影响,我们需要将上述预处理之后的附件一中的数据进行可视化从而得到任务位置对任务定价的影响。为此我们将任务 定为 ,将任务点与城市中心的距离定为 ,通过这两个数据便可绘制两者之间的关系图。此处,我们不妨使用散点图将每个数据点在直角坐标系中表示出来,从而分析任务定价随着任务位置之间的大致趋势,进而选择恰当的函数来对数据点进行拟合。除此之外,为了避免城市的不同而对任务价格的影响,我们分别绘制广州、佛山、深圳、东莞的定价位置散点图。基于这种分析,我们使用matplotlib工具绘制出的散点图以及程序的核心代码如下:
def draw_scatter(file_path, color, marker, city, image_path):
df = pd.read_csv(file_path)
p = df["任务标价"]
d = df["与中心距离"]
plt.scatter(d/100, p, s=10, c=color, alpha=0.6, marker=marker)
plt.title("{}任务位置与定价的关系".format(city), fontproperties=font)
plt.xlabel("Distance/x100km")
plt.ylabel("Price")
plt.savefig(image_path)
plt.show()
从上面的任务定价与任务位置之间的散点图中不难发现,深圳、广州、东莞、佛山整体呈现一种 "上疏下密,右疏左密"的形式。故于此,我们可以得到以下结论:距市中心越进的任务,APP平台对其任务定价相对比较低,定价在65~75之间;而距市中心越远的任务,APP平台对其任务定价相对较高,定价在76~86之间。此外,随着距离的增大,定价的价格增长的幅度整体在逐渐增大。对此,我们将这两种模型近似拟合为一个指数回归模型。
所以,根据上面所述,我们不妨建立一个关于任务点位置对任务定价的影响的模型,为了模型进行的精确性以及拟合效果的良好度,这里我们利用WPSExcel中的散点指数拟合模型进行拟合,且该模型满足以下关系式:
至此,我们已经求解出任务位置与定价的数学模型,而为了确认该模型的拟合程度,我们需要引入拟合优度值 来进一步验证该模型。
通过科学计算可得知拟合优度 =0.7317295,该值近似为1可知,该模型的拟合具有比较好的效果。于此,我们可以确认任务的位置与任务的定价存在较大的关系,即距市中心越进的任务,APP平台对其任务定价相对比较低,定价在65~75之间;而距市中心越远的任务,APP平台对其任务定价相对较高,定价在76~86之间。
通过结合现实来讲,存在这种定价规律主要存在以下原因:
随后,通过我们上面所描述,任务点的经纬度与城市中心满足以下关系:
所以,最终我们将任务的定价与任务的经纬度的关系模型定义如下:
除了任务距城市中心的距离对任务的定价造成影响之外,城市的经济状况也会对其产生一定的影响。而为分析此种影响,我们需要对以上所述的四个城市的经济状况有所了解。据资料显示,四个城市的GDP以及变化率如下所示:
城市 | GDP | 变化情况 |
---|---|---|
深圳 | 19492.60亿 | 增长:9.38% |
广州 | 19610.94亿 | 增长:8.34% |
佛山 | 8003.92亿 | 增长:7.16% |
东莞 | 6275.06亿 | 增长:6.69% |
之后,我们通过matplotlib来绘制四个城市的任务平均价格关系图,相关程序及图示结果如下所示:
def get_mean_price(file_path):
df = pd.read_csv(file_path)
p = df["任务标价"]
mean_price = np.mean(p)
return mean_price
if __name__ == "__main__":
files = [parent_path + "/newdata/cleaneddata/final_data1_sz.csv", parent_path + "/newdata/cleaneddata/final_data1_gz.csv", parent_path + "/newdata/cleaneddata/final_data1_fs.csv", parent_path + "/newdata/cleaneddata/final_data1_dg.csv"]
price_ls = [get_mean_price(each_file) for each_file in files]
plt.barh(np.arange(4), price_ls, height=0.7, color="lightskyblue")
plt.xlabel("Price")
plt.ylabel("Cities")
plt.title("城市与任务定价的关系", fontproperties=font)
plt.yticks(np.arange(4), ["深圳", "广州", "佛山", "东莞"], rotation=45, size=8)
for x, value in enumerate(price_ls):
plt.text(y=x, x=value, s="{}".format(value), ha="center", )
plt.savefig(parent_path + "/images/city_price.jpg")
plt.show()
从图中我们可以了解到,东莞与佛山的任务定价相对较高,而广州和深圳的任务定价相对较低。这与上述各个城市的经济状况正好呈现一种相反的趋势,这种趋势理解为城市的经济状况与任务定价成负相关。
从实际出发,公司在给任务进行定价的时候,一般会考虑到一定区域内会员的分布密度,且在分布密度较小的区域内公司会提高任务的定价,以此来吸引更多的会员,从而提高APP平台访问的用户量,进而使公司的利益最大化。为此,我们同样需要由整合之后的数据来分析会员集中度对任务定价的影响,最终实现的效果图如下所示:
由上图我们可以看出,结果与我们料想的如出一辙。会员的集中度与任务的定价整体存在某种负相关关系。
在进行分析任务未完成原因分析时,首先我们需要对任务未完成的数据进行处理,该处理部分我们主要通过任务未完成任务数据的提取来进行。相关处理核心代码如下:
def split_yes_no(file_name1, file_name2):
df = pd.read_csv(data_path6, encoding="gbk")
for status, group in df.groupby("任务执行情况"):
if status == 0:
group.to_csv(file_name1, index=False)
else:
group.to_csv(file_name2, index=False)
在分解之后,我们发现未完成的任务数据有313组,后面我们将针对这313组的数据进行原因分析。
首先,我们不难发现,若公司对一组任务的定价过低,则定价很大可能不足以引起会员的接单欲望。所以,任务的定价过低很有可能是造成任务未完成的原因之一。
通过对数据的观察我们可以发现,任务的定价皆在65~85之间。为此,我们将任务分为四个定价区间,即65~69、70~74、75~79、80~85四个数据区域。通过此分析,我们可以绘制数据的散点图以及他们的相关统计图,相关核心代码以及结果图示如下:
def draw_no_scatter():
df = pd.read_csv(parent_path + "/newdata/no.csv")
plt.scatter(np.arange(313), df["任务标价"], s=20, c="red", alpha=0.6, marker="*")
plt.title("任务未完成数据的价格分布")
plt.xlabel("Mission")
plt.ylabel("Price")
plt.savefig(parent_path + "/images/no_price.jpg")
plt.show()
def draw_no_bar():
df = pd.read_csv(parent_path + "/newdata/no.csv")
df1 = df[(65 <= df["任务标价"]) & (df["任务标价"] < 70)]
df2 = df[(70 <= df["任务标价"]) & (df["任务标价"] < 75)]
df3 = df[(75 <= df["任务标价"]) & (df["任务标价"] < 80)]
df4 = df[(80 <= df["任务标价"]) & (df["任务标价"] < 85)]
data_len = [len(df1), len(df2), len(df3), len(df4)]
plt.barh(np.arange(4), data_len, height=0.6, alpha=0.7, color="yellowgreen")
plt.ylabel("Price Region")
plt.xlabel("Num")
plt.title("未完成价格条形图")
plt.yticks(np.arange(4), ["65-69", "70-74", "75-79", "80-85"], rotation=45, size=13)
for x, value in enumerate(data_len):
plt.text(y=x, x=value, s="{}".format(value), ha="center")
plt.savefig(parent_path+"/images/price_bar.jpg")
plt.show()
plt.pie(x=data_len, labels=["65-69", "70-74", "75-79", "80-85"], autopct="%.0f%%", shadow=True, radius=1.2)
plt.legend(["65-69", "70-74", "75-79", "80-85"], loc="best")
plt.title("未完成价格饼图")
plt.savefig(parent_path+"/images/price_pie.jpg")
plt.show()
通过以上图的显示,我们在散点图中可以明显的看出任务定价在65-75的区间内明显占据绝大部分。从条形图和饼图中我们也能看出其所占比例之大。为此,我们可以确定之前的结论,即公司对任务定价过低的时候,不足以勾起会员完成任务的欲望,也就是说会员完成任务的可能性极小。
在原因一的分析中,除了任务定价在65-75之间之外,还有23组的任务的定价相对来说是比较高的。那么既然如此,会员未完成任务的原因是什么呢?为此,我们在这需要分析任务是否离城市中心的距离较远,从而导致会员完成任务的难度增大。
通过以上模块的分析,我们得出公司对任务定价主要从任务点位置、任务发布地的经济状况、会员预定任务限额、信誉值、集中度所决定。而任务未完成的原因主要是任务的定价较低以及任务较远导致。
“拍照赚钱”APP的任务定价是一种任务发布者(APP平台)与任务接受者(平台会员)之间所进行的互利模式,PP平台希望在成本一定的情况下使得任务的完成率尽可能的提高,而APP会员则需要在满足某种条件要求下追求收益尽可能大。在问题二中,要求为项目设计新的任务定价方案,则需要考虑以下几个方面:
除此之外,从实际出发,一份任务理论上可以分配给多个使其相互竞争,而一个会员亦可接受多份任务,这两者在初始阶段是一种“多对多”的关系。而最终的情况是一个任务只能分配给一个会员,而一个会员依然可以接受多份任务,这是一种“多对一”的关系。通过以上对该问题的分析,我们可以构建项目-会员-任务之间的关系图:
通过上述的分析,对此本文主要从以下四个步骤进行解决该问题:
Step1. 确定任务-会员空间分配算法
Step2. 由问题一分析出的多因素对任务定价造成的影响,建立出多维度空间任务定价模型。
Step3. 对任务完成度进行分析并构建会员抉择概率模型。
Step4. 通过上述模型的构建最终确定能够体现出新方案效果的项目总收益模型。
Step5. 使用Matlab、Python、Lingo三种编程语言来对模型进行求解并总结新方案的优势。
首先,我们需要一个算法来对任务、会员之间进行合理的分配,以便后续模型的构建。出于体现出全国大学生数学建模竞赛的创新性,以下给出该任务-会员空间分配算法的实现流程图:
任务-会员距离矩阵:
用 表示第 个任务, 表示第 个会员。以任务为节点,任务与每位会员之间的距离为对应的权重,为此构建该城市的任务-会员距离矩阵 ,如下所示:
通过问题一的分析,我们已经知道了多因素对任务定价产生的影响规律,主要体现在以下多个方面:
在上一模块中,我们已经通过任务-会员空间分配模型分别对任务分配了相对应的会员量,并将其距离数据存储于矩阵 中,但为了避免影响其他因素之间复杂的相关性以及给模型求解带来一定的难度,我们首先需要使用 模型对矩阵中数据进行归一化处理:
同理,我们可以对矩阵中所对应会员的信誉值做归一化处理。通过上述分析,为此,我们可以构建多维度空间任务定价模型:
其中, 表示该任务点所在领域内会员信誉期望值, 表示任务点所在领域内会员位置期望值,表示任务所在城市的经济系数, 表示任务所在领域内会员密度值, 表示价格与多维度因素之间的比例系数, 表示该方案的容错误差值。
同时,我们可以通过上述四个城市的GDP(国内生产总值)来确定该模型的经济系数 :
城市 | 深圳 | 广东 | 佛山 | 东莞 |
---|---|---|---|---|
1.949260 | 1.961094 | 0.800392 | 0.627506 |
最终,确定的多维度空间任务定价模型如下:
同样,在上述的分析中,我们已经把影响任务完成度的因素归结于任务的定价、任务位置、任务竞争度。且这三种因素的影响如下:
首先,任务完成与否是由会员对任务所能获得期待的效益决定,当该效益值达到一定的阈值时,我们可以认为该任务被领域内的会员完成的概率较高,对此,我们应该建立该任务-会员吸引度模型:
其中, 表示任务的竞争度,在这里我们可以将上述任务-会员矩阵中每行的会员数。根据实际情况,我们认为若一个任务的定价小于70,归一化后的距离大于0.8,被相中的会员数小于3,我们任务该任务完成的概率为0,其所对应的任务对会员的吸引度为 ;同样若某个任务的定价大于80,归一化后的距离小于0.2,被相中的会员数大于8,我们认为该任务被完成的概率为1,其所对应的任务吸引度为 。根据上述信息,我们可以确定 的值:
经计算,。对此,我们可以确定任务-会员吸引度模型最终为:
根据任务-会员吸引度模型,我们可以建立出会员抉择概率模型:
我们已经分析得到项目的总收益是由任务定价以及任务的完成情况而定,而在上面我们确定了任务多维度空间任务定价模型和会员抉择概率模型,由此,我们可以构建出项目总收益模型,以分析出新方案的实施效果,项目的总收益根据会员抉择概率模型可分为三个部分:不可能收益、绝对收益、概率收益。其一般式如下:
之后,将上述的任务多维度空间定价模型和会员抉择概率模型代入其中,可最终确定项目总收益模型:
首先,我们需要通过上述的任务-会员分配算法对任务、会员之间进行分配,部分程序代码和分配结果如下:
[a, b] = size(data1_dg_jing);
[c, d] = size(data2_dg_jing);
mt_data = zeros(a,c);
for i = 1:a
for j = 1:c
mt_data(i,j) = 6371*acos(sin(data1_dg_wei(i))*sin(data2_dg_wei(j))+cos(data1_dg_wei(i))*cos(data2_dg_wei(j))*cos(data1_dg_jing(i)-data2_dg_jing(j)));
end
end
normalized_data = zscore(mt_data);
for i = 1:a
for j = 1:c
if normalized_data(i,j) > 0
normalized_data(i,j) = 0;
end
end
end
通过上述对任务-会员进行分配之后,我们可以使用Matlab进行网格搜索来确定各个需要的系数值,最终我们可以将多维度空间任务定价模型定义如下:
而会员抉择概率模型我们在上面已经进行分析,其与任务定价、距离、竞争有关,对此我们同样可以确定最终的会员抉择概率模型并进一步求解项目总收益模型:
其对应的优化模型如下:
对于该最优化模型,我们使用Lingo对其进行求解,之后使用新方案对附件一中的任务数据进行处理,最终获得的结果部分如下所示:
通过上述的模型构建以及模型求解,我们制订了任务定价的新方案,而且对于新方案,其优势可以由以下几个方面总结:
在任务完成方面,原方案中含有313个未完成任务,而在使用新方案之后任务未完成数减少到213个,其任务完成率从 提高到了 ,新方案对任务完成情况的优化程度为 。
在项目实际收益方面,原方案的实际收益为 ,新方案的实际收益为 ,收益率提高了 。
在项目的理想收益方面,原方案该项目的理想收益为 ,而在使用了新方案之后,该项目的理想收益是 。
综上所述,我们所制订的新方案一方面能够让APP平台的收益有个较大的提高,另一方面同时能够使会员的收入有所增多,即在APP平台和会员之间实现了双赢的效果,在双方获利的同时还有利于之间的长期合作。
在本问中,提到实际情况下多个任务可能会因为位置比较集中而导致用户会争相选择,为此我们需要采用一种打包方式来对原方案进行优化。在采用任务打包方案之后之后虽然能够提高平台的任务发布效率,但是我们必须首先解决以下两个问题:
制订一个合理的方案来对任务进行打包从而进一步提高任务的完成率,即每个打包任务簇中的任务数需要确定。
任务簇中的任务数的确定由哪些因素决定
在问题二的多维度空间任务定价模型、会员抉择概率模型、项目收益模型中,他们之间是一种环环相扣的关系。而打包任务定价模型是建立在多位度空间任务定价模型的基础上的,所以我们优化的主要模型是多维度空间任务定价模型,继而改变打包任务的定价来体现出优化方案的优势。
1) 会员信誉值及预定任务限额分析
结合实际情况,从APP平台的角度分析,打包任务的任务数是由该打包区域中的所有会员的信誉值和预定任务的限额所决定。为此,我们需要进一步分析会员信誉值和预定任务限额的分布规律。
要想了解会员预定任务的限额以及信誉值对任务定价的影响,我们首先需要用matplotlib绘图工具分别绘制以下两种因素对定价的影响:
由上述图示我们可以知道,数据点主要集中在图示的左下端部分,即在会员信誉值及预定任务额较低的时候,任务的定价普遍较低;反之,任务定价相对较高。
为了避免影响其他因素之间复杂的相关性以及给模型求解带来一定的难度,我们首先需要对会员信誉值 和预定任务限额 进行标准化处理:
通过统计我们发现,会员信誉值和预定任务限额呈现一种“中间高,两头低”的特征。对此,我们认为会员的信誉值和预定任务限额满足二维高斯分布,其概率密度函数如下所示:
其中, 都是常数,我们把该分布记作 。
2)打包方案任务定价模型
在确定了会员信誉值和预定任务限额的分布以及对任务定价影响规律之后,我们需要建立打包方案的任务定价模型。当概率 达到一定值时,我们可以认为该会员对任务的影响较大,即某任务簇中的任务数较多,入包的可能性较大,对此,我们先建立一个任务入包威胁矩阵模型
其中, 表示最终打包的任务簇数, 表示附件二中的任务数,而 表示该任务簇中是否包含了 ,在这里为0-1值,具体表示如下:
最终,确定的打包任务定价模型以及完成概率模型为:
同问题二,我们最终可以求解出打包方案的实际收益总值为 ,未完成的任务数为 个,项目的理想收益为 。相对问题二中未使用打包方案而言,任务完成率提高了 ,收益率提高了 。
通过上述结果显示,我们可以得出结论:制订打包方案在一定程度上不仅能够提高APP平台任务的效率,同时还能够提高自身的效益。
[1]潘斌,于晶贤,衣娜. 数学建模教程[M]. 化学工业出版社,2017.
[2]韩中庚. 数学建模方法及其应用[M]. 高等教育出版社,2017.
import pandas as pd
import numpy as np
from matplotlib import pyplot as plt
import os
import numpy as np
d = os.path.dirname(__file__)
parent_path = os.path.dirname(d)
data_path1 = parent_path + "/problems/附件一:已结束项目任务数据.xls"
data_path2 = parent_path + "/problems/附件二:会员信息数据.xls"
data_path3 = parent_path + "/newdata/after_deal.csv"
data_path4 = parent_path + "/newdata/data1.csv"
data_path5 = parent_path + "/newdata/data2.csv"
data_path6 = parent_path + "/newdata/true_data1.csv"
data_path7 = parent_path + "/newdata/true_data2.csv"
def deal_column(data_path, sheet):
data = pd.ExcelFile(data_path)
df = data.parse(sheet)
locations = df["会员位置(GPS)"]
location_jing = [location.split(" ")[1] for location in locations]
location_wei = [location.split(" ")[0] for location in locations]
df["会员gps经度"] = location_jing
df["会员gps纬度"] = location_wei
df.to_csv(d + "/after_deal.csv", index=False)
def create_city1(data_path, path_name):
df = pd.ExcelFile(data_path).parse("t_tasklaunch")
cities = [each["详细地址"][3:6] for index, each in df.iterrows()]
df["城市"] = cities
df.to_csv(path_name, index=False)
def create_city2(data_path, path_name):
df = pd.read_csv(data_path, encoding="gbk")
cities = [each["详细地址"][3:6] for index, each in df.iterrows()]
df["城市"] = cities
df.to_csv(path_name, index=False)
def delete_data1(data_path, error_file, true_file):
df = pd.read_csv(data_path, encoding="gbk")
groups = df.groupby("城市")
error_group = pd.DataFrame(columns=["任务号码","任务gps 纬度","任务gps经度","任务标价","任务执行情况","详细地址","城市"])
true_group = pd.DataFrame(columns=["任务号码","任务gps 纬度","任务gps经度","任务标价","任务执行情况","详细地址","城市"])
for city, group in groups:
if city not in ["佛山市","广州市","深圳市","东莞市"]:
error_group = pd.concat([error_group, group])
else:
true_group = pd.concat([true_group, group])
error_group.to_csv(error_file, index=False)
true_group.to_csv(true_file, index=False)
def delete_data2(data_path, error_file, true_file):
df = pd.read_csv(data_path)
groups = df.groupby("城市")
error_group = pd.DataFrame(columns=["会员编号","会员位置(GPS)","预订任务限额","预订任务开始时间","信誉值","会员gps经度","会员gps纬度","详细地址","城市"])
true_group = pd.DataFrame(columns=["会员编号","会员位置(GPS)","预订任务限额","预订任务开始时间","信誉值","会员gps经度","会员gps纬度","详细地址","城市"])
for city, group in groups:
if city not in ["佛山市","广州市","深圳市","东莞市"]:
error_group = pd.concat([error_group, group])
else:
true_group = pd.concat([true_group, group])
error_group.to_csv(error_file, index=False)
true_group.to_csv(true_file, index=False)
# delete_data1(data_path4, parent_path+"/newdata/error_data1.csv", parent_path+"/newdata/true_data1.csv")
def append_length(file_path, jing_value2, wei_value2, file_name, encoding):
df1 = pd.read_csv(file_path, encoding=encoding)
length = []
for index1, each_data1 in df1.iterrows():
jing_value1 = each_data1["任务gps经度"]
wei_value1 = each_data1["任务gps 纬度"]
distance = int(6371 * np.arccos(np.sin(wei_value1)*np.sin(wei_value2)+np.cos(wei_value1)*np.cos(wei_value2)*np.cos(jing_value1-jing_value2)))
length.append(distance)
df1["与中心距离"] = length
df1.to_csv(file_name, index=False)
# append_length(parent_path + "/newdata/cleaneddata/data1_gz.csv", 113.27, 23.13, parent_path + "/newdata/cleaneddata/final_data1_gz.csv", "gbk")
# append_length(parent_path + "/newdata/cleaneddata/data1_fs.csv", 113.12, 23.02, parent_path + "/newdata/cleaneddata/final_data1_fs.csv", "gbk")
# append_length(parent_path + "/newdata/cleaneddata/data1_dg.csv", 113.75, 23.05, parent_path + "/newdata/cleaneddata/final_data1_dg.csv", "gbk")
# append_length(parent_path + "/newdata/cleaneddata/data1_sz.csv", 114.05, 22.55, parent_path + "/newdata/cleaneddata/final_data1_sz.csv", "gbk")
def split_data(file_path, file1, file2, file3,file4, encoding):
df = pd.read_csv(file_path, encoding=encoding)
groups = df.groupby("城市")
for city, group in groups:
if city == "广州市":
group.to_csv(file1, index=False)
if city == "佛山市":
group.to_csv(file2, index=False)
if city == "深圳市":
group.to_csv(file3, index=False)
if city == "东莞市":
group.to_csv(file4, index=False)
#split_data(data_path6, parent_path + "/newdata/cleaneddata/data1_gz.csv", parent_path + "/newdata/cleaneddata/data1_fs.csv", parent_path + "/newdata/cleaneddata/data1_sz.csv", parent_path + "/newdata/cleaneddata/data1_dg.csv", "gbk")
#split_data(data_path7, parent_path + "/newdata/cleaneddata/data2_gz.csv", parent_path + "/newdata/cleaneddata/data2_fs.csv", parent_path + "/newdata/cleaneddata/data2_sz.csv", parent_path + "/newdata/cleaneddata/data2_dg.csv", "utf-8")
def concat_data_tool(file_path1, file_path2):
df1 = pd.read_csv(file_path1, encoding="gbk")
df2 = pd.read_csv(file_path2, encoding="utf-8")
concat_data = []
for index1, each_data1 in df1.iterrows():
jing_value1 = each_data1["任务gps经度"]
wei_value1 = each_data1["任务gps 纬度"]
d_data = []
for index2, each_data2 in df2.iterrows():
jing_value2 = each_data2["会员gps经度"]
wei_value2 = each_data2["会员gps纬度"]
distance = int(6371 * np.arccos(np.sin(wei_value1)*np.sin(wei_value2)+np.cos(wei_value1)*np.cos(wei_value2)*np.cos(jing_value1-jing_value2)))
if distance < 500:
d_data.append(index2)
concat_data.append(d_data)
return concat_data
def concat_data(data_path1, data_path2, data_list, file_name):
df1 = pd.read_csv(data_path1, encoding="gbk")
df2 = pd.read_csv(data_path2, encoding="utf-8")
datas = []
for indexs in data_list:
temp_df = df2.iloc[indexs, :]
mean_df = np.mean(temp_df)
mean_df["会员集中度"] = len(indexs)
datas.append([mean_df["信誉值"], mean_df["预订任务限额"], mean_df["会员集中度"]])
concat_df = df1.join(pd.DataFrame(datas, columns=["信誉值","预定任务限额","会员集中度"]))
concat_df.to_csv(file_name, index=False)
#if __name__ == "__main__":
# concat_d = concat_data_tool(data_path6, data_path7)
# concat_data(data_path6, data_path7, concat_d, parent_path + "/newdata/concat_data.csv")
def split_yes_no(file_name1, file_name2):
df = pd.read_csv(data_path6, encoding="gbk")
for status, group in df.groupby("任务执行情况"):
if status == 0:
group.to_csv(file_name1, index=False)
else:
group.to_csv(file_name2, index=False)
split_yes_no(parent_path+"/newdata/no.csv", parent_path+"/newdata/yes.csv")
"""
data = []
for index2, each_data2 in df2.iterrows():
jing_value2 = each_data2["会员gps经度"]
wei_value2 = each_data2["会员gps纬度"]
R = [[],[]]
for index1, each_data1 in df1.iterrows():
jing_value1 = each_data1["任务gps经度"]
wei_value1 = each_data1["任务gps 纬度"]
distance = int(6371 * np.arccos(np.sin(wei_value1)*np.sin(wei_value2)+np.cos(wei_value1)*np.cos(wei_value2)*np.cos(jing_value1-jing_value2)))
R[0].append(each_data1["任务号码"])
R[1].append(distance)
print(index2)
a = R[0]
b = R[1]
data.append([index2, a[b.index(np.min(b))], np.min(R[1]), df2.iloc[index2]["预订任务限额"], int(df2.iloc[index2]["预订任务开始时间"].split(":")[0])*3600 + int(df2.iloc[index2]["预订任务开始时间"].split(":")[1])*60 + int(df2.iloc[index2]["预订任务开始时间"].split(":")[2])])
print("*"*100)
print(np.array(data))
print(len(data))
calc_data = pd.DataFrame(data, columns=["会员编号", "任务编号", "两者距离", "预定任务限额", "预定任务时间"])
mean_data = []
for index, group in calc_data.groupby("任务编号"):
print([index] + list(np.mean(group.iloc[:,1:])))
mean_data.append([index] + list(np.mean(group.iloc[:,1:])))
mean_df = pd.DataFrame(mean_data, columns=["任务编号", "两者距离", "预定任务限额", "预定任务时间"])
sort_mean_df = mean_df.sort_values(by="任务编号", ascending=True)
real_data = df1.join(sort_mean_df)
real_data.to_csv(data_file)
"""
# concat_data(parent_path + "/newdata/cleaneddata/data1_gz.csv", parent_path + "/newdata/cleaneddata/data2_gz.csv", parent_path + "/newdata/cleaneddata/data_gz.csv")
import pandas as pd
from matplotlib import pyplot as plt
from matplotlib.font_manager import FontProperties
import os
import numpy as np
import matplotlib as mpl
mpl.rcParams['font.sans-serif'] = ['SimHei']
mpl.rcParams['axes.unicode_minus'] = False
font = FontProperties(fname=r"C:\windows\fonts\simsun.ttc", size=10)
d = os.path.dirname(__file__)
parent_path = os.path.dirname(d)
def draw_scatter(file_path, color, marker, city, image_path):
df = pd.read_csv(file_path)
p = df["任务标价"]
d = df["与中心距离"]
plt.scatter(d/100, p, s=10, c=color, alpha=0.6, marker=marker)
plt.title("{}任务位置与定价的关系".format(city), fontproperties=font)
plt.xlabel("Distance/x100km")
plt.ylabel("Price")
plt.savefig(image_path)
plt.show()
# draw_scatter(parent_path + "/newdata/cleaneddata/final_data1_gz.csv", "red", "o", "广州市", parent_path + "/images/scatter_gz.jpg")
# draw_scatter(parent_path + "/newdata/cleaneddata/final_data1_sz.csv", "red", "o", "深圳市", parent_path + "/images/scatter_sz.jpg")
# draw_scatter(parent_path + "/newdata/cleaneddata/final_data1_fs.csv", "red", "o", "佛山市", parent_path + "/images/scatter_fs.jpg")
# draw_scatter(parent_path + "/newdata/cleaneddata/final_data1_dg.csv", "red", "o", "东莞市", parent_path + "/images/scatter_dg.jpg")
def get_mean_price(file_path):
df = pd.read_csv(file_path)
p = df["任务标价"]
mean_price = np.mean(p)
return mean_price
def draw_price_(file_path, column, color, marker, title, xlabel, image_path):
df = pd.read_csv(file_path)
p = df["任务标价"]
d = df[column]
plt.scatter(d/100, p, s=10, c=color, alpha=0.6, marker=marker)
plt.title("{}与定价的关系".format(title), fontproperties=font)
plt.xlabel(xlabel)
plt.ylabel("Price")
plt.savefig(image_path)
plt.show()
# draw_price_(parent_path + "/newdata/concat_data.csv", "信誉值", "red", "*", "信誉值", "Credibility", parent_path + "/images/credit_price.jpg")
# draw_price_(parent_path + "/newdata/concat_data.csv", "预定任务限额", "red", "*", "预定任务限额", "Num", parent_path + "/images/num_price.jpg")
# draw_price_(parent_path + "/newdata/concat_data.csv", "会员集中度", "red", "*", "会员集中度", "Density", parent_path + "/images/density_price.jpg")
def draw_no_scatter():
df = pd.read_csv(parent_path + "/newdata/no.csv")
plt.scatter(np.arange(313), df["任务标价"], s=20, c="red", alpha=0.6, marker="*")
plt.title("任务未完成数据的价格分布")
plt.xlabel("Mission")
plt.ylabel("Price")
plt.savefig(parent_path + "/images/no_price.jpg")
plt.show()
# draw_no_price()
def draw_no_bar():
df = pd.read_csv(parent_path + "/newdata/no.csv")
df1 = df[(65 <= df["任务标价"]) & (df["任务标价"] < 70)]
df2 = df[(70 <= df["任务标价"]) & (df["任务标价"] < 75)]
df3 = df[(75 <= df["任务标价"]) & (df["任务标价"] < 80)]
df4 = df[(80 <= df["任务标价"]) & (df["任务标价"] < 85)]
data_len = [len(df1), len(df2), len(df3), len(df4)]
plt.barh(np.arange(4), data_len, height=0.6, alpha=0.7, color="yellowgreen")
plt.ylabel("Price Region")
plt.xlabel("Num")
plt.title("未完成价格条形图")
plt.yticks(np.arange(4), ["65-69", "70-74", "75-79", "80-85"], rotation=45, size=13)
for x, value in enumerate(data_len):
plt.text(y=x, x=value, s="{}".format(value), ha="center")
plt.savefig(parent_path+"/images/price_bar.jpg")
plt.show()
# draw_no_bar()
def draw_no_pie():
df = pd.read_csv(parent_path + "/newdata/no.csv")
df1 = df[(65 <= df["任务标价"]) & (df["任务标价"] < 70)]
df2 = df[(70 <= df["任务标价"]) & (df["任务标价"] < 75)]
df3 = df[(75 <= df["任务标价"]) & (df["任务标价"] < 80)]
df4 = df[(80 <= df["任务标价"]) & (df["任务标价"] < 85)]
data_len = [len(df1), len(df2), len(df3), len(df4)]
plt.pie(x=data_len, labels=["65-69", "70-74", "75-79", "80-85"], autopct="%.0f%%", shadow=True, radius=1.2)
plt.legend(["65-69", "70-74", "75-79", "80-85"], loc="best")
plt.title("未完成价格饼图")
plt.savefig(parent_path+"/images/price_pie.jpg")
plt.show()
# draw_no_pie()
def draw_xin_xian(column):
df = pd.read_csv(parent_path + "/newdata/true_data2.csv")
column_data = sorted(df[column])
numbers = len(column_data)
plt.plot(range(numbers), column_data)
# plt.bar(range(numbers), column_data)
plt.show()
draw_xin_xian("信誉值")
"""
if __name__ == "__main__":
files = [parent_path + "/newdata/cleaneddata/final_data1_sz.csv", parent_path + "/newdata/cleaneddata/final_data1_gz.csv", parent_path + "/newdata/cleaneddata/final_data1_fs.csv", parent_path + "/newdata/cleaneddata/final_data1_dg.csv"]
price_ls = [get_mean_price(each_file) for each_file in files]
plt.barh(np.arange(4), price_ls, height=0.7, color="lightskyblue")
plt.xlabel("Price")
plt.ylabel("Cities")
plt.title("城市与任务定价的关系", fontproperties=font)
plt.yticks(np.arange(4), ["深圳", "广州", "佛山", "东莞"], rotation=45, size=8)
for x, value in enumerate(price_ls):
plt.text(y=x, x=value, s="{}".format(value), ha="center", )
plt.savefig(parent_path + "/images/city_price.jpg")
plt.show()
"""
[a, b] = size(data1_dg_jing);
[c, d] = size(data2_dg_jing);
mt_data = zeros(a,c);
for i = 1:a
for j = 1:c
mt_data(i,j) = 6371*acos(sin(data1_dg_wei(i))*sin(data2_dg_wei(j))+cos(data1_dg_wei(i))*cos(data2_dg_wei(j))*cos(data1_dg_jing(i)-data2_dg_jing(j)));
end
end
normalized_data = zscore(mt_data);
for i = 1:a
for j = 1:c
if normalized_data(i,j) > -1
normalized_data(i,j) = 0;
end
end
end
new_data = zeros(a, 3);
for i = 1:a
cols = find(normalized_data(i,:) < 0);
mean_dg_xin = mean(data2_dg_xin(cols));
mean_dg_xian = mean(data2_dg_xian(cols));
[temp, dg_rho] = size(cols);
new_data(i, 1) = mean_dg_xin;
new_data(i,2) = mean_dg_xian;
new_data(i,3) = dg_rho;
end