Pandas 模組

Pandas 模組

前言

pandas 是Python程式語言的用於數據操縱和分析的軟體庫。特別是,它提供操縱數值表格和時間序列的資料結構和運算操作。

Pandas 套件全名 Panel DataFrame Series,很多人常用熊貓作為暱稱,但實際上命名的意義和緣由與熊貓八竿子打不著關係。Pandas 套件提供了新的資料結構類別 Index、Series、DataFrame 與 Panel(其中 Panel 資料結構類別在 0.20.0 停止開發維護,)扮演著 Python 在資料分析中的「最後一塊拼圖」;當使用者有表格式資料(Tabular data)的運算處理需求時,就可以透過她達成目的,另外她也提供了豐富的表格式資料載入、整併清理、轉換外型與視覺化的函式。

本文 實例 使用 Python Shell 做演示。

大綱

安裝

  • 安裝指令 : pip install pandas
  • 導入 import pandas as pd
  • 了解版本pd.__version__

有關 Pandas 模組 詳細說明,請參考 : pandas官網

Pandas 的 Series 資料型態

 Series 是一種一維的陣列資料結構,在這個陣列內可以存放整數、浮點數、字串、Python 物件(例如 : 字串 list、字典 dict….) Numpy 的 ndarray,純量....等等。雖然是一維陣列資料,可是看起來卻好像是二維陣列資料,因為一個是索引 index 或稱為 標籤 label,另一個是實際的資料。

Series 結構與 Python 的 list 類似,不過程式設計師可以為 Series 的每個元素自行命名。可以使用 pd.Series() 建立 Series 物件,語法如下:

pandas.Series(data=None, index=None, dtype=None, name=None, options, .....)

使用串列 list 建立 Series 物件

最簡單的建立 Series 物件的方式是在 Data 參數使用串列。

實例 1 : 在 Data 參數使用串列建立 Series 物件 s1 ,然後列出結果。

>>> import pandas as pd
>>> s1 = pd.Series([11,22,33,44,55])
>>> s1
0    11
1    22
2    33
3    44
4    55
dtype: int64 

我們只有建立 Series 物件 s1 內容,可是列印時看到左邊欄位有系統自建的索引, Pandas 的索引也是從 0 開始計數的,這也是為什麼我們說 Series 是一個一維陣列,可是看起來像二維陣列的原因。有了這個索引,我們可以使用索引存取物件內容。上述最後一個列出 "dtype : int64" 指出資料在 Pandas 是以64位元整數儲存與處理的。

實例 2 : 延續先前 實例 1 ,列出 Series 特定索引內容與修改內容

>>> s1[1]
22
>>> s1[1]=20
>>> s1
0    11
1    20
2    33
3    44
4    55
dtype: int64

使用 python 字典建立 Series 物件

如果我們使用 Python 的字典建立 Series 物件,字典的 鍵(key) 就會被視為 Series 物件的索引,字典的 鍵(key) 的值(value) 就會被視為物件的值

實例 1 : 使用 python 字典建立 Series 物件同時列出結果。

>>> import pandas as pd
>>> mydict = {'台北':'Taipei','東京':'Tokyo'}
>>> s2 = pd.Series(mydict)
>>> s2
台北    Taipei
東京     Tokyo
dtype: object

使用 Numpy 的 ndarray 建立 Series 物件

實例 1 : 使用 Numpy 的 ndarray 建立 Series 物件同時列出結果

>>> import pandas as pd
>>> import numpy as np
>>> s3 = pd.Series(np.arange(0,7,2))
>>> s3
0    0
1    2
2    4
3    6
dtype: int32

以上使用 numpy 模組,這也是一個數據科學常用的模組,使用前他需導入此模組

import numpy as np

上述 np.arrange(0,7,2) 可以產生 "0 - 6(=7-1)” 之間的序列,數字每次 增加 2 所以可以得到 (0,2,4,6)

建立含索引的 Series 物件

目前為止,我們了解在建立 Series 物件時,預設情況索引是從 0 開始計數,若是我們使用字典建立 Series 物件,字典的 鍵(key) 就是索引,其實在建立 Series 物件時,也可以使用 index 參數自行建立索引。

上述有時候也可以用下列方式建立一樣的 Series 物件

實例 1 : 建立索引不是從 0 開始。

>>> import pandas as pd
>>> import numpy as np
>>> myindex = [3,5,7]
>>> price = [100,200,300]
>>> s4 = pd.Series(price, index=myindex)
>>> s4
3    100
5    200
7    300
dtype: int64

實例 2 : 建立含自訂索引的 Series 物件,同時列出結果。

>>> import pandas as pd
>>> fruit = ['Orange', 'Apple', 'Grape']
>>> price = [30, 50, 70]
>>> s5 = pd.Series(price, index=fruit)
>>> s5
Orange    30
Apple     50
Grape     70
dtype: int64

上述有時候也可以用下列方式建立一樣的 Series 物件。

s5 = pd.Series([30,50,70], index=['Orange', 'Apple', 'Grape'])

由上述讀者應該體會到, Series 物件有一個很大的特色,是可以使用任一方式的索引。

使用純量建立 Series 物件

實例 1 :使用純量建立 Series 物件,同時列出結果

>>> import pandas as pd
>>> s6 = pd.Series(9, index=[1,2,3])
>>> s6
1    9
2    9
3    9
dtype: int64

雖然只有一個純量搭配 3 個索引,Pandas 會主動將所有索引值用純量補上。

列出 Series 物件索引與值

從前面實例,可以知道我們可以直接用 print (物件名稱) 列印 Series 物件,其實也可以使用下列方式得到 Series 物件索引。

  • obj.values  #假設物件名稱是 obj ,Series 物件值
  • obj.index   #假設物件名稱是 obj ,Series 物件索引值

實例 1 : 列印 Series 物件索引和值

>>> import pandas as pd
>>> s5 = pd.Series([30,50,80], index=['Orange', 'Apple', 'Grape'])
>>> print(s5.values)
[30 50 80]
>>> print(s5.index)
Index(['Orange', 'Apple', 'Grape'], dtype='object')

Series的運算

Series 運算方式許多和 Numpy 的 ndarray 或是 Python 的串列相同,但有一些擴充更好用的功能。

實例 1 : 可以將切片觀念應用到 Series 物件

>>> import pandas as pd
>>> s = pd.Series([0,1,2,3,4,5])
>>> s[2:4]
2    2
3    3
dtype: int64
>>> s[:3]
0    0
1    1
2    2
dtype: int64
>>> s[2:]
2    2
3    3
4    4
5    5
dtype: int64
>>> s[-1:]
5    5
dtype: int64

四則運算與求餘數的觀念也可以應用在 Series 物件。

實例 2 : Series 物件相加

>>> import pandas as pd
>>> x = pd.Series([1, 2])
>>> y = pd.Series([3, 4])
>>> x + y
0    4
1    6
dtype: int64

實例 3 : Series 物件相乘

>>> import pandas as pd
>>> x = pd.Series([1, 2])
>>> y = pd.Series([3, 4])
>>> x * y
0    3
1    8
dtype: int64

邏輯運算的觀念也可以應用在 Series 物件。

實例 4 : 邏輯運算應用在 Series 物件

>>> import pandas as pd
>>> x = pd.Series([1, 5, 9])
>>> y = pd.Series([2, 4, 8])
>>> x > y
0    False
1     True
2     True
dtype: bool

實例 5 : Series 物件有相同索引,執行相加應用

>>> import pandas as pd
>>> Fruit = ['Orange', 'Apple', 'Grape']
>>> x1 = pd.Series([30,50,80], index=Fruit)
>>> x2 = pd.Series([35,55,85], index=Fruit)
>>> y = x1 + x2
>>> y
Orange     65
Apple     105
Grape     165
dtype: int64

在執行相加時,如果2個索引不相同也可以執行相加,這時不同的索引的索引內容值會填上 NaN(Not a Number) ,可以解釋為非數字或無定義數字。

實例 6 : Series 物件有不相同索引,執行相加應用

>>> import pandas as pd
>>> Fruit1 = ['Orange', 'Apple', 'Grape']
>>> Fruit2 = ['Orange', 'Banana', 'Grape']
>>> x1 = pd.Series([30,50,80], index=Fruit1)
>>> x2 = pd.Series([25,55,85], index=Fruit2)
>>> y = x1 + x2
>>> y
Apple       NaN
Banana      NaN
Grape     165.0
Orange     55.0
dtype: float64

當索引是非數值而是字串時,可以使用下列方式取得元素內容 

實例 7 : Series 的索引是字串,取得元素內容的應用

>>> import pandas as pd
>>> Fruit = ['Orange', 'Apple', 'Grape']
>>> x = pd.Series([30,50,80], index=Fruit)
>>> print(x['Apple'])
50

我們也可以將純量與 Series 物件做運算,甚至也可以將函數應用在 Series 物件

實例 8 : 將純量與函數應用在 Series 物件上

>>> import pandas as pd
>>> Fruit = ['Orange', 'Apple', 'Grape']
>>> x = pd.Series([30,50,80], index=Fruit)
>>> print((x+12)*2)
Orange     84
Apple     124
Grape     184
dtype: int64

