全国三甲医院数据分析-pyecharts动态图表可视化

1200+三甲医院地理坐标(百度地图API),数据处理和分析,pyecharts数据可视化

https://www.linkedin.com/feed/update/urn:li:activity:6642160429646868480

https://lnkd.in/f78_A3T
Within one or days, I did ETL on 1200+ top hospitals in China. 30000hospitals of which 3000 were tier 3. Only 1200+ tier 3 Top Class.
Data crawling was done a few weeks ago. Recently I put the data into mysql. Then I did data cleaning and processing with MySQL and python. Finally data visualization was done with Pyecharts.
Feel free to review it on my website. It’s in html tabs and easy to view. Very interactive Charts!
To your surprise, Wuhan or Hubei has one of the best medical resources in China. Virus like this is beyond capability of any emergency systems in any countries.
嗯,天快亮了。花了一两天时间把全国1200+三甲医院的数据(全国3万家左右医院,提取出不到3000三级,三甲其实才1200+)提取出来(在我这个链接里你可以看到每家三甲医院的地理位置和电话),处理分析了下,一共12张动态图,做成了html,欢迎大家打开链接观赏下,不看你会后悔的,开心一笑!
大家一定要打开网页看下,手机调成横屏。一共12个标签页,因为这样更方便看图。有时间我再出个简单的分析报告和处理流程,放在自己的网站。你的点击能助力那些想学习数据分析而又找不到敲门的朋友找到我的网站[Shake]如果有朋友懂得运营,欢迎指点迷津,有志于做个高质量的技术博客。
这些漂亮的动态图之后是几十个小时的数据处理,这两天代码敲的都快折了!一共700-800行代码,包括爬虫,导入数据库(mysql里也做了些简单的分析处理),数据清洗,数据处理,数据分析和可视化。用到的工具主要是python,pyecharts,json,mysql。
睡个觉,该起来吃早饭了,忙的一天只吃了一顿饭。

全国三甲医院分析-Pyecharts自定义坐标(json_file)

为pyecharts定制json文件,自定义坐标

虽然Pyecharts很多槽点,但是目前还没找到替代品,换新工具也有时间成本(沉没成本?)。之前爬虫爬取的全国三万家医院信息放在了mysql,最近在休假正好有空拿来处理下做个分析。这两天主要是学习下json包的使用,因为pyecharts的自定义坐标用json文件比较方便:

从SQL读取并将关键信息写入新的sql table

def generate_mysql():
    conn = pymysql.connect(
        host='localhost',
        user='username',
        password='passwd',
        port=3306,
        charset = 'UTF8MB4',  
        db = 'hospital_list')
    cursor = conn.cursor()

    sql = 'CREATE TABLE IF NOT EXISTS hospital_coord (hospital VARCHAR(30),\
    provinces VARCHAR(15), cities VARCHAR(15), location VARCHAR(30) ,phone VARCHAR(30), PRIMARY KEY (hospital))'
    # hospital_list是要在数据库中建立的表,用于存放数据
    
    cursor.execute(sql)
    conn.close()
def read_mysql(db):
    Engine = create_engine('mysql+pymysql://user:passwd@localhost:3306/{0}?charset=UTF8MB4'.format(db))
    con = Engine.connect()
    #提取hospital_name_new, Provinces, Cities, address, phone
    df = pd.read_sql("""SELECT hospital_name_new, Provinces, Cities, address, phone
    FROM top_tier3_hospital ORDER BY Cities ASC """, con)
#    print(type(df['Cities', 'address']))
#    print(df['address'])
    for m in range(0, len(df[ 'address'])):
        df[ 'address'][m] = str(df[ 'address'][m]).split()[0]
        if(len(str(df[ 'address'][m]).split()) > 1):
            print(df[ 'address'])
    
    return df['hospital_name_new'], df['Provinces'], df['Cities'], df[ 'address'], df['phone']
    con.close()
#hospital, provinces, cities, location, address, phone 
        
def write_to_sql(data_frame, db = 'hospital_list'):
    engine = create_engine('mysql+pymysql://user:passwd@localhost:3306/{0}?charset=UTF8MB4'.format(db))
    try:
        # df = pd.read_csv(df)
        data_frame.to_sql('hospital_coord',con = engine,if_exists='append',index=False)
        # append表示在原有表基础上增加,但该表要有表头(header)
    except Exception as e:
        print(e)       
      

这部分比较熟练了,但是还是有几点需要注意:

  • primary key的使用,防止重复数据
  • 处理不规范数据,比如之前地址栏有两个地址(分号隔开),医院名称也有别称(括号内),个别医院地址找不到返回的无效坐标(0,0)-这个会在后面的程序中带来很多麻烦,比如重复数据,None类型的这种数据会带来意外的问题。
  • SQL命令熟悉了可以方便查看一些数据问题,否则还得写代码,即使是python也很浪费时间,不如命令行来的方便。比如这次产生的None类型数据,重复医院名称,很容易在命令行查到,如果用Python大概率要写代码循环判断。
  • 常用的几个命令:
