1. 程式人生 > >Matplotlib學習---用matplotlib畫折線圖(line chart)

Matplotlib學習---用matplotlib畫折線圖(line chart)

顯示 原因 mea overflow func pen port 一點 mes

這裏利用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()

這個數據文件比較大,在用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()

最終圖像如下:

技術分享圖片

Matplotlib學習---用matplotlib畫折線圖(line chart)