上述有列出 float64 表示模組是使用 64位元的浮點數處理此數據 。

Dataframe

Dataframe 是一種二維的陣列資料結構,邏輯上而言可以視為是類似 Excel 的工作表,在這個二維陣列內可以存放整數、浮點數、字串、 Python 物件(例如 :  字串 list、 字典 dict …) 、Numpy 的 ndarray、純量....等。

可以使用 Dataframe() 建立 Dataframe 物件,語法如下:

pandas.Dataframe(data=None, index=None, dtype=None, name=None)

建立 DataFrame 使用 Series

我們可以使用組合 Series 物件成為二維陣列的 DataFrame。組合的方式是使用:

pandas.concat([Series1,Series2,.....], axis=1)

範例 pythonPandas-01.py : 建立 Beijing Hongkong Singapre 2020-2022年3月平均溫度,成為3個 Series 物件,這裡設定 concat() 方法不設定 axis,結果不是我們預期的。

# pythonPandas-01.py
import pandas as pd
years = range(2020, 2023)
beijing = pd.Series([20, 21, 19], index = years)
hongkong = pd.Series([25, 26, 27], index = years)
singapore = pd.Series([30, 29, 31], index = years)
citydf = pd.concat([beijing, hongkong, singapore])  # 預設axis=0
print(type(citydf))
print(citydf)

執行結果

<class 'pandas.core.series.Series'>
2020    20
2021    21
2022    19
2020    25
2021    26
2022    27
2020    30
2021    29
2022    31
dtype: int64

很明顯上述不是我們的預期經過,concat() 方法組合後, citydf 資料型態仍是 Series 物件,問題出現在使用 concat() 組合 Series 物件時, axis 預設為 0 ,如果將第 7 行改為增加 axis=1 參數即可。 

範例 pythonPandas-02.py : 重新設計 pythonPandas-01.py 建立 DataFrame 物件。

# pythonPandas-02.py
import pandas as pd
years = range(2020, 2023)
taipei = pd.Series([20, 21, 19], index = years)
hongkong = pd.Series([25, 26, 27], index = years)
singapore = pd.Series([30, 29, 31], index = years)
citydf = pd.concat([taipei, hongkong, singapore],axis=1)  # axis=1
print(type(citydf))
print(citydf)

執行結果

<class 'pandas.core.frame.DataFrame'>
       0   1   2
2020  20  25  30
2021  21  26  29
2022  19  27  31

上述結果是我們所要的 DataFrame 物件了。

欄位 columns 屬性

上述 pythonPandas-02.py 的執行結果並不完美,是因為欄位 columns 並沒有名稱,在 pandas 中可以使用 columns 屬性設定欄位名稱。

範例 pythonPandas-03.py : 擴充 pythonPandas-02.py 使用 columns 屬性設定欄位名稱。

# pythonPandas-03.py
import pandas as pd
years = range(2020, 2023)
taipei = pd.Series([20, 21, 19], index = years)
hongkong = pd.Series([25, 26, 27], index = years)
singapore = pd.Series([30, 29, 31], index = years)
citydf = pd.concat([taipei, hongkong, singapore],axis=1)  # axis=1
cities = ["Taipei", "HongKong", "Singapore"]
citydf.columns = cities
print(citydf)

執行結果

      Taipei  HongKong  Singapore
2020      20        25         30
2021      21        26         29
2022      19        27         31

Series 物件的屬性

Series 物件有 name 屬性,我們可以在建立物件時,在 Series() 內建立此屬性,也可以物件建立好了以後再設定此屬性。如果有 name 屬性,列印 Series 物件時,就可以看到此屬性。

實例 1 : 建立 Series 物件時同時建立 name

>>> import pandas as pd
>>> taipei = pd.Series([20,23,19], name='Taipei')
>>> taipei
0    20
1    23
2    19
Name: Taipei, dtype: int64

範例 pythonPandas-04.py : 更改 pythonPandas-03.py 設計方式,使用 name 屬性設定 DataFrame columns欄位名稱。

# pythonPandas-04.py
import pandas as pd
years = range(2020, 2023)
taipei = pd.Series([20, 21, 19], index = years)
hongkong = pd.Series([25, 26, 27], index = years)
singapore = pd.Series([30, 29, 31], index = years)
taipei.name = "Taipei"
hongkong.name = "HongKong"
singapore.name = "Singapore"
citydf = pd.concat([taipei, hongkong, singapore],axis=1)  
print(citydf)

執行結果

      Taipei  HongKong  Singapore
2020      20        25         30
2021      21        26         29
2022      19        27         31

使用元素是字典的串列建立 DataFrame 

有一個串列他的元素是字典,可以使用此串列建立 DataFrame

範例 pythonPandas-05.py : 使用元素是字典的串列建立 DataFrame物件

# pythonPandas-05.py
import pandas as pd
data = [{'apple':50,'Orange':30,'Grape':80},{'apple':50,'Grape':80}]
fruits = pd.DataFrame(data)
print(fruits)

執行結果

   apple  Orange  Grape
0    50   30.0    80
1    50    NaN    80

上述如果碰上字典 鍵(key) 沒有對應,該位置將填入NaN。

使用字典建立 DataFrame

一個字典 鍵(key)的值(value) 是串列時,也可以很方便用於建立 DataFrame。

範例 pythonPandas-06.py : 使用字典鍵(key)的值(value) 是串列建立 DataFrame 物件

# pythonPandas-06.py
import pandas as pd
cities = {'country':['Taiwan', 'Japan', 'Singapore'],
          'town':['Taipei','Tokyo','Singapore'],
          'population':[400, 1600, 600]}
citydf = pd.DataFrame(cities)
print(citydf)

執行結果

     country       town  population
0     Taiwan     Taipei        2000
1      Japan      Tokyo        1600
2  Singapore  Singapore         600

index屬性

對於 DataFrame 物件而言,我們可以使用 index 屬性 設定物件的 row 標籤,例如,若是以 範例 pythonPandas-06.py 這個的執行結果而言,0,1,2 索引就是 row 標籤。

範例 pythonPandas-07.py : 重新設計 範例 pythonPandas-06.py 將 row 標籤改為 first, second, third

# pythonPandas-07.py
import pandas as pd
cities = {'country':['Taiwan', 'Japan', 'Singapore'],
          'town':['Taipei','Tokyo','Singapore'],
          'population':[400, 1600, 600]}
rowindex = ['first', 'second', 'third']
citydf = pd.DataFrame(cities, index=rowindex)
print(citydf)

執行結果

          country       town  population
first      Taiwan     Taipei         400
second      Japan      Tokyo        1600
third   Singapore  Singapore         600

將 columns 欄位當作 DataFrame 物件的 index

另外,以字典方式建立 DataFrame,如果字典內某個元素被當作 index 時,這個元素就不會在 DataFrame 的欄位 columns 上出現

範例 pythonPandas-08.py : 重新設計 範例 pythonPandas-07.py ,這個程式會將 country 當作 index

# pythonPandas-08.py
import pandas as pd
cities = {'country':['Taiwan', 'Japan', 'Singapore'],
          'town':['Taipei','Tokyo','Singapore'],
          'population':[400, 1600, 600]}
citydf = pd.DataFrame(cities, columns=["town","population"],
                      index=cities["country"])
print(citydf)

執行結果

                town  population
Taiwan        Taipei         400
Japan          Tokyo        1600
Singapore  Singapore         600

基本 Pandas 資料分析與處理

SeriesDataFrame 物件建立完成後,下一步就是執行資料分析與處理,pandas 提供許多函數或方法,使用者可以針對此執行許多資料分析與處理。本節將講解基本觀念,讀者若想更進一步學習,可以參考 pandas專門的書籍,或是參考 : pandas 官方網站

索引參照屬性

本節將說明下列屬性的用法

  • at :  使用 index 和 columns 內容取得或設定單一元素內容或陣列內容。
  • iat : 使用 index 和 columns 編號取得或設定單一元素內容。
  • loc : 使用 index 和 columns 內容取得或設定整個 row 或 columns 資料和陣列內容。
  • iloc : 使用index和 columns 編號取得或設定整個 row 或 columns 資料 。

範例 pythonPandas-09.py : 在說明上述屬性用法前,先建立一個 DataFrame 物件,然後用此物件做解說。

# pythonPandas-09.py
import pandas as pd
cities = {'Country':['China','China','Thailand','Japan','Singapore'],
          'Town':['Beijing','Shanghai','Bangkok', 'Tokyo','Singapore'],
          'Population':[2000, 2300, 900, 1600, 600]}
df = pd.DataFrame(cities, columns=["Town","Population"],
                  index=cities["Country"])
print(df)

執行結果

                Town  Population
China        Beijing        2000
China       Shanghai        2300
Thailand     Bangkok         900
Japan          Tokyo        1600
Singapore  Singapore         600

下列是 Python Shell 視窗的執行結果,下列實例請在此視窗執行

實例 1 : 使用 at 屬性 row 是 'japan' 和 column 是 ‘Town’ 並列出結果。

>>> df.at['Japan','Town']
'Tokyo'

如果觀察,可以看到有 2 個索引,是 "China",如果是這時可以獲得陣列資料可以參考下列實例

