Pandas

      在〈Pandas〉中尚無留言

Pandas-貓熊

pandas 是 Python 一個重要的數據分析套件,於2009年開源出來,以numpy為基本資料型態,再度創造另一種高效易用的資料格式(DataFrame),可以快速操作分析資料。

Pandas 的資料格式,叫DataFrame。DataFrame似乎不好理解,一般教學也模糊不清。但其實很簡單,只要記得一件事~~凡事以行為主即可。

DataFrame 還有一個超級好用的功能,就是可以把收集到的資料,加以分析處理後,然後儲存成Excel檔案,亦可以存成 csv檔。

使用前, 必需先 pip install pandas, 記注意, 有加 “s” 喔

補充說明

標準正態分佈(Standard normal distribution) : 又稱為u分佈,是以0為均值、以1為標準差的正態分佈,記為N(0,1)

np.random.randn(4,3,2) : 產生三維的正態分佈亂數, 維度可以隨便增加, 也可以是一維

Pandas的資料結構

Series : 類似list, 有自已的索引, 可以自已命名
DataFrame : 類似資料表格, 可以有欄位名, 每列資料錄可以有索引

Series

Series 其實只有一個欄位,但有多筆資料。每筆資料都可以自訂索引,如下圖。

Series的用途,除了可以使用索引指定資料外,也可以使用標籤來指定,比如 s[0],或 s[‘地址’]。

預設索引

產生Series的方式,可以使用 pd.Series([]), 將list或np.array([])或字典 資料傳入, 如下示範list及array的使用方式。

import pandas as pd
import numpy as np
s1=pd.Series([1,2,3,4,5])
print(s1)
print("s1[4]=%d " % s1[4])
s2=pd.Series(np.random.randint(0,3,5))
print(s2)
結果 :
0 1
1 2
2 3
3 4
4 5
dtype: int64
s1[4]=5 
0 1
1 1
2 0
3 0
4 2
dtype: int32

自訂索引

Series 的自訂索引需在 Series 方法中加入 index 參數,index 可以為數字,也可以為字串。

當index為字串時,稱為標籤,同時也會產生由0開始的索引。
當index為數字時,則只有索引,沒有標籤。

另外請注意一件事,index可以為list,亦可以為np 的陣列格式。
pd.Series([資料…], index=[索引…])
pd.Series([資料…], index=np.array([索引…]))

import pandas as pd
index=['mercury','venus','earth','mars','jupiter','saturn','uranus','neptune','pluto']
planet=['水星','金星','地球','火星','木星','土星','天王星','海王星','冥王星']
s=pd.Series(planet, index=index)
print(s)
print(f"s[4]={s[4]}")
print(f"s['jupiter']={s['jupiter']}")
結果 : 
mercury     水星
venus       金星
earth       地球
mars        火星
jupiter     木星
saturn      土星
uranus     天王星
neptune    海王星
pluto      冥王星
dtype: object
s[4]=木星
s['jupiter']=木星

使用字典資料

底下是使用字典資料產生Series格式

import pandas as pd
d={'name':'Thomas', 'sex':'female', 'age':50, 'phone':'0989889123'}
s=pd.Series(d)
print(s)

