大数据实战项目记录--农村、城市居民主要疾病死亡率及死因数据分析(国家数据网站)

1爬取医疗行业数据并输出保存为csv格式文件

目的:编写爬虫完成爬取国家统计局网站年度数据之卫生行业分类数据,具体爬取数据为:
(1)城市居民主要疾病死因构成
(2)农村居民主要疾病死因构成
将爬取的数据进行初步的分析、整合,要求输出为csv格式文件,供Spark程序处理分析。

1.1网站数据查找与分析

国家数据网站(国家统计局) :https://data.stats.gov.cn/easyquery.htm
选择“年度数据”,然后选择“卫生”行业数据下的“城市居民主要疾病死亡率及死因构成”和“农村居民主要疾病死亡率及死因构成”。
再进一层获取下一级的“城市居民主要疾病死因构成”和“农村居民主要疾病死因构成”数据,均为近十年数据。
数据显示如下:
在这里插入图片描述
打开浏览器调试窗口找到具体的请求url地址信息。

通过preview页签核实是否是自己需要的数据。
在这里插入图片描述datanodes:对应的是数据
wdnodes:对应的就是数据标签、或者数据对照表,这里面有疾病指标代码对照、年份列表等等

最终锁定爬取的url目标地址:
在这里插入图片描述
Request URL:
https://data.stats.gov.cn/easyquery.htm?m=QueryData&dbcode=hgnd&rowcode=zb&colcode=sj&wds=%5B%5D&dfwds=%5B%7B%22wdcode%22%3A%22zb%22%2C%22valuecode%22%3A%22A0O0G02%22%7D%5D&k1=1625457548015&h=1
参数传递:
在这里插入图片描述
Query String Parameters 其实就是get请求中url后面要带的参数。也就是说,向对应网址服务器传递这些参数就可以获得请求的内容。
当发起一次GET请求时,参数会以url string的形式进行传递。即?后的字符串则为其请求参数,并以&作为分隔符。

# coding=utf-8
import requests
import json
import csv
def crawl(url, save_file):
    '''
     数据爬取,将数据爬取下来保存为json文件,开发时每次读取json文件,降低被封的几率
    :param url: 爬取页面url
    :param save_file: 保存路径
    '''
    response = requests.get(url, verify=False)   # 直接get会有问题, 设置verify=False移除SSL认证
    cont = response.content.decode('utf-8')      #HTTP响应内容的二进制(bytes) 形式以utf-8解码
    m = json.loads(cont)
    print(m)
    with open(save_file, 'w', encoding='utf-8') as f:
        json.dump(m, f, ensure_ascii=False)
        
def load_jsondata(file):
    '''
    读取本地json文件
    :param file:
    :return: 返回读取的文本数据转换成的字典对象
    '''
    f = open(file, encoding='utf-8')
    m = json.load(f)
    f.close()
    return m

def save_csvfile(lst, savefile):
    '''
    将传入的列表数据保存为csv文件
    :param lst:
    :param savefile:
    :return:
    '''
    with open(savefile, 'w', newline='', encoding='utf-8') as f:
        writer = csv.writer(f)
        for n in lst:
            writer.writerow(n)

def merge(m):
    '''
    将json数据中的文本数据与列标签进行合并
    :param m:
    :return:
    '''    
    #datanode格式样例:{'code': 'zb.A0O0G0201_sj.2020', 'data': {'data': 0.0, 'dotcount': 2, 'hasdata': False, 'strdata': ''}, 'wds': [{'valuecode': 'A0O0G0201', 'wdcode': 'zb'}, {'valuecode': '2020', 'wdcode': 'sj'}]}
    #wdnode格式样例:{'cname': '城市传染病(不含呼吸道结核)死亡人数占总死亡人数的比重', 'code': 'A0O0G0201', 'dotcount': 2, 'exp': '', 'ifshowcode': False, 'memo': '', 'name': '城市传染病(不含呼吸道结核)死亡人数占总死亡人数的比重', 'nodesort': '1', 'sortcode': 51658, 'tag': '', 'unit': '%'}
    #我们需要的数据在strdata中,但strdata外包裹着一层datanodes
    datanodes = m['returndata']['datanodes']      # 剥开datanodes,获取数据节点列表
    wdnodes = m['returndata']['wdnodes'][0]['nodes']      # 获取列标签(年份)信息列表
    print(datanodes[0])
    print(wdnodes[4])
    # print(len(datanodes))
    # print(len(wdnodes[0]['nodes']))
    一个wdnode对应十个(十年)datanode,每个wdnode中需要提取两项,cname和code;每个datanode需要提取两项,data和wds[0]的valuecode
    # 获取wdnode中的编号和标签名,标签名中含有英文半角逗号,会对结果造成问题,因此转换为全角逗号
    urban_l = [[node['code'], node['cname'].replace(',', ',')] for node in wdnodes]
    # 获取datanode中的编号和数值
    # urban_data_l = [(node['wds'][0]['valuecode'], node['data']['strdata']) if node['data']['strdata'] != '' else
    #                (node['wds'][0]['valuecode'], '0.0') for node in datanodes]
    urban_data_l = [(node['wds'][0]['valuecode'], node['data']['strdata']) for node in datanodes]
    tmp_m = {}
    tmp_l = []
    key = ''
    for idx, node in enumerate(urban_data_l):
        key = node[0]
        tmp_l.append(node[1])
        if (idx + 1) % 10 == 0:
            tmp_m[key] = tmp_l.copy()
            tmp_l.clear()

    for n in urban_l:
        key = n[0]
        n.extend(tmp_m[key])
    return urban_l