實例 2 : 使用屬性 at 取得 row 是 “China” 和 columns 是 "Town" 並列印出結果。

>>> df.at['China','Town']
China     Beijing
China    Shanghai
Name: Town, dtype: object

實例 3 : 使用屬性 iat 取得 row 是 “2” 和 columns 是 "0" 並列印出結果。

>>> df.iat[2,0]
'Bangkok'

實例 4 : 使用屬性 loc 取得 row 是 “Singapore”  並列印出結果。

>>> df.loc['Singapore']
Town          Singapore
Population          600
Name: Singapore, dtype: object

實例 5 : 使用屬性 loc 取得 row 是 “Japan” 和 "Tailand"  並列印出結果。

>>> df.loc[['Japan','Tailand']]

實例 6 : 使用屬性 loc 取得 row 是 “China”:"Tailand",column 是 "Town":”Population”  並列印出結果。

>>> df.loc['China':'Tailand','Town':'Population']

實例 7 : 使用屬性 iloc 取得 row 是 “0” 的資料, 並列印出結果。

>>> df.iloc[0]
Town          Beijing
Population       2000
Name: China, dtype: object


直接索引

除了上節的方法可以取得 DataFrame 物件內容,也可以使用直接索引方式取內容,在此將沿用 範例 pythonPandas-09.py 建立的 DataFrame 物件 df。

實例 1 : 直接取得索引 'Town' 的資料並列出結果。

>>> df['Town']
China          Beijing
China         Shanghai
Thailand       Bangkok
Japan            Tokyo
Singapore    Singapore
Name: Town, dtype: object

實例 2 : 取得 column 是 'Town' ,row 是 'Japan' 的資料並列出結果。

>>> df['Town']['Japan']
'Tokyo'
實例 3 : 取得 column 是 'Town'  和  'Population'  的資料並列出結果。
>>> df[['Town','Population']]
                Town  Population
China        Beijing        2000
China       Shanghai        2300
Thailand     Bangkok         900
Japan          Tokyo        1600
Singapore  Singapore         600
實例 4 : 取得 row 編號 3 之前的資料並列出結果。
>>> df[:3]
              Town  Population
China      Beijing        2000
China     Shanghai        2300
Thailand   Bangkok         900
實例 5 : 取得 population 大於 1000 的資料並列出結果。
>>> df[df['Population'] > 1000]
           Town  Population
China   Beijing        2000
China  Shanghai        2300
Japan     Tokyo        1600


四則運算方法

  • add() : 加法運算
  • sub() : 減法運算
  • mul() : 乘法運算
  • div() : 除法運算

實例 1 : 加法、減法運算。
>>> import pandas as pd
>>> s1 = pd.Series([1,2,3])
>>> s2 = pd.Series([4,5,6])
>>> x = s1.add(s2)
>>> print(x)
0    5
1    7
2    9
dtype: int64
>>> y = s1.sub(s2)
>>> print(y)
0   -3
1   -3
2   -3
dtype: int64
實例 2 : 乘法、除法運算。
>>> data1 = [{'a':10,'b':20},{'a':30,'b':40}]
>>> df1 = pd.DataFrame(data1)
>>> data2 = [{'a':1,'b':2},{'a':3,'b':4}]
>>> df2 = pd.DataFrame(data2)
>>> x = df1.mul(df2)
>>> print(x)
    a    b
0  10   40
1  90  160
>>> y = df1.div(df2)
>>> print(y)
      a     b
0  10.0  10.0
1  10.0  10.0


邏輯運算方法

  • gt()、lt() : 大於、小於運算
  • ge()、le() : 大於或等於、小於或等於運算
  • eq()、ne() : 等於、不等於運算
實例 1 : 邏輯運算 gt()、eq()
>>> s1 = pd.Series([1,3,9])
>>> s2 = pd.Series([2,4,6])
>>> x = s1.gt(s2)
>>> print(x)
0    False
1    False
2     True
dtype: bool
>>> y = s1.eq(s2)
>>> print(y)
0    False
1    False
2    False
dtype: bool

Numpy 的函數應用在 Pandas

實例 1 : Numpy 的函數 square() 應用在 Pandas 的 Series
>>> import numpy as np
>>> import pandas as pd
>>> s = pd.Series([1,2,3])
>>> x = np.square(s)
>>> print(x)
0    1
1    4
2    9
dtype: int64

範例 pythonPandas-10.py : 將 Numpy 的隨機值函數 randint() 應用在建立 DataFrame 物件元素內容,假設有一個課程,第一次 first、第二次 second、和最後一次成績 final 皆是使用隨機數給予,分數介於 60 -99 之間。程式第6行 np.random.randint(60,100,size=(3,3)) 方法可以建立 (3,3) 陣列,每格的的數據是在 60-99 分之間。

# pythonPandas-10.py
import pandas as pd
import numpy as np
name = ['Frank', 'Peter', 'John']
score = ['first', 'second', 'final']
df = pd.DataFrame(np.random.randint(60,100,size=(3,3)),
                  columns=name,
                  index=score)
print(df)

執行結果

        Frank  Peter  John
first      82     80    69
second     60     96    90
final      98     75    95

NaN 相關的運算

在大數據的資料蒐集中,常常因為執行者的疏忽,漏了收集某一段時間的資料,這些可用 NaN 代替。在先前四則運算,我們沒有對 NaN 的值做運算實例,其實凡與 NaN 作運算所獲得的結果也是 NaN。

實例 1 : NaN 相關的運算
>>> s1 = pd.Series([1,np.nan,5])
>>> s2 = pd.Series([np.nan,6,8])
>>> x = s1.add(s2)
>>> print(x)
0     NaN
1     NaN
2    13.0
dtype: float64


NaN 的處理

下列是適合處理 NaN 的方法 

  • dropna() : 將 NaN 刪除,然後傳回新的 Series 和 DataFrame 物件
  • fillna(value) : 將 NaN 由特定 value值 取代,然後傳回新的 Series 和DataFrame 物件
  • isna() : 判斷是否為 NaN ,如果是傳回 True,如果否傳回 False
  • notna() : 判斷是否為 NaN,如果是傳回False,如果否傳回 True

實例 1 : isna() notna() 的應用。下述 np.nan 是使用 Numpy 模組,然後在指定位置生成 NaN 數據。
>>> df = pd.DataFrame([[1,2,3],[4,np.nan,6],[7,8,np.nan]])
>>> df
   0    1    2
0  1  2.0  3.0
1  4  NaN  6.0
2  7  8.0  NaN
>>> x = df.isna()
>>> print(x)
       0      1      2
0  False  False  False
1  False   True  False
2  False  False   True
>>> x = df.notna()
>>> print(x)
      0      1      2
0  True   True   True
1  True  False   True
2  True   True  False
實例 2 : 沿用前例,將NaN位置填上 0。
>>> z = df.fillna(0)
>>> print(z)
   0    1    2
0  1  2.0  3.0
1  4  0.0  6.0
2  7  8.0  0.0
實例 3 : dropna() 如果不含參數,會刪除含 NaN 的 row。
>>> a = df.dropna()
>>> print(a)
   0    1    2
0  1  2.0  3.0
實例 4 : 刪除含 NaN 的columns。
>>> b = df.dropna(axis='columns')
>>> print(b)
   0
0  1
1  4
2  7

幾個簡單的統計函數

cummax(axis=None) : 傳回指定軸累計的最大值

cummin(axis=None) : 傳回指定軸累計的最小值

cumsum(axis=None) : 傳回指定軸累計的總和

max(axis=None) : 傳回指定軸的最大值

min(axis=None) : 傳回指定軸的最小值

sum(axis=None) : 傳回指定軸的總和

mean(axis=None) : 傳回指定軸的平均數

median(axis=None) : 傳回指定軸的中位數

std(axis=None) : 傳回指定軸的標準差

實例 1 : 在 Python Shell 先執行  範例 pythonPandas-09.py ,以方便取得 DataFrame 物件 df 的數據,然後依此數據,列出人口總和 sum() 和累計人口總數 cumsum()
>>> print(df)
                Town  Population
China        Beijing        2000
China       Shanghai        2300
Thailand     Bangkok         900
Japan          Tokyo        1600
Singapore  Singapore         600
>>> x = df['Population'].sum()
>>> print(x)
7400
>>> y = df['Population'].cumsum()
>>> print(y)
China        2000
China        4300
Thailand     5200
Japan        6800
Singapore    7400
Name: Population, dtype: int64
實例 2 : 延續前 實例1,在 df 物件內插入人口累計總數 Sum_Population 欄位。
>>> df['Cum_Population']=y
>>> print(df)
                Town  Population  Cum_Population
China        Beijing        2000            2000
China       Shanghai        2300            4300
Thailand     Bangkok         900            5200
Japan          Tokyo        1600            6800
Singapore  Singapore         600            7400
實例 3 : 列出最多和最少人口數。
>>> df['Population'].max()
2300
>>> df['Population'].min()
600

範例 pythonPandas-11.py : 有幾位學生的大學學測分數,請建立此 DataFrame 物件,並列印。

# pythonPandas-11.py
import pandas as pd