print('讀取值,可用如下三個方法,分別為索引,標籤,多索引list')
print(f'索引 : {s[0]}')
print(f'標籤 : {s["name"]}')
print(f'多索引 : \n{s[[0,1,2]]}')
print(f'多標籤 : \n{s[['name', 'sex']]}

結果 : 
name Thomas
sex female
age 50
phone 0989889123
dtype: object
讀取值,可用如下三個方法,分別為索引,標籤,多索引list
索引 : Thomas
標籤 : Thomas
多索引 : 
name Thomas
sex female
age 50
多標籤 : 
name Thomas
sex female
dtype: object

上述 s[[0,1,2]] 看起來怪怪的,請記得最外面的 “[]” 是包含索引的符號,而裏面的 “[]” 則是 List。
s[ [list] ]

DataFrame

把 DataFrame 是具有多個欄位,多筆資料的格式。把它想成是一種類似Excel 的二維表格就不難理解了,每個欄位需有欄位名稱,每列需有索引,如下圖的資料所示。

產生DataFrame的方式,有三個要注意,一是欄位名稱,二是每個欄的內容值,三是每列的索引。

總共有三種方式可以產生DataFrame格式,詳述如下。

第一種方式

以行為主,將每行的資料集合在List中,再將每個List 加入以欄位名為 key 的字典

import pandas as pd
data={
    'id': [1,2,3,4,5,6],
    'name' :  ['段譽','虛竹','喬峰','張三豐','張無忌','趙敏'],
    'account' : ['thomas','kevin','eric','wind','kenneth','vivian'],
    'password' : ['1234','2345','3456','4567','5678','6789'],
    'sex' : ['M','M','M','M','M','F'],
    'address': ['大理','少林','大寮','武當','光明頂','蒙古']
    }
df=pd.DataFrame(data)
print(df)
結果 :
   id name  account password sex address
0   1   段譽   thomas     1234   M      大理
1   2   虛竹    kevin     2345   M      少林
2   3   喬峰     eric     3456   M      大寮
3   4  張三豐     wind     4567   M      武當
4   5  張無忌  kenneth     5678   M     光明頂
5   6   趙敏   vivian     6789   F      蒙古

第二種方式

以列為主,每列以 {欄1:值1,欄2:值2, 欄3:值3} 的字典的方式形成,再將每列加入List中

import pandas as pd
data=[]
data.append({'id':1, 'name':'段譽', 'account':'thomas', 'password':'1234', 'sex':'M', 'address': '大理'})
data.append({'id':2, 'name':'虛竹', 'account':'kevin', 'password':'2345', 'sex':'M', 'address': '少林'})
data.append({'id':3, 'name':'喬峰', 'account':'eric', 'password':'3456', 'sex':'M', 'address': '大寮'})
data.append({'id':4, 'name':'張三豐', 'account':'wind', 'password':'4567', 'sex':'M', 'address': '武當'})
data.append({'id':5, 'name':'張無忌', 'account':'kenneth', 'password':'5678', 'sex':'M', 'address': '光明頂'})
data.append({'id':6, 'name':'趟敏', 'account':'vivian', 'password':'6789', 'sex':'F', 'address': '蒙古'})
df=pd.DataFrame(data)
print(df)

第三種方式

也是以列為主,每列的資料集合在 List 中,再將每列加入 List 中。最後指定columns

import pandas as pd
data=[]
data.append([1,'段譽','thomas','1234','M','大理'])
data.append([2, '虛竹', 'kevin', '2345', 'M', '少林'])
data.append([3, '喬峰', 'eric', '3456', 'M', '大寮'])
data.append([4, '張三豐', 'wind', '4567', 'M', '武當'])
data.append([5, '張無忌', 'kenneth', '5678', 'M', '光明頂'])
data.append([6, '趟敏', 'vivian', '6789', 'F', '蒙古'])
cols=['id','name','account','password','sex','address']
df=pd.DataFrame(data=data, columns=cols)
print(df)

plotly_express 練習資料

plotly_express是一個用於資料分析和資料圖表化的套件,在後續的視覺化中會有詳細的說明。
plotly_express 提供了 gapminder 資料表,其格式就是DataFrame。裏面的資料記錄了世界各國的國名,洲別,每年的人口平均壽命,人口數,國別簡碼及國別代碼。
在使用此範例前,請先 pip install plotly-express

底下的代碼會因欄及列的資料太多而無法完整列出,所以會有  … 只列出部分資料

import plotly_express as px
df = px.data.gapminder().query('year == 2007')
print(df)

結果 : 
                 country continent  year  ...     gdpPercap  iso_alpha  iso_num
11           Afghanistan      Asia  2007  ...    974.580338        AFG        4
23               Albania    Europe  2007  ...   5937.029526        ALB        8
35               Algeria    Africa  2007  ...   6223.367465        DZA       12
47                Angola    Africa  2007  ...   4797.231267        AGO       24
59             Argentina  Americas  2007  ...  12779.379640        ARG       32
...                  ...       ...   ...  ...           ...        ...      ...
1655             Vietnam      Asia  2007  ...   2441.576404        VNM      704
1667  West Bank and Gaza      Asia  2007  ...   3025.349798        PSE      275
1679         Yemen, Rep.      Asia  2007  ...   2280.769906        YEM      887
1691              Zambia    Africa  2007  ...   1271.211593        ZMB      894
1703            Zimbabwe    Africa  2007  ...    469.709298        ZWE      716

[142 rows x 8 columns]

完美列印DataFrame

如果想要完整列出所有資料的話,需加四個參數
pd.set_option(‘display.max_columns’, None)
pd.set_option(‘display.max_rows’, None)
pd.set_option(‘display.width’, None)
pd.set_option(‘display.max_colwidth’, None)

import pandas as pd
import plotly_express as px
#顯示所有行
pd.set_option('display.max_columns', None)
#顯示所有列
pd.set_option('display.max_rows', None)
#設定整個表格的寬度
pd.set_option('display.width', None)
#設定每個欄位的最大寬竟,預設為50
pd.set_option('display.max_colwidth', None)
df = px.data.gapminder().query('year == 2007')
print(df)

上述在 set_option裏,使用字串的方式設定,說真的不是個好方法,所以也可以使用如下方式

import pandas as pd
import plotly_express as px
df=px.data.gapminder().query('year==2007')
display=pd.options.display
display.max_columns=None
display.max_rows=None
display.width=None
display.max_colwidth=None
print(df)

df.values

df.values會將所有列轉換成二維的 numpy 陣列,所以可以指定要列印的列數,也可以使用切片。請注意,指定列數時,如下 df.values[[1,4,5,6,7]],外面的 “[]” 為索引,而裏面的”[]” 則為 list. 

import plotly_express as px
import pandas as pd
import numpy as np
dis=pd.options.display
dis.max_columns=None
dis.max_rows=None
dis.width=None
dis.max_colwidth=None
df=px.data.gapminder()
df=df.query("year==2007")

print("指定列 : ")
array=df.values[[1,6,8,10,15,20]]
print(array)

print("陣列切片 : ")
array=df.values[10:21]
print(array)

#陣列可以作切片,也可以指定list
a=np.array([[1,2,3,4,5],[2,3,4,5,6],[3,4,5,6,7]])
print(a[[0,1]])

結果:
指定列 : 
[['Albania' 'Europe' 2007 76.423 3600523 5937.029525999998 'ALB' 8]
 ['Austria' 'Europe' 2007 79.829 8199783 36126.4927 'AUT' 40]
 ['Bangladesh' 'Asia' 2007 64.062 150448339 1391.253792 'BGD' 50]
 ['Benin' 'Africa' 2007 56.728 8078314 1441.284873 'BEN' 204]
 ['Bulgaria' 'Europe' 2007 73.005 7322858 10680.79282 'BGR' 100]
 ['Canada' 'Americas' 2007 80.653 33390141 36319.23501 'CAN' 124]]
陣列切片 : 
[['Benin' 'Africa' 2007 56.728 8078314 1441.284873 'BEN' 204]
 ['Bolivia' 'Americas' 2007 65.554 9119152 3822.137084 'BOL' 68]
 ['Bosnia and Herzegovina' 'Europe' 2007 74.852 4552198 7446.298803 'BIH'
  70]
 ['Botswana' 'Africa' 2007 50.728 1639131 12569.85177 'BWA' 72]
 ['Brazil' 'Americas' 2007 72.39 190010647 9065.800825 'BRA' 76]
 ['Bulgaria' 'Europe' 2007 73.005 7322858 10680.79282 'BGR' 100]
 ['Burkina Faso' 'Africa' 2007 52.295 14326203 1217.032994 'BFA' 854]
 ['Burundi' 'Africa' 2007 49.58 8390505 430.0706916 'BDI' 108]
 ['Cambodia' 'Asia' 2007 59.723 14131858 1713.778686 'KHM' 116]
 ['Cameroon' 'Africa' 2007 50.43 17696293 2042.09524 'CMR' 120]
 ['Canada' 'Americas' 2007 80.653 33390141 36319.23501 'CAN' 124]]
[[1 2 3 4 5]
 [2 3 4 5 6]]

loc

loc可以指定索引編號讀取列,但傳入的索引 list 必需是索引編號存在的數字,比如經過 df.query(“year=2007”) 查詢後,索引編號只剩下 11, 23, 35, 47…..等1000多列。而0,1,2,….等編號都不在了。所以只能使用 df.loc[[11,23,35,47]],但不能使用 df.loc[[0,1,2,3,4,5]]。

另外df.loc[0:100] 亦可以使用切片功能。

請注意 loc傳回的是 DataFrame格式。

import plotly_express as px
import pandas as pd
dis=pd.options.display
dis.max_columns=None
dis.max_rows=None
dis.width=None
dis.max_colwidth=None
df=px.data.gapminder()
df=df.query("year==2007")
df1=df.loc[[11,23,35]]
print(df1)
df2=df.loc[0:100]
print(df2)

讀取行

讀取行時,直接在 df[[……]] list中指定欄位名稱就好。複習一下,讀取列是 df.loc[[….]]。讀取行時,傳回的格式亦為 DataFrame。

import plotly_express as px
import pandas as pd
display=pd.options.display
display.max_columns=None
display.max_rows=None
display.width=None
display.max_colwidth=None
df=px.data.gapminder().query("year==2007 and continent=='Asia'")
print(df[['country', 'continent', 'pop']])

結果:
                 country continent         pop
11           Afghanistan      Asia    31889923
95               Bahrain      Asia      708573
107           Bangladesh      Asia   150448339
227             Cambodia      Asia    14131858
299                China      Asia  1318683096

iloc

iloc可以同時指定列及行。

如 df.iloc[0, 0] 則傳回第 0 列第0行的資料,傳回格式為 string

import plotly_express as px
df=px.data.gapminder()
df=df.query("year==2007")
s=df.iloc[0,0]
print(s, type(s))
結果:
Afghanistan <class 'str'>

行可以作切片,傳回為 Series格式

import plotly_express as px
df=px.data.gapminder()
df=df.query("year==2007")
s=df.iloc[0,0:3]
print(s, type(s))
結果:
country      Afghanistan
continent           Asia
year                2007
Name: 11, dtype: object  <class 'pandas.core.series.Series'>

列及行都可同時作切片,此時傳回的格式為DataFrame格式

import plotly_express as px
df=px.data.gapminder()
df=df.query("year==2007")
s=df.iloc[0:5,0:5]
print(s)
print(type(s))
結果 : 
        country continent  year  lifeExp       pop
11  Afghanistan      Asia  2007   43.828  31889923
23      Albania    Europe  2007   76.423   3600523
35      Algeria    Africa  2007   72.301  33333216
47       Angola    Africa  2007   42.731  12420476
59    Argentina  Americas  2007   75.320  40301927
<class 'pandas.core.frame.DataFrame'>

產生日期格式

pd.date_range()可以產生第一個參數開始的日期格式. 如下, 每列使用日期當索引, 欄位名為 ABCD四欄位

dates=pd.date_range("2019/08/01", periods=6)
df0=pd.DataFrame(np.random.randn(6,4), index=dates, columns=list('ABCD'))
print(df0)

dataframe

df.describe

describe可以求出每一欄位的數量, 平均值, 標準差…等資訊

df0.describe() 及 mem_df.describe()分別如下

dataframe2 dataframe3

其它功能

df.head(2) : 印前二個
df.tail(2) : 印後二個
df.transpose() : 轉置

df.sort_index(axis=1, ascending=True) :
axis=0: 對索引進行排序, axis=1 : 對欄位名稱排序
ascending : False 遞減

import pandas as pd
import numpy as np
dates=pd.period_range('2020/01/01', '2020/01/10')
rng=np.random.RandomState(1)
df=pd.DataFrame(rng.randn(len(dates),4), index=dates, columns=list('ABCD'))
print(df.sort_index(axis=0, ascending=False))
結果 : 
                   A        B        C        D
2020-01-10 -1.117310 0.234416 1.659802 0.742044
2020-01-09 -0.687173 -0.845206 -0.671246 -0.012665
2020-01-08 -0.267888 0.530355 -0.691661 -0.396754
2020-01-07 0.900856 -0.683728 -0.122890 -0.935769
2020-01-06 -1.100619 1.144724 0.901591 0.502494
2020-01-05 -0.172428 -0.877858 0.042214 0.582815
2020-01-04 -0.322417 -0.384054 1.133769 -1.099891
2020-01-03 0.319039 -0.249370 1.462108 -2.060141
2020-01-02 0.865408 -2.301539 1.744812 -0.761207
2020-01-01 1.624345 -0.611756 -0.528172 -1.072969
import pandas as pd
import numpy as np
dates=pd.period_range('2020/01/01', '2020/01/10')
rng=np.random.RandomState(1)
df=pd.DataFrame(rng.randn(len(dates),4), index=dates, columns=list('ABCD'))
print(df.sort_index(axis=1, ascending=False))
結果 :
                   D         C         B        A
2020-01-01 -1.072969 -0.528172 -0.611756 1.624345
2020-01-02 -0.761207 1.744812 -2.301539 0.865408
2020-01-03 -2.060141 1.462108 -0.249370 0.319039
2020-01-04 -1.099891 1.133769 -0.384054 -0.322417
2020-01-05 0.582815 0.042214 -0.877858 -0.172428
2020-01-06 0.502494 0.901591 1.144724 -1.100619
2020-01-07 -0.935769 -0.122890 -0.683728 0.900856
2020-01-08 -0.396754 -0.691661 0.530355 -0.267888
2020-01-09 -0.012665 -0.671246 -0.845206 -0.687173
2020-01-10 0.742044 1.659802 0.234416 -1.117310

df.sort_values(by=’D’,ascending=False) : 使用D欄位遞減排序

df.index : 印出每列的索引
df.columns : 印出欄位名稱
df.values : 傳回每一列的list
df.shape : 傳回df的列數及行數

DataFrame 與 MySQL

取得資料表裏每列的資料,必需使用如 r[2] 這種以索引來指定欄位。但如果欄位太多的話,那不就算的要死。此時就可以使用如下的方式,將資料表轉成DataFrame, 然後再使用欄位名稱,如r[‘tx_closing’] 來取得欄位資料。

import mysql.connector as mysql
import pandas as pd
conn=mysql.connect(host="ip", user="帳號", password="密碼", database="資料庫")
cursor=conn.cursor()
cursor.execute("select * from taiex")
rs=cursor.fetchall()

#資料庫轉DataFrame
descriptions=cursor.description
columns=[d[0] for d in descriptions]
data=[]
for r in rs:
    data.append(r)
df=pd.DataFrame(data=data, columns=columns)
df_price=df[["tx_opening","tx_closing"]]
print(df_price)

#將每列轉成 Series
for r in df.values:
    s=pd.Series(r,index=columns)
    print(s["tx_date"],s["tx_closing"])

結果:
      tx_opening  tx_closing
0        6310.41     6152.43
1        6082.02     6199.91
....................

1999-01-05 6152.43
1999-01-06 6199.91
1999-01-07 6404.31

匯出與讀入Excel

DataFrame可以匯出或讀入Excel檔, 首先需安裝 pip install xlrd openpyxl, 再使用如下指令即可
xlrd : 讀取Excel所需的套件
openpyxl : 儲存成Excel所需的套件

df0.to_excel('foo.xlsx', sheet_name='Sheet1') #存成Excel

df1=pd.read_excel('foo.xlsx', 'Sheet1', index_col=0, na_values=['NA']) #讀入Excel

df2=pd.read_excel('/files/python.xlsx')

讀取儲存格資料

譊取行列的語法,採用 df.iloc[列, 行] ,列與行採用  “,” 隔開。多列或多行使用 “:” 隔開,如下語法
df.iloc[啟始列:結束列, 啟始行:結束行]

1. df.iloc[列, 行] : df.iloc[0,0]讀取第0列, 第0行的資料。請注意,索引編號由 0開始
2. df.iloc[列, [行, 行, 行]] : df.iloc[2,[0,1,2]] 讀取第2列, 0,1,2 行的資料. 使用values取出
3. df.iloc[列, :] : df.iloc[2,:] 讀取第2列所有行的資料. “:”代表所有行(或列). 使用values取出
3. df.iloc[:, 5] : df.iloc[:, 5] 讀取所有列第5列的資料. 使用values取出

import plotly_express as px
df = px.data.gapminder().query('year == 2007')
print('DataFrame 前 3 列資料')
for i in range(3):
    print(df.values[i])
print()

print('列印第0列第0,1,2欄')
row=df.iloc[0,[0,1,2]]
for c in row:
    print(f'{c} ', end='')
print('\n')

row=df.iloc[0,:]
print('第0列所有資料 :\n',row, '\n')
print('list :', list(row))
print('dict :', dict(row))
print('\n')

column=df.iloc[0:3,1]
print('第1行前3列的資料 :\n',column, '\n')
print(column)
print('list :', list(column))
print('dict :', dict(column))
print('\n')

結果:
DataFrame 前 3 列資料
['Afghanistan' 'Asia' 2007 43.828 31889923 974.5803384 'AFG' 4]
['Albania' 'Europe' 2007 76.423 3600523 5937.029525999998 'ALB' 8]
['Algeria' 'Africa' 2007 72.301 33333216 6223.367465 'DZA' 12]

列印第0列第0,1,2欄
Afghanistan Asia 2007 

第0列所有資料 :
 country      Afghanistan
continent           Asia
year                2007
lifeExp           43.828
pop             31889923
gdpPercap     974.580338
iso_alpha            AFG
iso_num                4
Name: 11, dtype: object 

list : ['Afghanistan', 'Asia', 2007, 43.828, 31889923, 974.5803384, 'AFG', 4]
dict : {'country': 'Afghanistan', 'continent': 'Asia', 'year': 2007, 'lifeExp': 43.828, 'pop': 31889923, 'gdpPercap': 974.5803384, 'iso_alpha': 'AFG', 'iso_num': 4}

第1行前3列的資料 :
11      Asia
23    Europe
35    Africa
Name: continent, dtype: object 

11      Asia
23    Europe
35    Africa
Name: continent, dtype: object
list : ['Asia', 'Europe', 'Africa']
dict : {11: 'Asia', 23: 'Europe', 35: 'Africa'}

資料庫功能

#設定搜尋條件
df=(df[df['country']=='Taiwan'])

#選取要列印的欄位
print(df[['country', 'year']])

#新增欄位
df['abb']=d['country'].apply(lambda x:x[:3])

#計算某欄位總合
print(df[df['year']==2007][['pop']].sum())

#計算多欄位的平均
print(df[df['year']==2007][['lifeExp', 'gdpPercap']].mean())

DataFrame視覺化

DataFrame 也可以視覺化製作圖表。但DataFrame使用matplotlib製作圖表,所以必需先
pip install matplotlib

底下df[[list]] 中,list裏的欄位除了要有繪制圖表的資料外,也要包含要顯示在 x 軸及 y 軸的資料。list裏的資料如果是數字才會繪制,文字的部份就不會繪制。

x軸的標籤若要使用國別,可以在plot參數中加入 x=’country’

import plotly_express as px
import pylab as plt
df=px.data.gapminder().query("year==2007 and continent=='Asia'")
print(df.columns)
df[['country','pop']].plot(kind='bar', x='country')
plt.show()

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *