zoukankan      html  css  js  c++  java
  • Matplotlib学习---用matplotlib画折线图(line chart)

    这里利用Jake Vanderplas所著的《Python数据科学手册》一书中的数据,学习画图。

    数据地址:https://raw.githubusercontent.com/jakevdp/data-CDCbirths/master/births.csv

    准备工作:先导入matplotlib和pandas,用pandas读取csv文件,然后创建一个图像和一个坐标轴

    import pandas as pd
    from matplotlib import pyplot as plt
    birth=pd.read_csv(r"https://raw.githubusercontent.com/jakevdp/data-CDCbirths/master/births.csv")
    fig,ax=plt.subplots()

    我们想要画一个反映每天平均出生人数的折线图,看看节假日是否对出生人数有影响。

    折线图: ax.plot(x,y,marker="-",color="black")

    这个数据文件比较大,在用print(birth.head())和print(birth.tail())分别查看前后5行数据后,发现day这一列的数据中有null字样。

    用print(birth[birth["day"]!="null"])命令查看没有null字样的数据(此处只截取部分):

           year  month day gender  births
    0      1969      1   1      F    4046
    1      1969      1   1      M    4440
    2      1969      1   2      F    4454
    3      1969      1   2      M    4548
    4      1969      1   3      F    4548
    5      1969      1   3      M    4994
    6      1969      1   4      F    4440
    7      1969      1   4      M    4520
    8      1969      1   5      F    4192
    9      1969      1   5      M    4198
    10     1969      1   6      F    4710
    11     1969      1   6      M    4850
    12     1969      1   7      F    4646
    13     1969      1   7      M    5092
    14     1969      1   8      F    4800
    15     1969      1   8      M    4934
    16     1969      1   9      F    4592
    17     1969      1   9      M    4842

    用print(birth[birth["day"]=="null"])命令查看有null字样的数据(此处只截取部分):

           year  month   day gender  births
    15067  1989      1  null      F  156749
    15068  1989      1  null      M  164052
    15069  1989      2  null      F  146710
    15070  1989      2  null      M  154047
    15071  1989      3  null      F  165889
    15072  1989      3  null      M  174433
    15073  1989      4  null      F  155689
    15074  1989      4  null      M  163432
    15075  1989      5  null      F  163800
    15076  1989      5  null      M  172892
    15077  1989      6  null      F  165525
    15078  1989      6  null      M  173823
    15079  1989      7  null      F  174054
    15080  1989      7  null      M  183063
    15081  1989      8  null      F  178986
    15082  1989      8  null      M  188074
    15083  1989      9  null      F  174808
    15084  1989      9  null      M  182962

    可以看出,从1989年开始,数据画风突变,此前记录的是每天的数据,后面记录的是每个月的数据。

    这里我们先把1969年-1988年的数据提取出来进行统计:birth=birth.iloc[:15067],然后用birth.info()命令查看各列数据的类型,发现day这一列数据类型是object,因此需要转换其数据类型:

    birth["day"]=birth["day"].astype(int)

    接下来把year,month和day这三列的数据结合起来,用pd.to_datetime()转化为时间序列格式。结果发生了错误:

    ValueError: cannot assemble the datetimes: 'int' object is unsliceable

    网上查找原因,在Stackoverflow上有人解答说是因为日期可能超出了允许的范围:

    The valid day range is between 1 and 31. Check your data again make sure all the columns are within allowable range.

    现在需要把超标的地方找出来,看一看究竟是什么错误。用print(birth[birth["day"]>31])命令查看day超过31的地方,结果发现有好多(此处只截取部分):

           year  month  day gender  births
    62     1969      1   99      F      26
    63     1969      1   99      M      38
    126    1969      2   99      F      42
    127    1969      2   99      M      48
    190    1969      3   99      F      64
    191    1969      3   99      M      50
    254    1969      4   99      F      50
    255    1969      4   99      M      66
    318    1969      5   99      F      54
    319    1969      5   99      M      52
    382    1969      6   99      F      54
    383    1969      6   99      M      48
    446    1969      7   99      F      24
    447    1969      7   99      M      44

    不清楚是什么情况,在这里先把这些超标数据去除:birth=birth[birth["day"]<=31]。结果发现还是不对,把数据再调出来看了之后,发现还有一些day超标,比如说2月份还有29号和30号。干脆在pd.to_datetime()命令中加上errors='coerce'参数,这样在数据超标的地方,时间序列就会变成NaT:

    birth["date"]=pd.to_datetime({"year":birth["year"],"month":birth["month"],"day":birth["day"]},errors='coerce')

    运行上述命令--->给birth数据文件添加了一列---“date”,记录时间序列。

    再次查看birth数据后,发现在day超标的地方,date一列已经标上了NaT(此处只截取部分):

         year  month  day gender  births       date
    0    1969      1    1      F    4046 1969-01-01
    1    1969      1    1      M    4440 1969-01-01
    2    1969      1    2      F    4454 1969-01-02
    ..    ...    ...  ...    ...     ...        ...
    190  1969      3   99      F      64        NaT
    191  1969      3   99      M      50        NaT

    现在把date一列中不是null的值提取出来:

    birth=birth[birth["date"].notnull()]

    接下来制作一个透视表,把1969年-1988年之间各天出生人数的平均数计算出来(此处需要导入numpy):

    birth_by_date=pd.pivot_table(birth,values="births",index=["month","day"],aggfunc=np.mean)

    birth_by_date透视表如下(此处只截取部分):

                 births
    month day          
    1     1    4009.225
          2    4247.400
          3    4500.900
          4    4571.350
          5    4603.625
          6    4668.150
          7    4706.925
          8    4629.650
          9    4537.775
                ...
    12    2    4830.300
          3    4758.500
          4    4718.725
          5    4734.675
          6    4683.050
          7    4704.325
          8    4803.800
          9    4793.825

    接下来把透视表的index改为时间序列格式,由于只需要month和day这两项,year先随便填一个:

    birth_by_date.index=pd.DatetimeIndex([pd.datetime(2000,month,day) for (month,day) in birth_by_date.index])

    现在透视表如下(此处只截取部分):

                  births
    2000-01-01  4009.225
    2000-01-02  4247.400
    2000-01-03  4500.900
    2000-01-04  4571.350
    2000-01-05  4603.625
    2000-01-06  4668.150
    2000-01-07  4706.925
    2000-01-08  4629.650
    2000-01-09  4537.775

    终于大功告成!真不敢相信,图还没画,处理数据已经花了那么多功夫。但在实际工作中,这其实是很常见的,数据的清洗通常需要花费大部分时间。

    接下来以透视表的index为x轴,values为y轴,画折线图:

    ax.plot(birth_by_date.index,birth_by_date.values,"-")

    图像如下:

    可以看出有几处日期,出生人数骤降,因此需要在这几个地方进行标注。同时,x轴的刻度值要改成12个月份,y轴要加上标签,整个图再拉长一点,再加上标题。这样,这个折线图就基本完美了。

    完整代码如下:

    import numpy as np
    import pandas as pd
    import matplotlib as mpl
    from matplotlib import pyplot as plt
    birth=pd.read_csv(r"https://raw.githubusercontent.com/jakevdp/data-CDCbirths/master/births.csv")
    fig,ax=plt.subplots(figsize=(12,4))
    
    birth=birth.iloc[:15067]
    birth["day"]=birth["day"].astype(int)
    
    birth["date"]=pd.to_datetime({"year":birth["year"],"month":birth["month"],"day":birth["day"]},errors='coerce')
    birth=birth[birth["date"].notnull()]
    
    birth_by_date=pd.pivot_table(birth,values="births",index=["month","day"],aggfunc=np.mean)
    
    birth_by_date.index=pd.DatetimeIndex([pd.datetime(2000,month,day) for (month,day) in birth_by_date.index])
    
    ax.plot(birth_by_date.index,birth_by_date.values,"-")
    ax.set(ylabel="average daily births",title="USA births by day of year (1969-1988)",xlim=("2000-01","2001-01"))
    ax.grid(True) #显示网格
    
    t=birth_by_date.stack()  #把透视表展开
    
    #在相应的地方标上节日名称
    ax.text("2000-01-01",t["2000-01-01"],"New Year's Day")
    ax.text("2000-07-04",t["2000-07-04"]-60,"Independence Day",ha="center")
    ax.text("2000-09-04",t["2000-09-01"]-60,"Labor Day",ha="center")
    ax.text("2000-10-31",t["2000-10-31"]-60,"Halloween",ha="center")
    ax.text("2000-11-25",t["2000-11-27"]-60,"Thanksgiving",ha="center")
    ax.text("2000-12-25",t["2000-12-25"],"Christmas",ha="right")
    
    #设置x轴刻度值为月份,并使其居中
    ax.xaxis.set_major_locator(mpl.dates.MonthLocator())
    ax.xaxis.set_minor_locator(mpl.dates.MonthLocator(bymonthday=15))
    ax.xaxis.set_major_formatter(plt.NullFormatter())
    ax.xaxis.set_minor_formatter(mpl.dates.DateFormatter('%h'))
    
    plt.show()

    最终图像如下:

  • 相关阅读:
    Binder机制,从Java到C (大纲)
    大陆台湾计算机术语比对
    关于nginx架构探究(2)
    关于nginx架构探究(1)
    关于开源中文搜索引擎架构coreseek中算法详解
    关于linux定时操作cron的理解
    sqlachemy 使用实例
    nginx+uwsgi+flask搭建python-web应用程序
    VC维
    关于zMPLS的设计解析
  • 原文地址:https://www.cnblogs.com/HuZihu/p/9426527.html
Copyright © 2011-2022 走看看