if __name__ == '__main__':
    # 爬取城市居民主要疾病死因和城市居民主要疾病死因(近十年,2011~2020)
     urban_url = "https://data.stats.gov.cn/easyquery.htm?m=QueryData&dbcode=hgnd&rowcode=zb&colcode=sj&wds=%5B%5D&dfwds=%5B%7B%22wdcode%22%3A%22zb%22%2C%22valuecode%22%3A%22A0O0G02%22%7D%5D&k1=1623569580790&h=1"
     urban_save = 'urban.json'
     crawl(urban_url, urban_save)
    # 爬取农村居民主要疾病死因和城市居民主要疾病死因(近十年,2011~2020)
     rural_url = 'https://data.stats.gov.cn/easyquery.htm?m=QueryData&dbcode=hgnd&rowcode=zb&colcode=sj&wds=%5B%5D&dfwds=%5B%7B%22wdcode%22%3A%22zb%22%2C%22valuecode%22%3A%22A0O0H02%22%7D%5D&k1=1623572236704&h=1'
     rural_save = 'rural.json'
     crawl(rural_url, rural_save)
     urban_m = load_jsondata('urban.json')
     rural_m = load_jsondata('rural.json')
     urban_l = merge(urban_m)
     rural_l = merge(rural_m)
     save_csvfile(urban_l, 'urban.csv')
     save_csvfile(rural_l, 'rural.csv')

结果展示:
json文件:
在这里插入图片描述
csv文件:
在这里插入图片描述

2使用spark技术完成数据的统计分析

2.1数据排序

(1)去除数据中为“”的记录,并将数据按照死亡人数由高到低排序
(2)男性按照死亡人数由高到低排序
(3)女性按照死亡人数由高到低排序

将数据爬取处理后的两个csv格式文件放到当前工程的某个文件夹下,此处建立文件夹:data/original
创建Sort.scala程序完成数据处理。除了总排序外,还需实现男性、女性死亡率的排序问题

import org.apache.spark.{SparkConf, SparkContext}
object Sort {
  def main(args: Array[String]): Unit = {
    val conf=new SparkConf()
      .setMaster("local[*]")
      .setAppName("sort")
    val sc=new SparkContext(conf)
    val datas=sc.textFile("./data/original/")
      .map(_.split(","))
      .filter(arr=>{if (arr(3)!="") true
      else false
      }).map(arr=>{
      (arr(3).toFloat,arr)  // 保存到缓存中,提高后面男性和女性分别计算的效率
    }).persist()

    // 根据key排倒序,并合并到一个partition
    datas.sortByKey(ascending = false,1)
      .map(_._2)
      .map(_.mkString(","))
      .saveAsTextFile("./data/sort/total")

    datas.filter(_._2(1).contains("男性")).sortByKey(ascending = false,1)
      .map(_._2)
      .map(_.mkString(","))
      .saveAsTextFile("data/sort/man/")

    datas.filter(_._2(1).contains("女性")).sortByKey(ascending = false,1)
      .map(_._2)
      .map(_.mkString(","))
      .saveAsTextFile("data/sort/woman")
  }
}

在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述

2.2计算每种疾病死亡率最高的年份

先创建一个列表用于存储最终的结果(包括疾病编号 疾病名称),然后对读取的数据文件中的数据进行初步的筛选(只保留死亡率数据),然后进行数据的转换(将数据转换为float类型),对数据进行比较取最大值最后整合输出。

package com.fy.spark.sql
import org.apache.spark.{SparkConf, SparkContext}
import scala.::
//计算每种疾病哪一年的死亡率最高
  object DeathRate {
    def main(args: Array[String]): Unit = {
      val conf = new SparkConf()
      conf.setAppName("analyze1").setMaster("local[2]")
      val sc = new SparkContext(conf)
      sc.textFile("data/original")
        .map(_.split(","))
        .map(arr => {
          // linelist用于保存结果数据
          var linelist = arr(1) :: arr(0) :: Nil
          // 去掉编号和标签列,只保留数据列
          val sub = arr.slice(2, arr.length)
          // 将所有数据转换为float类型
          sub.foreach(v => {
            if (v == "") 0.0
            else v.toFloat
          })
          val years = (2011 to 2020).reverse
          // 年份与数据的映射关系 (年份,数据)
          val merge = years.zip(sub)
          // 计算最大值
          val r = merge.reduce((a, b) => {
            if (a._2 > b._2) a else b
          })
          //保存到linelist
          ((r._1 + ":" + r._2) :: linelist).reverse
        })
        .map(_.mkString(","))
        .saveAsTextFile("data/deathrate")
    }
  }