course = ['Chinese', 'English', 'Math', 'Natural', 'Society']
chinese = [14, 12, 13, 10, 13]
eng = [13, 14, 11, 10, 15]
math = [15, 9, 12, 8, 15]
nature = [15, 10, 13, 10, 15]
social = [12, 11, 14, 9, 14]

df = pd.DataFrame([chinese, eng, math, nature, social],
                  columns = course,
                  index = range(1,6))
print(df)

執行結果

   Chinese  English  Math  Natural  Society
1       14       12    13       10       13
2       13       14    11       10       15
3       15        9    12        8       15
4       15       10    13       10       15
5       12       11    14        9       14
實例 4 : 列出每位學生總分。
>>> total = [df.iloc[i].sum() for i in range(0,5)]
>>> print(total)
[62, 63, 59, 63, 60]
實例 5 : 增加總分欄位,然後列印出此 DataFrame。
>>> df['Total']=total
>>> print(df)
   Chinese  English  Math  Natural  Society  Total
1       14       12    13       10       13     62
2       13       14    11       10       15     63
3       15        9    12        8       15     59
4       15       10    13       10       15     63
5       12       11    14        9       14     60
實例 6 : 列出各科平均數,同時也列出平均分數總分。
>>> ave = df.mean()
>>> print(ave)
Chinese    13.8
English    11.2
Math       12.6
Natural     9.4
Society    14.4
Total      61.4
dtype: float64


增加 index

可使用 loc 屬性為 DataFrame 增加平均分數。

實例 1 : 在 df 下方新增一行 Average 平均分數。

>>> df.loc['Average'] = ave
>>> print(df)
         Chinese  English  Math  Natural  Society  Total
1           14.0     12.0  13.0     10.0     13.0   62.0
2           13.0     14.0  11.0     10.0     15.0   63.0
3           15.0      9.0  12.0      8.0     15.0   59.0
4           15.0     10.0  13.0     10.0     15.0   63.0
5           12.0     11.0  14.0      9.0     14.0   60.0
Average     13.8     11.2  12.6      9.4     14.4   61.4

刪除 index

使用 drop() 刪除 index Average 的那一行資料

實例 1 : 刪除一行 Average 平均分數。

>>> df = df.drop(index=['Average'])
>>> print(df)
   Chinese  English  Math  Natural  Society  Total
1     14.0     12.0  13.0     10.0     13.0   62.0
2     13.0     14.0  11.0     10.0     15.0   63.0
3     15.0      9.0  12.0      8.0     15.0   59.0
4     15.0     10.0  13.0     10.0     15.0   63.0
5     12.0     11.0  14.0      9.0     14.0   60.0

排序

排序可以使用 sort_values()

實例 1 : 將 DataFrame 物件 Total 欄位,從大到小排序。

>>> df = df.sort_values(by='Total', ascending=False)
>>> print(df)
   Chinese  English  Math  Natural  Society  Total
2     13.0     14.0  11.0     10.0     15.0   63.0
4     15.0     10.0  13.0     10.0     15.0   63.0
1     14.0     12.0  13.0     10.0     13.0   62.0
5     12.0     11.0  14.0      9.0     14.0   60.0
3     15.0      9.0  12.0      8.0     15.0   59.0

實例 2 : 增加名次欄位,然後填入名次(Ranking)。

>>> rank = range(1,6)
>>> df['Ranking']=rank
>>> print(df)
   Chinese  English  Math  Natural  Society  Total  Ranking
2     13.0     14.0  11.0     10.0     15.0   63.0        1
4     15.0     10.0  13.0     10.0     15.0   63.0        2
1     14.0     12.0  13.0     10.0     13.0   62.0        3
5     12.0     11.0  14.0      9.0     14.0   60.0        4
3     15.0      9.0  12.0      8.0     15.0   59.0        5

實例 2 中 第1、2名總分相同,應該名次相同都是 第1 名。

實例 3 : 改善相同總分應該相同名次。

>>> for i in range(1,5):
	if df.iat[i,5] == df.iat[i-1,5]:
		df.iat[i,6]= df.iat[i-1,6]
>>> print(df)       
   Chinese  English  Math  Natural  Society  Total  Ranking
2     13.0     14.0  11.0     10.0     15.0   63.0        1
4     15.0     10.0  13.0     10.0     15.0   63.0        1
1     14.0     12.0  13.0     10.0     13.0   62.0        3
5     12.0     11.0  14.0      9.0     14.0   60.0        4
3     15.0      9.0  12.0      8.0     15.0   59.0        5

實例 4 : 依 index 重新排序,可以使用 sort_index()。

>>> df = df.sort_index()
>>> print(df)
   Chinese  English  Math  Natural  Society  Total  Ranking
1     14.0     12.0  13.0     10.0     13.0   62.0        3
2     13.0     14.0  11.0     10.0     15.0   63.0        1
3     15.0      9.0  12.0      8.0     15.0   59.0        5
4     15.0     10.0  13.0     10.0     15.0   63.0        1
5     12.0     11.0  14.0      9.0     14.0   60.0        4

檔案的輸入與輸出

可以讀取的檔案有許多,例如 : TXT、CSV、JSON、Excel….等,也可以將文件以上述資料格式寫入文件。本節將說明讀寫格式的文件。

寫入 CSV 格式檔案

CSV (Comma Separated Values) 格式是電子表格和數據庫中最常見的輸入、輸出文件格式,每一行 row 是一筆資料 record ,每個資料欄位 column 用 "," 逗號隔開,第一行 row 資料是表頭欄位。

Pandas 可以使用 to_csv()  將 DataFrame 物件寫入 CSV 檔案,語法如下:

to_csv(path=None, sep=',' ,header=True, index=True, encoding=None, ....)

  • path : 檔案路徑(名稱)。
  • sep : 分隔字元,預設是 ','。
  • header : 是否保留 columns,預設是 True。
  • index : 是否保留 index,預設是 True。
  • encoding : 檔案編碼方式。

範例 pythonPandas-12.py : 將 範例 pythonPandas-11.py 所建立的 DataFrame 物件,用有保留 headerindex 方式儲存至 output-pythonPandas-12a.csv ,然後用沒有保留方式存入 output-pythonPandas-12b.csv

# pythonPandas-12.py
import pandas as pd

course = ['Chinese', 'English', 'Math', 'Natural', 'Society']
chinese = [14, 12, 13, 10, 13]
eng = [13, 14, 11, 10, 15]
math = [15, 9, 12, 8, 15]
nature = [15, 10, 13, 10, 15]
social = [12, 11, 14, 9, 14]

df = pd.DataFrame([chinese, eng, math, nature, social],
                  columns = course,
                  index = range(1,6))
df.to_csv("output-pythonPandas-12a.csv")
df.to_csv("output-pythonPandas-12b.csv", header=False, index=False)

執行結果

讀取CSV格式檔案

Pandas 可以使用 read_csv() 讀取 CSV 檔案,也可以讀取 TXT檔案,他的語法如下:

read_csv(path=None, sep=',' ,header=True, index_col=None, name=None, encoding=None, userrows=None, usecols=None, ....)

  • path : 檔案路徑(名稱)。
  • sep : 分隔元素,預設是 ','。
  • header : 設定哪一個 row 為欄位標籤,預設是 0,當參數有 name 時,此為 None。如果所讀取的檔案有欄位標籤時,就需要設定此 header 值。
  • index : 指出第幾個欄位 column 是索引,預設是 None。
  • encoding : 檔案編碼方式。
  • nrows : 設定讀取前幾 row。
  • usecols : 設定讀取哪幾欄位。 

範例 pythonPandas-13.py : 分別讀取所建立的 CSV 檔案,然後列印。

# pythonPandas-13.py
import pandas as pd

course = ['Chinese', 'English', 'Math', 'Natural', 'Society']
x = pd.read_csv("out4_12a.csv",index_col=0)
y = pd.read_csv("out4_12b.csv",names=course)
print(x)
print(y)

執行結果

   Chinese  English  Math  Natural  Society
1       14       12    13       10       13
2       13       14    11       10       15
3       15        9    12        8       15
4       15       10    13       10       15
5       12       11    14        9       14
   Chinese  English  Math  Natural  Society
0       14       12    13       10       13
1       13       14    11       10       15
2       15        9    12        8       15
3       15       10    13       10       15
4       12       11    14        9       14

Pandas繪圖

Pandas 內有許多繪圖函數,最常使用的是 plot(),我們可以使用它為 SeriesDataFrame 物件繪圖。基本上這是 Pandas 模組將 matplotlib.pyplot 包裝起來的一個繪圖方法,所以程式設計時需要這個 plot(), 基本語法如下:

plot(x=None, Y=None, kind='xx',title=None, legend=True, rot=None....)

  • kind : 是選擇繪圖模式,預設是 line。常見的選項有 bar、barth、hist、box、scatter.....等。
  • rot : 是旋轉刻度。

使用 Series 畫折線圖表

範例 pythonPandas-14.py : 建立一個 Series 物件 tw,是紀錄 1950到2010年間,每隔10年台灣人口的數據,單位是萬人。

# pythonPandas-14.py
import pandas as pd
import matplotlib.pyplot as plt