SELECT * FROM top_tier3_hospital WHERE hospital_name_new LIKE '%枣庄%' and hospital_tier LIKE '三级甲等%';
SELECT COUNT(*) FROM hospital_coord;
SELECT * FROM top_tier3_hospital WHERE address LIKE "%;%";
SELECT * FROM top_tier3_hospital LIMIT 20;

mysql>  SELECT * FROM hospital_list_table
    ->  WHERE hospital_name_new='南京长江医院第三医院' or  hospital_name_new='孝感市人民医院';
数据查重:
mysql>  SELECT COUNT(*) as repetitions, hospital_name_new
    ->  FROM hospital_list_table
    -> GROUP BY hospital_name_new
    ->  HAVING repetitions > 1;

xml转json:

  • 百度API返回的页面没有xml的declaration,但是Python中requests的response却是有的,这个declaration最好去掉:
  • 百度的返回:
<GeocoderSearchResponse>
<status>OK</status>
<result>
<location>
<lat>33.082054</lat>
<lng>107.022983</lng>
</location>
<precise>1</precise>
<confidence>80</confidence>
<level>门址</level>
</result>
</GeocoderSearchResponse>
#定义xml转json的函数
def xmltojson(xmlstr):
    #parse是的xml解析器
    xmlparse = xmltodict.parse(xmlstr)
    #json库dumps()是将dict转化成json格式,loads()是将json转化成dict格式。
    #dumps()方法的ident=1,格式化json
    jsonstr = json.dumps(xmlparse,indent=1)
    return jsonstr
    #print(jsonstr)

百度API调取坐标

这个函数是用来从百度调取具体地点的坐标的,申请API之后就有了这个ak密钥。虽然百度限制宽松,但是我还是限制了使用频率,所以程序会跑300多秒。另外,加上headers和except容错机制,不然肯定会有几个地点让程序崩溃。需要说明的是,ExpatError这个是需要在程序开头import相关的包的,用了捕捉出现的错误,一个健壮的程序必不可少。

容错语句里我傻傻的加上了错误的地方返回(0,0),其实这个后来给我带来了麻烦。我没有重写这段,只是在后来把产生的(0,0)去掉了。不过,不返回一个值我觉得会有问题,给它一个确定的返回值更好,后面也很方便处理掉,因为这个是可控的。

def __get_location1__(hospital, provinces, cities, location_list, address, phone):  
        my_ak = 'yourak_from_baidu'    # 需要自己填写自己的AK
#        print(location_list)
        headers = {
            "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
            "Accept-Encoding": "gzip, deflate, br",
            "Accept-Language": "zh-CN,zh;q=0.8",
            "Cache-Control": "max-age=0",
            "Connection": "keep-alive",
            "Upgrade-Insecure-Requests": "1",
            "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.86 Safari/537.36"
        }
        try:
            url = 'http://api.map.baidu.com/geocoder?address=' + location_list +'output=json&key=' + my_ak
            print(url)
            response = requests.get(url, headers = headers , timeout = 15)
            txt = response.text
#            print(txt)
            print('\n')
            res_json = txt[42:]
            res_json_ = xmltojson(res_json)
            print(res_json_)
            json_dict = json.loads(res_json_)
#            print(type(json_dict))
            print(json_dict)
            lat = json_dict['GeocoderSearchResponse']['result']['location']['lat']
            lng = json_dict['GeocoderSearchResponse']['result']['location']['lng']
            print(lat, lng)
#            xml_dict = xmltodict.parse(res_json)
            return hospital, provinces, cities, lat, lng, address, phone      
            
        except ExpatError as e:
            print('location {} {} not found. BaiduMap is lost!'.format(cities, location_list))
            print(e)
            #print(response.status_code)
            print(time.strftime('%Y-%m-%d %H:%M:%S'))
#            time.sleep(random.random()*2)
            return 0,0

坐标转换成Geo需要的json文件(自定义坐标)

自定义坐标这块pyecharts官方文档没有详细说明,网上也很少相关的帖子。Geo对json格式要求严格,对输入的data_pair要求也很高,不然不会出图。先说data_pair:

data_pair 我通常用list(zip(list1, list2)),这样你就得到了a list of tuples,比如:

[('重庆三峡中心医院平湖分院', '重庆市', '万州区', '30.836284', '108.409686', '重庆市万州区北山大道862号', '023-58355555,023-58355511'), ('重庆三峡中心医院御安分院', '重庆市', '万州区', '30.844058', '108.425126', '重庆市万州区北山大道1666号', '023-58339601,023-58339553'), ('重庆三峡中心医院百安分院', '重庆市', '万州区', '30.771119', '108.452463', '重庆万州区五桥百安移民新区', '023-58556222')]

通常,你可能不需要这么多,只是[(‘name’, ‘value’), (‘name2’, ‘value2’) ] 就够用了,但是我这里需要在地图上显示医院名称,详细地址和电话。而Geo函数所需要的坐标是自定义json文件中得到的。Pyecharts自带的city_coordinate_json是这个样子的,你的格式也必须处理成这样。这里你会用到json_dumps, json_loads。

{
  "阿城": [
    126.58,
    45.32
  ],
  "阿克苏": [
    80.19,
    41.09
  ],
  "阿勒泰": [
    88.12,
    47.5
  ]
}