在这里插入图片描述

在这里插入图片描述

2.3计算比较每种疾病城市和农村每年的死亡率哪个更高

import org.apache.spark.{SparkConf, SparkContext}
object Compare {
  def main(args: Array[String]): Unit = {
    val conf = new SparkConf().setMaster("local[2]").setAppName("analyze1")
    val sc = new SparkContext(conf)
    val ruralRDD = sc.textFile("./data/original/rural.csv")
    val ruralmapRDD = ruralRDD.map(_.split(","))
      .filter(arr=>{
        !(arr(1).contains("男性")) && !(arr(1).contains("女性"))
      })
      .sortBy(arr=>arr(0),true,1)
      .zipWithIndex()
      .map(t=>(t._2,t._1))
    val urbanRDD = sc.textFile("./data/original//urban.csv")
    val urbanmapRDD = urbanRDD.map(_.split(","))
      .filter(arr=>{
        !(arr(1).contains("男性")) && !(arr(1).contains("女性"))
      })
      .sortBy(arr=>arr(0),true,1)
      .zipWithIndex()
      .map(t=>(t._2,t._1))

    ruralmapRDD.join(urbanmapRDD)
      //.foreach(println)
      .map(_._2)
      //.foreach(println)
      .map(t=>{
        val rural = t._1
        val urban = t._2
        rural.zip(urban)
      })
      .map(arr=>{
        val merge = arr.slice(1,arr.length)
        //merge.foreach(println)
        val resultline = new Array[String](merge.length)
        for(i<-merge.indices){
          if(i==0){
            resultline(i)=merge(i)._1.replace("农村","")
          }else{
            val one = if(merge(i)._1=="") 0.0 else merge(i)._1.toFloat
            val two = if(merge(i)._2=="") 0.0 else merge(i)._2.toFloat
            if(one>two)
              resultline(i)="农村"
            else if(one<two)
              resultline(i)="城市"
            else resultline(i)="相同"
          }
        }
        resultline
      })
      .map(_.mkString(","))
      .saveAsTextFile("data/compare")
  }
}

在这里插入图片描述

2.4计算各种疾病死亡的年增长率,及每种疾病死亡率的平均增长率

import org.apache.spark.{SparkConf, SparkContext}
import scala.collection.mutable
object Increaserate {
  def main(args: Array[String]): Unit = {
    val conf = new SparkConf()
    conf.setAppName("analyze1").setMaster("local[2]")
    val sc = new SparkContext(conf)
    sc.textFile("data/original").map(_.split(",")).map(arr => {
      // 汇总每年的增长率,用于计算年平均增长率
      var rateList: List[Float] = Nil
      // 将每年的增长率整合为列表,用于文本输出
      var lineList: List[String] = Nil
      // 拆分成的数组前三列为 id,label,2020年数据,因为2020年数据都为"",因此也需要跳过
      val endColumn = 3
      // 去掉编号和标签列,只保留各年的数据列
      val arrCleared = arr.slice(endColumn, arr.length)
      // 用于映射年份,key为下面迭代过程的索引
      val dataYearMap =
        mutable.Map[Int, String](
          8 -> "2011",
          7 -> "2012",
          6 -> "2013",
          5 -> "2014",
          4 -> "2015",
          3 -> "2016",
          2 -> "2017",
          1 -> "2018",
          0 -> "2019")
      // 用于保存每条记录共计算几次增长率,用于最后计算每年平均增长率
      var flag = 0
      // 迭代计算各年增长率,公式为:(当前数据-下一年数据)/当前数据*100%
      for (i <- arrCleared.indices.reverse if i > 0) {
        //print(arrCleared(i)+":"+arrCleared(i-1)+",")
        val strCurData = arrCleared(i)
        val strPreData = arrCleared(i - 1)
        var s = ""
        if (strCurData != "" && strPreData != "") {
          val curData = strCurData.toFloat
          val preData = strPreData.toFloat
          // 计算增长率
          val rate = (preData - curData) / curData * 100
          // 计算结果转换为字符串,最后保存到linelist中
          s = dataYearMap(i) + "~" + dataYearMap(i - 1) + ":" + rate
          // 将每年增长率保存到ratelist,用于最后计算平均增长率
          rateList = rate :: rateList
          flag += 1
        } else {
          // 如果当年或下一年数据为空字符串,则标识为0.0
          s = dataYearMap(i) + "~" + dataYearMap(i - 1) + ":" + 0.0
        }
        // 将各年增长率生成的字符串保存到linelist
        lineList = s :: lineList
      }
      // 平均增长率并生成字符串
      val avg_total = "avg:" + rateList.sum / flag
      // 将平均增长率以及整条记录的编号和标签添加到linelist后,通过mkstring转换为以逗号分隔的字符串
      (arr(0) :: arr(1) :: (avg_total :: lineList).reverse).mkString(",")
    }).saveAsTextFile("data/increaserate")
  }
}

在这里插入图片描述

在这里插入图片描述

  • 4
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值