population = [860, 1100, 1450, 1800, 2020, 2200, 2260]
tw = pd.Series(population, index=range(1950, 2011, 10))
tw.plot(title='Population in Taiwan')
plt.xlabel("Year")
plt.ylabel("Population")
plt.show()

執行結果

使用 DataFrame 繪圖表基本知識

範例 pythonPandas-15.py : 設計一個世界大城市的人口圖,製作 DataFrame 物件,然後繪製圖表。

# pythonPandas-15.py
import pandas as pd
import matplotlib.pyplot as plt

cities = {'population':[1000, 850, 800, 1500, 600, 800],
          'town':['New York','Chicago','Bangkok','Tokyo',
                   'Singapore','HongKong']}
tw = pd.DataFrame(cities, columns=['population'],index=cities['town'])
          
tw.plot(title='Population in the World')
plt.xlabel('City')
plt.ylabel("Population")
plt.show()

執行結果

直條圖的設計

我們也可以使用適當的 kind 參數,更改不同的圖表設計。

範例 pythonPandas-16.py : 使用直條圖重新設計程式實例 pythonPandas-15.py

# pythonPandas-15.py
import pandas as pd
import matplotlib.pyplot as plt

cities = {'population':[1000, 850, 800, 1500, 600, 800],
          'town':['New York','Chicago','Bangkok','Tokyo',
                   'Singapore','HongKong']}
tw = pd.DataFrame(cities, columns=['population'],index=cities['town'])
          
tw.plot(title='Population in the World')
plt.xlabel('City')
plt.ylabel("Population")
plt.show()

執行結果

調整過後

一個圖表含不同數值資料

我們也可以使用一張圖表建立多個數值資料,例如 : 下列是增加城市面積的數據實例。

範例 pythonPandas-17.py : 擴充 DataFrame ,增加城市面積資料(平方公里)。

# pythonPandas-15.py
import pandas as pd
import matplotlib.pyplot as plt

cities = {'population':[1000, 850, 800, 1500, 600, 800],
          'town':['New York','Chicago','Bangkok','Tokyo',
                   'Singapore','HongKong']}
tw = pd.DataFrame(cities, columns=['population'],index=cities['town'])
          
tw.plot(title='Population in the World')
plt.xlabel('City')
plt.ylabel("Population")
plt.show()

執行結果


在上述程式設計中,將人口數的單位設為"萬",如果我們在程式設計中填入實際人數,若是重新設計上述程式,因為面積與人口數相差太大,將造成面積的資料無法正常顯示。

範例 pythonPandas-18.py : 將人口單位數將為"人",重新設計 pythonPandas-17.py

# pythonPandas-18.py
import pandas as pd
import matplotlib.pyplot as plt

cities = {'population':[10000000,8500000,8000000,15000000,6000000,8000000],
          'area':[400, 500, 850, 300, 200, 320],
          'town':['New York','Chicago','Bangkok','Tokyo',
                   'Singapore','HongKong']}
tw = pd.DataFrame(cities, columns=['population','area'],index=cities['town'])
          
tw.plot(title='Population in the World')
plt.xlabel('City')
plt.show()

執行結果

若是要解決這類問題,建議是增加數值軸,可以參考下一小節。

多個數值軸的設計

這時需又要使用 subplots() ,可以在一個圖表內顯示多組不同軸的數據。

程式第11行,內容如下:

fig, ax = plt.subplots()    # fig 是整個圖表物件,ax 是第一個軸

程式第16行,使用 twinx() 可以建立第2個數值軸,內容如下:

ax2 = ax.twinx()    # 使用 twinx() 可以建立第2個數值軸 ax2

範例 pythonPandas-19.py : 使用第2軸的觀念,重新設計 範例 pythonPandas-18.py

# pythonPandas-19.py
import pandas as pd
import matplotlib.pyplot as plt

cities = {'population':[10000000,8500000,8000000,15000000,6000000,8000000],
          'area':[400, 500, 850, 300, 200, 320],
          'town':['New York','Chicago','Bangkok','Tokyo',
                   'Singapore','HongKong']}
tw = pd.DataFrame(cities, columns=['population','area'],index=cities['town'])

fig, ax = plt.subplots()    # fig 是整個圖表物件,ax 是第一個軸
fig.suptitle("City Statistics")
ax.set_ylabel("Population")
ax.set_xlabel("City")

ax2 = ax.twinx()    # 使用 twinx() 可以建立第2個數值軸 ax2
ax2.set_ylabel("Area")
tw['population'].plot(ax=ax,rot=90)     # 繪製人口數線
tw['area'].plot(ax=ax2, style='g-')     # 繪製面積線
ax.legend(loc=1)                        # 圖例位置在右上
ax2.legend(loc=2)                       # 圖例位置在左上
plt.show()

執行結果

範例 pythonPandas-20.py : 重新設計 範例 pythonPandas-19.py,左側 y 軸不用科學記號表示人口數,此例 第15行增加下列 

ax.ticklabel_format(style='plain')      # 不用科學記號表示

# pythonPandas-20.py
import pandas as pd
import matplotlib.pyplot as plt

cities = {'population':[10000000,8500000,8000000,15000000,6000000,8000000],
          'area':[400, 500, 850, 300, 200, 320],
          'town':['New York','Chicago','Bangkok','Tokyo',
                   'Singapore','HongKong']}
tw = pd.DataFrame(cities, columns=['population','area'],index=cities['town'])

fig, ax = plt.subplots()
fig.suptitle("City Statistics")
ax.set_ylabel("Population")
ax.set_xlabel("City")
ax.ticklabel_format(style='plain')     # 不用科學記號表示
ax2 = ax.twinx()
ax2.set_ylabel("Area")
tw['population'].plot(ax=ax,rot=90)     # 繪製人口數線
tw['area'].plot(ax=ax2, style='g-')     # 繪製面積線
ax.legend(loc=1)                        # 圖例位置在右上
ax2.legend(loc=2)                       # 圖例位置在左上
plt.show()

執行結果

使用 Series 物件設計圓餅圖

繪製圓餅圖可以使用 plot.pie() ,常用的語法如下:

pie(x, options, …..)

x 是一個串列,主要是圓餅圖 X 軸的資料,options 代表系列選擇性參數,可以是下列參數內容:

  • label : 圓餅圖項目所組成的串列。
  • colors : 圓餅圖項目顏色所組成的串列,如果省略則用預設顏色。
  • explode : 可設定是否從圓餅圖分離的串列,0 表示不分離。一般可用 0.1,分離數值越大分離越遠,例如在程式實例 pythonPandas-21.py 中可改用 0.2 測試,效果不同,預設是 0。 
  • autopct : 表示項目的百分比格式,基本語法是 "%格式%%",例如 : "%2.2%%",表示正數2位數,小數2位數。
  • labeldistance : 項目標題與圓餅圖中心的距離是半徑的多少倍,例如 1.2 代表是 1.2 倍。
  • center : 圓中心座標,預設是 0。
  • shadow : True 表示圓餅圖形有陰影,False 表示圓餅圖形沒有陰影,預設是 False。

範例 pythonPandas-21.py : 使用 Series 物件繪製圓餅圖。

# pythonPandas-21.py
import pandas as pd
import matplotlib.pyplot as plt

fruits = ['Apples', 'Bananas', 'Grapes', 'Pears', 'Oranges']
s = pd.Series([2300, 5000, 1200, 2500, 2900], index=fruits,
              name='Fruits Shop')
explode = [0.4, 0, 0, 0.2, 0]
s.plot.pie(explode = explode, autopct='%1.2f%%')
plt.show()

執行結果

時間序列(Time Series)

時間序列是指一系列的數據是依時間次序列出來,時間是指一系列的時間戳記 (timestamp) ,這些時間戳記是相等間隔的時間點。音樂MP3文件或是一些聲音文件,其實就是時間序列的應用,因為音頻會依時間序列排成數據點,將這些數據點視覺化,就可以組成音樂波形。這一節先介紹 Python的 datetime 模組,將它應用在 Series 物件,建立時間序列,然後再介紹 Pandas 處理時間序列的工具。

時間模組 datetime

這一節將講解一個時間模組 datatime,在使用前需導入此模組

from datatime import datatime

datetime 模組的資料型態 datetime 

datetime 模組內有一個資料型態 datetime,可以用它代表一個特定時間,有一個 now() 方法可以列出現在時間。

範例 pythonPandas-22.py : 列出現在時間。

# pythonPandas-22.py
from datetime import datetime

timeNow = datetime.now()
print(type(timeNow))
print("現在時間 : ", timeNow)

執行結果

<class 'datetime.datetime'>
現在時間 :  2021-10-07 12:14:08.031411

我們也可以使用屬性 year、month、day、hour、minuts、second、microsecond(百萬分之一秒),獲得上述時間的個別內容。

範例 pythonPandas-23.py : 列出時間的個別內容。

# pythonPandas-23.py
from datetime import datetime

timeNow = datetime.now()
print(type(timeNow))
print("現在時間 : ", timeNow)
print("年 : ", timeNow.year)
print("月 : ", timeNow.month)
print("日 : ", timeNow.day)
print("時 : ", timeNow.hour)
print("分 : ", timeNow.minute)
print("秒 : ", timeNow.second)

執行結果

<class 'datetime.datetime'>
現在時間 :  2021-10-07 12:22:48.237163
年 :  2021
月 :  10
日 :  7
時 :  12
分 :  22
秒 :  48

設定特定時間

當你了解了獲得現在時間的方法後,其實可以用下列方法設定一個特定時間。

xtime = datatime.datetime(年, 月, 日, 時, 分, 秒)

上述 xtime 就是一個特定時間。

範例 pythonPandas-24.py : 設定程式迴圈執行到 2019年3月11日22點271分0秒將甦醒停止列印 program is sleeping,然後列印 Wake up。

# pythonPandas-24.py
from datetime import datetime

timeStop = datetime(2021,10,7,12,33,0)
while datetime.now() < timeStop:
    print("Program is sleeping.", end="")
print("Wake up")

執行結果

Program is sleeping.Program is sleeping.
Program is sleeping.Program is sleeping.
Program is sleeping.Program is sleeping.
Program is sleeping.Program is sleeping.
Program is sleeping.Program is sleeping.
Program is sleeping.Program is sleeping.
Wake up

一段時間 timedelta

這是 timedelta 的資料型態,代表一段時間,可以用下列方式表示

deltatime = datetime.timedelta(weeks=xx, days=xx, hours=xx, minutes=xx, seconds=xx)

上述 xx 代表設定的單位數

一段時間 物件只有三個屬性, days 代表日數, seconds 代表秒數, microseconds 代表百萬分之一秒。

範例 pythonPandas-25.py : 列印出伊甸時間的日數、秒數、百萬分之一秒數。

# pythonPandas-25.py
from datetime import datetime, timedelta

deltaTime = timedelta(days=3,hours=5,minutes=8,seconds=10)
print(deltaTime.days, deltaTime.seconds, deltaTime.microseconds)

執行結果

3 18490 0

上述5小時8分10秒,被總計為18940秒。另有一個方法, total_second() 可以將一段時間轉為秒數。

範例 pythonPandas-26.py : 重新設計 範例 pythonPandas-25.py ,將一段時間轉為秒數。

# pythonPandas-26.py
from datetime import datetime, timedelta

deltaTime = timedelta(days=3,hours=5,minutes=8,seconds=10)
print(deltaTime.total_seconds())

執行結果

277690.0

使用 Python 的 datetime 模組建立含時間戳記的Series物件

對於時間序列( Time Series ) 而言,基本上就是將索引( index )用日期取代。

範例 pythonPandas-27.py : 使用 datetime 建立含 5 天的 Series 物件和列印,這五天是使用串列 [34, 44, 65, 53, 39] 同時列出時間序列物件的數據型態,以及時間序列的索引 index

# pythonPandas-27.py
import pandas as pd
from datetime import datetime, timedelta

ndays = 5
start = datetime(2019, 3, 11)   
dates = [start + timedelta(days=x) for x in range(0, ndays)]
data = [34, 44, 65, 53, 39]
ts = pd.Series(data, index=dates)
print(type(ts))
print(ts)
print(ts.index)

執行結果

<class 'pandas.core.series.Series'>
2019-03-11    34
2019-03-12    44
2019-03-13    65
2019-03-14    53
2019-03-15    39
dtype: int64
DatetimeIndex(['2019-03-11', '2019-03-12', '2019-03-13', '2019-03-14',
               '2019-03-15'],
              dtype='datetime64[ns]', freq=None)

時間序列使允許相同索引執行加法或代數運算。

 範例 pythonPandas-28.py : 擴充 範例 pythonPandas-27.py ,建立相同時間戳記的 Series 物件,然後計算兩個 Series 物件的相加與計算平均。

# pythonPandas-28.py
import pandas as pd
from datetime import datetime, timedelta

ndays = 5
start = datetime(2019, 3, 11)   
dates = [start + timedelta(days=x) for x in range(0, ndays)]
data1 = [34, 44, 65, 53, 39]
ts1 = pd.Series(data1, index=dates)

data2 = [34, 44, 65, 53, 39]
ts2 = pd.Series(data2, index=dates)

addts = ts1 + ts2
print("ts1+ts2")
print(addts)

meants = (ts1 + ts2)/2
print("(ts1+ts2)/2")
print(meants)

執行結果

ts1+ts2
2019-03-11     68
2019-03-12     88
2019-03-13    130
2019-03-14    106
2019-03-15     78
dtype: int64
(ts1+ts2)/2
2019-03-11    34.0
2019-03-12    44.0
2019-03-13    65.0
2019-03-14    53.0
2019-03-15    39.0
dtype: float64

範例 pythonPandas-29.py : 重新設計 範例 pythonPandas-28.py ,執行兩個 Series 物件相加,但是部分時間戳記是不同。

# pythonPandas-29.py
import pandas as pd
from datetime import datetime, timedelta

ndays = 5
start = datetime(2019, 3, 11)   
dates1 = [start + timedelta(days=x) for x in range(0, ndays)]
data1 = [34, 44, 65, 53, 39]
ts1 = pd.Series(data1, index=dates1)

dates2 = [start - timedelta(days=x) for x in range(0, ndays)]
data2 = [34, 44, 65, 53, 39]
ts2 = pd.Series(data2, index=dates2)

addts = ts1 + ts2
print("ts1+ts2")
print(addts)

執行結果

ts1+ts2
2019-03-07     NaN
2019-03-08     NaN
2019-03-09     NaN
2019-03-10     NaN
2019-03-11    68.0
2019-03-12     NaN
2019-03-13     NaN
2019-03-14     NaN
2019-03-15     NaN
Freq: D, dtype: float64

Pandas 的時間區間方法

Pandas 的 date_range() 可以產生時間區間,我們可以更方便的將此方法應用在前一小節的程式上。

 範例 pythonPandas-30.py : 重新設計 範例 pythonPandas-27.py ,使用 date_range()

# pythonPandas-30.py
import pandas as pd

dates = pd.date_range('3/11/2019', '3/15/2019')
data = [34, 44, 65, 53, 39]
ts = pd.Series(data, index=dates)
print(type(ts))
print(ts)
print(ts.index)

執行結果

<class 'pandas.core.series.Series'>
2019-03-11    34
2019-03-12    44
2019-03-13    65
2019-03-14    53
2019-03-15    39
Freq: D, dtype: int64
DatetimeIndex(['2019-03-11', '2019-03-12', '2019-03-13', '2019-03-14',
               '2019-03-15'],
              dtype='datetime64[ns]', freq='D')

基本上與  範例 pythonPandas-27.py 相同,但是多了註明 “Freq : D”, 表示索引是日期。如果這時我們輸入 ts.index 也將獲得一樣的結果。

上述我們使用 date_range() 方法時,是放了起始日期與終止日期,我們也可以用起始日期(start=)再加上期間(periods=),或是終止日期(end=)再加上期間(periods=)設定時間戳記。

實例 1 : 使用起始日期,加上期間設定時間索引。

>>> import pandas as pd
>>> dates = pd.date_range(start='2021-10-07', periods=5)
>>> dates
DatetimeIndex(['2021-10-07', '2021-10-08', '2021-10-09', '2021-10-10',
               '2021-10-11'],
              dtype='datetime64[ns]', freq='D')

實例 2 : 使用終止日期,加上期間設定時間索引。

>>> dates = pd.date_range(end='2021-10-15', periods=5)
>>> dates
DatetimeIndex(['2021-10-11', '2021-10-12', '2021-10-13', '2021-10-14',
               '2021-10-15'],
              dtype='datetime64[ns]', freq='D')

此外在設定 date_range() 時,若是參數 "freq=" 設定為 "B",可議讓時間索引只包含工作天(work day),相當於週六週日不包含在時間索引內。

實例 3 : 設定 '2021-10-01'  到  '2021-10-10'  時間索引,參數 freq='B' 並觀察執行結果。

>>> dates = pd.date_range('2021-10-01', '2021-10-10', freq='B')
>>> dates
DatetimeIndex(['2021-10-01', '2021-10-04', '2021-10-05', '2021-10-06',
               '2021-10-07', '2021-10-08'],
              dtype='datetime64[ns]', freq='B')

由於 2021-10-01是週六,2021-10-02是週日,所以皆不在時間索引內。若是設定 freq='M' 代表時間索引是兩個時間點之間的月底。

實例 4 : 參數 freq='M' 並觀察執行結果。

>>> dates = pd.date_range('2021-8-01', '2021-10-10', freq='M')
>>> dates
DatetimeIndex(['2021-08-31', '2021-09-30'], dtype='datetime64[ns]', freq='M')

也可以使用 freq='W-Mon',Mon 是週一的縮寫,2個時間點之間,代表每週一皆是時間索引,可以應用在其他日。

實例 5 : 參數  freq='W-Mon' 並觀察執行結果。

>>> dates = pd.date_range('2021-10-01', '2021-10-31', freq='W-Mon')
>>> dates
DatetimeIndex(['2021-10-04', '2021-10-11', '2021-10-18', '2021-10-25'], dtype='datetime64[ns]', freq='W-MON')

其他常用的 freq 設定如下

  • A : 年末
  • AS : 年初
  • Q : 季末
  • QS : 季初
  • H : 小時
  • T : 分鐘
  • S : 秒

專題 鳶尾花

在數據分析領域有一組很有名的資料 iris.csv ,是加州大學爾灣分校機器學習中常被應用的資料,這些數據是由美國植物學家艾德加安德生( Edger Anderson )在加拿大半島實習測量鳶尾花所採集的數據。了解此資料集,請參考 : 這裡

進入 iris.data 下載資料集 後將看到下列部分內容:

總共有150筆資料,在這資料集中總共有五個欄位,左到右分別代表意義如下:

  • 花萼長度( sepal length )
  • 花萼寬度( sepal width )
  • 花瓣長度( petal length )
  • 花瓣寬度( petal width )
  • 鳶尾花類別 (species, 有setosa、versicolor、virginica)
這個專題章節,將教導讀者使用 Python 網路爬蟲功能下載儲存成 iris.txt iris.csv ,然後一步一步使用此資料及配合 Pandas 功能執行分析。

網路爬蟲

由鳶尾花資料及網頁可以發現此網頁內容非常單純,沒有其他 HTML 標籤,所以可以直接讀取然後儲存。

範例 pythonPandas-31.py : 讀取加州大學鳶尾花資料集網頁,然後將此資料集儲存成 iris.csv

# pythonPandas-31.py
import requests

url = 'http://archive.ics.uci.edu/ml/machine-learning-databases/iris/iris.data'
try:
    htmlfile = requests.get(url)    # 將檔案下載至htmlfile
    print('下載成功')
except Exception as err:
    print('下載失敗')

fileName = 'iris.csv'      # 未來儲存鳶尾花的檔案
with open(fileName, 'wb') as fileobj:       # 開啟iris.csv
    for diskstorage in htmlfile.iter_content(10240):
        size = fileobj.write(diskstorage)       # 寫入

執行結果

下載成功

下載成功後,可以在目前工作目錄中發現 iris.csv 檔案,開啟後可看到如下結果:

上述的第13行,用 for 迴圈一次寫入10240 位元組資料,直到全部寫入完成。

鳶尾花資料集轉成 DataFrame

範例 pythonPandas-32.py : 讀取 iris.csv,為此資料集加上欄位名稱,然後列出此資料集的長度和內容。

# pythonPandas-32.py
import pandas as pd

colName = ['sepal_len','sepal_wd','petal_len','petal_wd','species']
iris = pd.read_csv('iris.csv', names = colName)
print('資料集長度 : ', len(iris))
print(iris)

執行結果

資料集長度 :  150
     sepal_len  sepal_wd  petal_len  petal_wd         species
0          5.1       3.5        1.4       0.2     Iris-setosa
1          4.9       3.0        1.4       0.2     Iris-setosa
2          4.7       3.2        1.3       0.2     Iris-setosa
3          4.6       3.1        1.5       0.2     Iris-setosa
4          5.0       3.6        1.4       0.2     Iris-setosa
..         ...       ...        ...       ...             ...
145        6.7       3.0        5.2       2.3  Iris-virginica
146        6.3       2.5        5.0       1.9  Iris-virginica
147        6.5       3.0        5.2       2.0  Iris-virginica
148        6.2       3.4        5.4       2.3  Iris-virginica
149        5.9       3.0        5.1       1.8  Iris-virginica

[150 rows x 5 columns]

建立好上述 DataFrame 後,也可以使用 describe() 獲得數據的數量、均值、標準差、最小、最大、各分位數的值。

實例 1 : 使用 describe() 列出 iris 的相關數據

>>> iris.describe()
        sepal_len    sepal_wd   petal_len    petal_wd
count  150.000000  150.000000  150.000000  150.000000
mean     5.843333    3.054000    3.758667    1.198667
std      0.828066    0.433594    1.764420    0.763161
min      4.300000    2.000000    1.000000    0.100000
25%      5.100000    2.800000    1.600000    0.300000
50%      5.800000    3.000000    4.350000    1.300000
75%      6.400000    3.300000    5.100000    1.800000
max      7.900000    4.400000    6.900000    2.500000

散點圖的製作

繪製散點圖可以使用 plot(....., kind=’scatter’), 另外還要給與 x軸 和 y軸 的座標陣列,由於是由 DataFrame 呼叫 plot() ,所以可以直接使用欄位 columns 名稱即可。 

範例 pythonPandas-33.py : 繪製 (Sepal Length, Sepal Width) 之散點圖。

# pythonPandas-33.py
import pandas as pd
import matplotlib.pyplot as plt

colName = ['sepal_len','sepal_wd','petal_len','petal_wd','species']
iris = pd.read_csv('iris.csv', names = colName)

iris.plot(x='sepal_len',y='sepal_wd',kind='scatter')
plt.xlabel('Sepal Length')
plt.ylabel('Sepal Width')
plt.title('Iris Sepal length and width anslysis')
plt.show()

執行結果

範例 pythonPandas-34.py : 修改 範例 pythonPandas-33.py 第8行, 使用 plot() 方式完成不同顏色和點標記。

# pythonPandas-34.py
import pandas as pd
import matplotlib.pyplot as plt

colName = ['sepal_len','sepal_wd','petal_len','petal_wd','species']
iris = pd.read_csv('iris.csv', names = colName)

plt.plot(iris['sepal_len'],iris['sepal_wd'],'*',color='g')
plt.xlabel('Sepal Length')
plt.ylabel('Sepal Width')
plt.title('Iris Sepal length and width anslysis')
plt.show()

執行結果

對於這類資料分析而言,我們可能想要了解各品種鳶尾花的的花萼長度與寬度之間的關係,其實我們需要將鳶尾花資料集依據品種先分離然後將不同品種的鳶尾花會在同一個圖表上這樣就可以一目了然下列是將不同品種的鳶尾花擷取出來的方法。

實例 1 : 延續先前實例,擷取品種是 versicolor 的鳶尾花

>>> iris_versicolor = iris[iris['species'] == 'Iris-versicolor']
    sepal_len  sepal_wd  petal_len  petal_wd          species
50        7.0       3.2        4.7       1.4  Iris-versicolor
51        6.4       3.2        4.5       1.5  Iris-versicolor
52        6.9       3.1        4.9       1.5  Iris-versicolor
53        5.5       2.3        4.0       1.3  Iris-versicolor
54        6.5       2.8        4.6       1.5  Iris-versicolor
55        5.7       2.8        4.5       1.3  Iris-versicolor
..............

範例 pythonPandas-35.py : 將不同的鳶尾花的花萼是用不同的標記繪製散點圖

# pythonPandas-35.py
import pandas as pd
import matplotlib.pyplot as plt

colName = ['sepal_len','sepal_wd','petal_len','petal_wd','species']
iris = pd.read_csv('iris.csv', names = colName)

# 擷取不同品種的鳶尾花
iris_setosa = iris[iris['species'] == 'Iris-setosa']
iris_versicolor = iris[iris['species'] == 'Iris-versicolor']
iris_virginica = iris[iris['species'] == 'Iris-virginica']
# 繪製散點圖
plt.plot(iris_setosa['sepal_len'],iris_setosa['sepal_wd'],
         '*',color='g',label='setosa')
plt.plot(iris_versicolor['sepal_len'],iris_versicolor['sepal_wd'],
         'x',color='b',label='versicolor')
plt.plot(iris_virginica['sepal_len'],iris_virginica['sepal_wd'],
         '.',color='r',label='virginica')
# 標註軸和標題
plt.xlabel('Sepal Length')
plt.ylabel('Sepal Width')
plt.title('Iris Sepal length and width anslysis')
plt.legend()
plt.show()

執行結果

鳶尾花分類統計與直條圖

如果我們想要獲得不同品種鳶尾花的花瓣與花蕊的均值直條圖,首先須計算統計不同品種鳶尾花的資料,這樣可以使用 groupby() 的方法 

實例 1 : 延續先前實例,統計不同品種鳶尾花的花萼與花瓣的長與寬。

>>> iris_mean = iris.groupby('species', as_index=False).mean()
           species  sepal_len  sepal_wd  petal_len  petal_wd
0      Iris-setosa      5.006     3.418      1.464     0.244
1  Iris-versicolor      5.936     2.770      4.260     1.326
2   Iris-virginica      6.588     2.974      5.552     2.026

範例 pythonPandas-36.py : 以均值和直條圖方式繪製不同品種花萼與花瓣長與寬

# pythonPandas-36.py
import pandas as pd
import matplotlib.pyplot as plt

colName = ['sepal_len','sepal_wd','petal_len','petal_wd','species']
iris = pd.read_csv('iris.csv', names = colName)

# 鳶尾花分組統計均值
iris_mean = iris.groupby('species', as_index=False).mean()
# 繪製直條圖
iris_mean.plot(kind='bar')
# 刻度處理
plt.xticks(iris_mean.index,iris_mean['species'], rotation=0)
plt.show()

執行結果

範例 pythonPandas-37.py : 重新設計 範例 pythonPandas-36.py ,將 x 軸 的品種前方字串 "iris-" 刪除,修改第7行程式碼 : iris['species'] = iris['species'].apply(lambda x: x.replace("Iris-",""))

# pythonPandas-37.py
import pandas as pd
import matplotlib.pyplot as plt

colName = ['sepal_len','sepal_wd','petal_len','petal_wd','species']
iris = pd.read_csv('iris.csv', names = colName)
iris['species'] = iris['species'].apply(lambda x: x.replace("Iris-",""))
# 鳶尾花分組統計均值
iris_mean = iris.groupby('species', as_index=False).mean()
# 繪製直條圖
iris_mean.plot(kind='bar')
# 刻度處理
plt.xticks(iris_mean.index,iris_mean['species'], rotation=0)

plt.show()

執行結果

我們也可以使用堆疊方式處理上述長條圖,方法是在 plot() 方法中增加  "stacked=True"。

範例 pythonPandas-38.py : 重新設計 範例 pythonPandas-37.py ,但是使用堆疊方式處裡數據,修改第11行程式碼 : iris_mean.plot(kind='bar',stacked=True)

# pythonPandas-38.py
import pandas as pd
import matplotlib.pyplot as plt

colName = ['sepal_len','sepal_wd','petal_len','petal_wd','species']
iris = pd.read_csv('iris.csv', names = colName)
iris['species'] = iris['species'].apply(lambda x: x.replace("Iris-",""))
# 鳶尾花分組統計均值
iris_mean = iris.groupby('species', as_index=False).mean()
# 繪製堆疊直條圖
iris_mean.plot(kind='bar',stacked=True)
# 刻度處理
plt.xticks(iris_mean.index,iris_mean['species'], rotation=0)

plt.show()

執行結果

直條圖與橫條圖,差別是 "bar" 與 "barh",參考以下範例。

範例 pythonPandas-39.py : 重新設計 範例 pythonPandas-38.py ,將直條圖改為橫條圖,修改第11行程式碼 :iris_mean.plot(kind='barh',stacked=True),第13行程式碼 : plt.yticks(iris_mean.index,iris_mean['species'], rotation=0)

# pythonPandas-39.py
import pandas as pd
import matplotlib.pyplot as plt

colName = ['sepal_len','sepal_wd','petal_len','petal_wd','species']
iris = pd.read_csv('iris.csv', names = colName)
iris['species'] = iris['species'].apply(lambda x: x.replace("Iris-",""))
# 鳶尾花分組統計均值
iris_mean = iris.groupby('species', as_index=False).mean()
# 繪製堆疊橫條圖
iris_mean.plot(kind='barh',stacked=True)
# 刻度處理
plt.yticks(iris_mean.index,iris_mean['species'], rotation=0)

plt.show()

執行結果

專題 匯入網頁表格資料

Pandas模組有一個匯入網頁資料的方法,如下所示:

  • read_html(‘url’)

我們可以使用這種方式將網頁的表格資料匯入 Python 程式,再轉成 Pandas 的 DataFrame。在股票和基金市場有個很有名的網站,stockq

進入此網站請先點選市場動態 => 再點選全球匯率行情,可以看到下列網頁內容

接下來將一步一步分析,將上述全球貨幣匯率表格轉成 Pandas 的  DataFrame資料。

範例 pythonPandas-40.py : 使用 Pandas 的 read_html(‘url’) 讀取全球貨幣匯率表格。

# pythonPandas-40.py
import pandas as pd

url = 'http://www.stockq.org/market/currency.php'
currencys = pd.read_html(url)

print(type(currencys))              # 列出資料型態
print(currencys)                    # 列出匯率的串列內容

執行結果

<class 'list'>
[                                                   0
0  google_ad_client = "ca-pub-9803646600609510"; ...,                                                    0
0  google_ad_client = "ca-pub-9803646600609510"; ...
1  首頁 市場動態 歷史股價 基金淨值 基金分類 經濟數據總覽 2021行事曆  期貨報告  加...,     0
                      1
0 NaN  google_ad_client = "ca-pub-9803646600609510"; ...,                                                    0     1
0  首頁 市場動態 歷史股價 基金淨值 基金分類 經濟數據總覽 2021行事曆  期貨報告  加...  简体中文,

由上述可以看到我們只用第 4-5 行,就可以讀取的全球匯率行情的資料,同時從第 7 行可以知道,所讀取的資料是 串列(list) 資料型態。從上述可以看到列表內,除了表格還有一系列元素資料,為了可以獲得表格真正的元素位置,我們可以使用下列方式更進一步分析。

範例 pythonPandas-41.py : 列出全球匯率網頁的元素內容,一個元素一個元素列印。

# pythonPandas-41.py
import pandas as pd

url = 'http://www.stockq.org/market/currency.php'
currencys = pd.read_html(url)         # 讀取全球匯率行情表

item = 0
for currency in currencys:
    print("元素 : ", item)            # 列出元素編號
    print(currency)                   # 列出元素內容
    print()
    item += 1

執行結果

元素 :  0
                                                   0
0  google_ad_client = "ca-pub-9803646600609510"; ...

元素 :  1
                                                   0
0  google_ad_client = "ca-pub-9803646600609510"; ...
1  首頁 市場動態 歷史股價 基金淨值 基金分類 經濟數據總覽 2021行事曆  期貨報告  加...
............................................................
元素 :  7
                           0                         1  ...                         3                         4
0   全球匯率 (Currency Exchange)  全球匯率 (Currency Exchange)  ...  全球匯率 (Currency Exchange)  全球匯率 (Currency Exchange)
1                         貨幣                        匯率  ...                        比例                        台北

2                      歐元/美元                    1.1561  ...                     0.06%                     19:02
3                      英鎊/美元                    1.3599  ...                     0.15%                     19:02
4                    美元/瑞士法郎                    0.9258  ...                    -0.13%                     19:02
5                    美元/瑞典克朗                    8.7797  ...                    -0.21%                     19:02
.............

從上述我們可以獲得更明確的結果了,由上述,我們可以知道所要的元素索引是 7,其中元素 7 的 column 標題顯示 "0....4",表示標題"1 2 3"是省略顯示。

範例 pythonPandas-42.py : 顯示元素 7 的 Pandas 之 DataFrame 資料。

# pythonPandas-42.py
import pandas as pd

url = 'http://www.stockq.org/market/currency.php'
currencys = pd.read_html(url)                               # 讀取全球匯率行情表

currency = currencys[7]                                     # 讀取第7元素
currency = currency.drop(currency.index[[0,1]])             # 拋棄前2 row
currency.columns = ['貨幣', '匯率', '漲跌', '比例', '台北'] # 建立column標題
currency.index = range(len(currency.index))                 # 建立row標題
print(currency)

執行結果

      貨幣       匯率    漲跌    比例    台北
0     歐元/美元   1.1561   0.0006   0.06%  19:02
1     英鎊/美元   1.3599   0.0021   0.15%  19:02
2   美元/瑞士法郎   0.9258  -0.0012  -0.13%  19:02
3   美元/瑞典克朗   8.7797  -0.0184  -0.21%  19:02
4    美元/俄盧布  72.0608  -0.3435  -0.47%  19:02
5   美元/匈牙利幣   309.50    -0.64  -0.21%  19:02
6   美元/土耳其幣   8.8690   0.0020   0.02%  19:02
7    美元/南非幣  14.8737  -0.0859  -0.57%  19:02
8   美元/以色列幣   3.2283  -0.0049  -0.15%  19:02
9    美元/摩洛哥   9.0692   0.0022   0.02%  19:02
10    澳幣/美元   0.7296   0.0024   0.34%  19:02
11    紐幣/美元   0.6928   0.0020   0.29%  19:02
12    美元/日圓   111.38    -0.01  -0.01%  19:02
13   美元/人民幣   6.4452   0.0000   0.00%  05:00
14    美元/港幣   7.7845  -0.0016  -0.02%  19:02
15    美元/台幣   27.968   -0.001  -0.00%  19:02
16    美元/韓圜  1189.71     0.25   0.02%  19:02
17    美元/泰銖   33.770   -0.030  -0.09%  19:02
18    美元/新元   1.3576  -0.0006  -0.05%  19:02
19   美元/菲披索   50.343   -0.467  -0.92%  19:02
20   美元/馬來幣   4.1810   0.0005   0.01%  16:14
21   美元/印尼盾  14215.0    -33.5  -0.24%  15:53
22  美元/印度盧比   74.739   -0.011  -0.01%  19:02
23    美元/加幣   1.2578  -0.0007  -0.06%  19:02
24   美元/巴西幣   5.4932   0.0025   0.05%  05:00
25   美元/墨披索  20.5165  -0.0272  -0.13%  19:02
26   美元/阿根廷  98.9100   0.0050   0.01%  09:57
27    美元/智利   812.93     0.00   0.00%  18:55


參考資料

特色、摘要,Feature、Summary:

關鍵字、標籤,Keyword、Tag:

  • Web-Crawler,Data-Mining,Data-Science,Python,Python-Tutorial,


留言

這個網誌中的熱門文章

Ubuntu 常用指令、分類與簡介

網路設定必要參數IP、netmask(遮罩)、Gateway(閘道)、DNS

iptables的觀念與使用

了解、分析登錄檔 - log

Python 與SQLite 資料庫

Blogger文章排版範本

如何撰寫Shell Script

查詢指令或設定 -Linux 線上手冊 - man

網路相關之指令