Pandas 入門

Series 和 index 簡介

創(chuàng)建 Series

pd.Series 主要設(shè)置兩個參數(shù),data 和 index,如果不設(shè)置 index,則 index 從 0 開始遞增。除此之外,還可以設(shè)置 dtype。

import pandas as pd
s = pd.Series([20,30,40,50], index=['Eve', 'Bill', 'Lis', 'Bob'])
s
# Eve     20
# Bill    30
# Liz     40
# Bob     50
# dtype: int64
s.index
# Index(['Eve', 'Bill', 'Liz', 'Bob'], dtype='object')
s.values
# array([20, 30, 40, 50], dtype=int64)

除了直接設(shè)置各個字段,還可以將字典作為參數(shù)傳入,series 會自動將 key 作為 index,將 value 作為 data。

d = {k:v**2 for k, v in zip('abcdefghij', range(10))}
s = pd.Series(d, dtype='int64')

為了保證數(shù)據(jù)存取的效率,series 的 index 必須是可哈希的。

索引

series 和字典一樣,索引和修改的時間復(fù)雜度為 O(1)。它有兩種索引方式,第一種索引方式與字典相同,但強烈不推薦,會導(dǎo)致各種副作用:

# 下面這種方法強烈不推薦
s['Eve'] 
# 20
s['Eve':'Liz']
# Eve     20
# Bill    30
# Liz     40
# dtype: int64

在某些情況下,它會產(chǎn)生歧義:

s = pd.Series(['a', 'b', 'c'], index=[2, 0, 1])
print(s[0])
# 'b'
print(s[0:1])
# 'a'
# 即,當(dāng)做切片時,是位置優(yōu)先的,當(dāng)獲取某個元素時,是 label 優(yōu)先的
# 為了解決這種歧義,pandas 引入了 loc 和 iloc 方法,前者索引的是 label,后者是位置下標(biāo)。

第二種索引方式是采用 .loc 方法,這種寫法是推薦的:

s.loc[['Eve']
# 20
s.loc['Eve':'Liz']
# Eve     20
# Bill    30
# Liz     40
# dtype: int64

注意一個細節(jié):利用 index 做切片時,和 list 或者 array 利用下標(biāo)切片,不包括末尾元素不同,index 切片是包括 end 元素的。
除了利用 index 切片,Series 也可以用下標(biāo)切片,此時不包括終點下標(biāo)對應(yīng)的元素,表現(xiàn)和 python 慣例一致:

s.iloc[0]  # 等價于 s.loc['Eve']
s.iloc[0:3]
# Eve     20
# Bill    30
# Liz     40
# dtype: int64

不像字典的 key 是唯一的,Series 支持 index 包含重復(fù)元素。但對 Series 做切片時,如果重復(fù)的 index 不是相鄰的,則會報錯:"Cannot get left\right slice bound for non-unique label: 'xxx' "。

animaux = ['chien', 'chat', 'chat', 'chien', 'poisson']
proprio = ['eve', 'bob','eve','bill','Liz']

s = pd.Series(animaux, index=proprio)
s
# eve       chien
# bob        chat
# eve        chat
# bill      chien
# Liz     poisson
# dtype: object
s.loc['eve':'liz'] 
# KeyError: "Cannot get left slice bound for non-unique label: 'eve'"

因此強烈建議先對 index 排序,這樣可以保證切片能夠一直能正確運行,并且還能提高索引的效率。

s = s.sort_index()
s 
# Liz     poisson
# bill      chien
# bob        chat
# eve       chien
# eve        chat
# dtype: object

# 也可以對 sort_index 設(shè)置 key 參數(shù):
s = s.sort_index(key = lambda x: x.str.lower())
# bill      chien
# bob        chat
# eve       chien
# eve        chat
# Liz     poisson
# dtype: object

和 numpy 一樣,Series 也支持高級索引:

s == 'chien'
# bill     True
# bob     False
# eve      True
# eve     False
# Liz     False
# dtype: bool
s.loc[(s=='chien') | (s=='poisson')] 
# bill      chien
# eve       chien
# Liz     poisson
# dtype: object
s.loc[(s=='chien') | (s=='poisson')]  = 'autre'
# bill    autre
# bob      chat
# eve     autre
# eve      chat
# Liz     autre
# dtype: object

合并

兩個 Series 可以相加,只有相同 label 的數(shù)據(jù)會相加,只存在于其中一個 Series 的數(shù)據(jù)相加后為 NaN,但也可以指定一方缺失的 label 對應(yīng)的默認值:

s1 = pd.Series([1, 2, 3], index=list('abc'))
s2 = pd.Series([5, 6, 7], index=list('acd'))
s1
# a    1
# b    2
# c    3
# dtype: int64

s1 + s2
# a    6.0
# b    NaN
# c    9.0
# d    NaN
# dtype: float64

s1.add(s2, fill_value=50)
# a     6.0
# b    52.0
# c     9.0
# d    57.0
# dtype: float64

數(shù)據(jù)類型

需要注意的是,在操作過程中, series value 的數(shù)據(jù)類型可能會隱式地被改變,如果不注意,很有可能影響增刪的效率,甚至產(chǎn)生錯誤的結(jié)果。

s = pd.Series({k:v**2 for k, v in zip('abcdefghij', range(10))})
print(s.values.dtype) # int64
s['c'] = 'spam'
print(s.values.dtype) # object

影響效率的例子:

s = pd.Series(range(10_000))
print(s.values.dtype) # int64
%timeit s**2
# 158 μs ± 18.3 μs per loop (mean ± std. dev. of 7 runs, 10000 loops each)

s[10_000] = 'spam'
del s[10_000] # 增加了一條數(shù)據(jù),又刪除,導(dǎo)致了數(shù)據(jù)雖然沒變,但是數(shù)據(jù)類型改變了
print(s.values.dtype) # object
%timeit s**2
# 3.95 ms ± 1.01 ms per loop (mean ± std. dev. of 7 runs, 100 loops each)

產(chǎn)生錯誤結(jié)果的例子:

s = pd.Series([1,2,3])
s[3] = '4'
print(s+s)
# 0     2
# 1     4
# 2     6
# 3    44
# dtype: object

DataFrame 簡介

創(chuàng)建 DataFrame

  1. 可以通過多個 Series 創(chuàng)建 DataFrame:
age = pd.Series([30, 20, 50], index = ['alice', 'bob', 'julie])
height = pd.Series([150, 170, 168], index=['alice', 'marc', 'julie'])
# 將字典作為參數(shù)傳入 pd.DataFrame
# 其中字典的鍵為 DataFrame 的列,值為 Series
stat = pd.DataFrame({'age': age, 'height':height})
print(stat)
"""
        age  height
alice  30.0   150.0
bob    20.0     NaN
julie  50.0   168.0
marc    NaN   170.0"""
# 可以看到,DataFrame 將缺失的值設(shè)為 NaN

DataFrame 還支持廣播功能:

stat = pd.DataFrame({'age': age, 'height':height, 'city':Nice })
print(stat)

#         age  height  city
# alice  30.0   150.0  Nice
# bob    20.0     NaN  Nice
# julie  50.0   168.0  Nice
# marc    NaN   170.0  Nice

  1. 還可以通過 numpy array 創(chuàng)建 DataFrame
a = np.random.randint(0,20,9).reshape(3,3)
p = pd.DataFrame(a, index=list('abc'), columns=list('xyz'))
print(p)

#     x   y  z
# a   3   1  0
# b   2   5  1
# c  13  18  5

存取 DataFrame

可以將 DataFrame 保存為 csv 文件或 json 文件

p.to_csv('my_data.csv') # 保存為 csv 文件
p.to_json('my_data.json') # 保存為 json 文件

p2 = pd.read_json('my_data.json')
print(p2) # 和 p 相同

p3 = pd.read_csv('my_data.csv')
print(p3) # 注意,默認情況下 p3 的 index 為 0,1,2,而 a b c 會被認為是一列
#  Unnamed: 0   x   y  z
# 0          a   3   1  0
# 1          b   2   5  1
# 2          c  13  18  5
# 為了避免上面的情況,需要告訴 pandas 第一列為 index 列
p4 = pd.read_csv('my_data.csv', index_col=0)
print(p4)
# x   y  z
# a   3   1  0
# b   2   5  1
# c  13  18  5

編輯 DataFrame

prenoms = ['liz', 'bob', 'bill', 'eve']
age = pd.Series([25, 30, 35, 40], index = prenoms)
taille = pd.Series([160, 175, 170, 180], index = prenoms)
sexe = pd.Series(list('fhhf'), index = prenoms)

df = pd.DataFrame({'age': age, 'taille': taille, 'sexe': sexe})
print(df)

#       age  taille sexe
# liz    25     160    f
# bob    30     175    h
# bill   35     170    h
# eve    40     180    f
  1. df.reset_index 將原來的 index 變成一個 column,并創(chuàng)建新的 index,以數(shù)字為編號:
df = df.reset_index()
print(df)
  index  age  taille sexe
# 0   liz   25     160    f
# 1   bob   30     175    h
# 2  bill   35     170    h
# 3   eve   40     180    f
  1. df.rename(columns={'index': 'prenom','taille':'height'}) 對某列或某幾列改名。
df = df.rename(columns={'index': 'prenom', 'taille': 'height'})
print(df)
#  prenom  age  height sexe
# 0    liz   25     160    f
# 1    bob   30     175    h
# 2   bill   35     170    h
# 3    eve   40     180    f
  1. df.set_index('age') 取出某列,作為新的 index。
df = df.set_index('age')
print(df)

查看元素

包括一系列函數(shù):

import seaborn as sns
tips = sns.load_dataset('tips') # 加載一個現(xiàn)成的DataFrame
tips.head(n=2) # 展示前兩行
tips.tail(n=3) # 展示后三行
# 也可以不設(shè)置 n

names = ['alice', 'bob', 'marc', 'bill', 'sonia']
# créons trois Series qui formeront les trois colonnes
age = pd.Series([12, 13, 16, 11, 16], index=names)
height = pd.Series([130, 140, 176, 120, 165], index=names)
sex = pd.Series(list('fmmmf'), index=names)

# créons maintenant la DataFrame
p = pd.DataFrame({'age': age, 'height': height, 'sex': sex})
print(p)
p.index 
# Index(['alice', 'bob', 'marc', 'bill', 'sonia'], dtype='object')
p.columns
# Index(['age', 'height', 'sex'], dtype='object')
p.values
# array([[12, 130, 'f'],
#        [13, 140, 'm'],
#        [16, 176, 'm'],
#        [11, 120, 'm'],
#        [16, 165, 'f']], dtype=object)

p.T
#        alice  bob marc bill sonia
# age       12   13   16   11    16
# height   130  140  176  120   165
# sex        f    m    m    m     f

p.describe() 默認只顯示數(shù)字列,但也可以設(shè)置參數(shù) include='all' 現(xiàn)實所有列。
p.loc['sonia'] 展示 sonia 行
p.loc['sonia', 'age'] 只顯示 sonia 的年齡

根據(jù)條件篩選元素

篩選出女性條目:

b = p.loc[:, 'sex'] == f # b 是一個 series
b.values # array([ True, False, False, False,  True])
b.index # Index(['alice', 'bob', 'marc', 'bill', 'sonia'], dtype='object') 
p.loc[b, :] 
#       age  height sex
# alice   12     130   f
# sonia   16     165   f

增加年齡篩選條件:

p.loc[(p.loc[:, 'sex']=='f') &(p.loc[:, 'age']>14), :]
#        age  height sex
# sonia   16     165   f

DataFrame.mean() 可以按列計算平均值

幾種不建議的寫法:

p['sex'] # 返回 sex 列
# 但是
p['alice': 'marc'] # 返回的是行,從 alice 到 marc(包含 marc)

p.age # 返回 age 列
# 但是,如果 p 本身有 age 這個 attribute 則優(yōu)先返回 attribute
p.age = 3 # 設(shè)置與 age 列同名的 attribute,那么下次調(diào)用 p.age 時,優(yōu)先返回 3
# 再比如,為 p 增加一列 mean
p['mean'] = 1
# 但是,由于 DataFrame 本身有 mean,那么 p.mean 并不會返回 mean 這列

p.drop(columns=['mean', ], inplace=True) 用于刪除一列或多列,inplace 作用是,設(shè)置是否修改原來的 p,如果True,返回 None,原 p 被修改,如果 False,返回被修改后的 DataFrame,同時原 p 保留。

Universal functions and pandas

DataFrame 支持所有 numpy 的函數(shù),numpy 函數(shù)可以直接施加在 DataFrame 上,例如:

d = pd.DataFrame(np.random.rand(3,3), columns=list('abc'))
np.log(d) # 直接對 values 取 log,同時保留 行、列的label

但是,如果需要用到 DataFrame 的 label 對齊特性,例如兩個 index 順序并不相同的 DataFrame 相加,那么 numpy 的函數(shù)將直接計算中間的 values,而不會考慮它們 label 對齊的問題。(該問題已經(jīng)在 pandas 0.2.5 中被修正)

運算中設(shè)置 fill_value 可以讓表中缺失的數(shù)據(jù)被 fill_value 代替。

names = ['alice', 'bob', 'charle']

bananas = pd.Series([10, 3, 9], index=names)
oranges = pd.Series([3, 11, 6], index=names)
fruits_jan = pd.DataFrame({'bananas': bananas, 'orange': oranges})

bananas = pd.Series([6, 1], index=names[:-1])
apples = pd.Series([8, 5], index=names[1:])
fruits_feb = pd.DataFrame({'bananas': bananas, 'apples': apples})

print(fruits_jan)
#         bananas  orange
# alice        10       3
# bob           3      11
# charle        9       6
print(fruits_feb)
#         bananas  apples
# alice       6.0     NaN
# bob         1.0     8.0
# charle      NaN     5.0

# 不設(shè)置 fill_value 時:
eaten_fruits = fruits_jan + fruits_feb
print(eaten_fruits)
#         apples  bananas  orange
# alice      NaN     16.0     NaN
# bob        NaN      4.0     NaN
# charle     NaN      NaN     NaN

# 設(shè)置后
eaten_fruits = fruits_jan.add(fruits_feb, fillvalue=0.0)
print(eaten_fruits)
#         apples  bananas  orange
# alice      NaN     16.0     3.0
# bob        8.0      4.0    11.0
# charle     5.0      9.0     6.0

# 我們發(fā)現(xiàn),如果一個值在兩個表中都是 NaN,那么這個值仍舊是 NaN

當(dāng)一個 Series 和一個 DataFrame 相加時,pandas 會默認 Series 是一行,并把它廣播到其它行。Series 的 index 會被對應(yīng)到 DataFrame 的列上,并對齊。如果 Series 的 index 與 DataFrame 的列沒關(guān)系,那么會擴增 DataFrame,擴增區(qū)域?qū)?yīng)的數(shù)據(jù)為 NaN。如果想讓 Series 的 index 和 DataFrame 的index 對應(yīng),則需要指定 axis=0:

dataframe.add(series_col, axis=0)

字符串操作

  • 字符串方法只適用于 SeriesIndex,不適用于 DataFrame
  • 字符串方法不改變 NaN,同時,把所有非字符串元素轉(zhuǎn)為 NaN
  • 字符串方法返回一個 Series 或者 Index 的拷貝,不改變輸入的對象
  • 大多數(shù) Python str 類方法在 pandas 中以向量化的形式存在
  • 調(diào)用字符串方法的通用句法為:Series.str.<vectorized method name> 或者 Index.str.<vectorized method name>
names = ['alice ', '  bOB', 'Marc', 'bill', 3, ' JULIE ', np.NaN]
age = pd.Series(names)
print(age)
# 0     alice 
# 1        bOB
# 2       Marc
# 3       bill
# 4          3
# 5     JULIE 
# 6        NaN
# dtype: object

# 將其中的 str 元素變成小寫
a = age.str.lower()
print(a)
# 0     alice 
# 1        bob
# 2       marc
# 3       bill
# 4        NaN
# 5     julie 
# 6        NaN
# dtype: object
# 所有字符串都變成了小寫,此外其中的非字符串元素被轉(zhuǎn)換為了 NaN,而原本的 NaN 沒變

# 刪掉字符串前后多余的空格
a = a.str.strip()
print(a)
# 0    alice
# 1      bob
# 2     marc
# 3     bill
# 4      NaN
# 5    julie
# 6      NaN
# dtype: object

處理缺失數(shù)據(jù)

在構(gòu)建 pandas Series 或者 DataFrame 時,有兩種方式可以表示 NaN,一種是 np.NaN,另一種是python 的 None 對象。np.NaN 的數(shù)據(jù)類型是 float,因此,在 pandas 中,存在 NaN 的對象要么是 float64 的,要么是 object 類型。

s = pd.Series([1, 2], list('xy'))
print(s)
# x    0
# y    1
# dtype: int64

s.loc['y'] = np.NaN
print(s)
# x    0.0
# y    NaN
# dtype: float64
#
# 引入 NaN 后,原本 int64 的數(shù)據(jù)變成了 float64

# 也可以用 Python 的 None 代替 np.NaN,效果是一樣的
s = pd.Series([1, 2], list('xy'))
s.loc['y'] = None
print(s)
# x    0.0
# y    NaN
# dtype: float64

pandas 利用如下方法處理缺失數(shù)據(jù):

  • isna() 返回一個 mask,在缺失數(shù)據(jù)部分為 True,非缺失部分為 Falseisnull() 是它的 alias)。
  • notna() 返回一個和 isna() 相反的 mask, notnull() 是它的 alias
  • dropna() 返回一個沒有 NaN 的新對象
  • fillna() 返回一個 NaN 被替代的新對象
p = pd.DataFrame([[1, 2, np.NaN], [3, np.NaN, np.NaN], [7, 5, np.NaN]])
print(p)
#    0    1   2
# 0  1  2.0 NaN
# 1  3  NaN NaN
# 2  7  5.0 NaN
# ----------------------------------------
# 默認情況下 .dropna() 會把所有包含 NaN 的《行》都刪除。
print(p.dropna())
# Empty DataFrame
# Columns: [0, 1, 2]
# Index: []
# ---------------------------------------
# 設(shè)置 axis,讓它把所有包含 NaN 的《列》都刪除
print(p.dropna(axis=1))
#    0
# 0  1
# 1  3
# 2  7
# --------------------------------------
# 只有該行/列全部為 NaN 時才會刪除它:
print(p.dropna(axis=1, how='all'))
#    0    1
# 0  1  2.0
# 1  3  NaN
# 2  7  5.0
# ------------------------------------
# thresh=n 可以設(shè)置該行/列至少包含幾個 NaN 才會被刪除
print(p.dropna(thresh = 2))
# 第二行有兩個 NaN,遂被刪除
#    0    1   2
# 0  1  2.0 NaN
# 2  7  5.0 NaN

# --------------------------------------
# fillna() 填充 NaN
print(p.fillna(-1))
#    0    1    2
# 0  1  2.0 -1.0
# 1  3 -1.0 -1.0
# 2  7  5.0 -1.0
# 用 NaN 后面一行的數(shù)據(jù)填充 NaN,bfill:backfill
print(p.fillna(method='bfill'))
#    0    1   2
# 0  1  2.0 NaN
# 1  3  5.0 NaN
# 2  7  5.0 NaN
# 用 NaN 前面一列的數(shù)據(jù)填充 NaN,ffill:forward fill
print(p.fillna(method='ffill', axis=1))
#      0    1    2
# 0  1.0  2.0  2.0
# 1  3.0  3.0  3.0
# 2  7.0  5.0  5.0

MultiIndex

MultiIndex 適用于數(shù)組大于二維的情況。所有可以用 Index 的地方,都可以用 MultiIndex。
MultiIndex Series

print(p)
#        age  height sex
# alice   12     130   f
# bob     13     140   m
# sonia   16     165   f
# unstack 可以將一個 DataFrame 變成一個多重索引的 Series
s = p.unstack()
print(s)
# age     alice     12
#         bob       13
#         sonia     16
# height  alice    130
#         bob      140
#         sonia    165
# sex     alice      f
#         bob        m
#         sonia      f
# dtype: object
print(s.index)
# MultiIndex([(   'age', 'alice'),
#             (   'age',   'bob'),
#             (   'age', 'sonia'),
#             ('height', 'alice'),
#             ('height',   'bob'),
#             ('height', 'sonia'),
#             (   'sex', 'alice'),
#             (   'sex',   'bob'),
#             (   'sex', 'sonia')],
#            )
# 所謂 MultiIndex 其實就是把 tuple 作為 index
# -------------------------------------------------------------
# 可以從 list of list 中創(chuàng)建 MultiIndex
names = ['alice']*3+['bob']*3
years = [2014, 2015, 2016]*2
ages = [40, 42, 45, 38, 40, 40]
s_list = pd.Series(ages, index=[names, years])

print(s_list)
# alice  2014    40
#        2015    42
#        2016    45
# bob    2014    38
#        2015    40
#        2016    40
# dtype: int64
# ---------------------------------------------------------------
# 也可以從鍵是 tuple 的字典中創(chuàng)建 MultiIndex 數(shù)據(jù)
s_tuple = pd.Series({('alice', 2014): 40,
                     ('alice', 2015): 42,
                     ('alice', 2016): 45,
                     ('bob', 2014): 38,
                     ('bob', 2015): 40,
                     ('bob', 2016): 40})
print(s_tuple)
# 結(jié)果和上小節(jié)相同
# -----------------------------------------------------------------
# 還可以用 from_product 的方式創(chuàng)建 MultiIndex
# from_product 生成了兩組 index 的所有組合
name = ['alice', 'bob']
year = [2014, 2015, 2016]
i = pd.MultiIndex.from_product([name, year])
s = pd.Series([40, 42, 45, 38, 40, 40], index=i)
print(s)
# 結(jié)果和上上小節(jié)相同
# ----------------------------------------------------------------
# 為 MultiIndex 命名
i = pd.MultiIndex.from_product([name, year], names=['name', 'year'])
s = pd.Series([40, 42, 45, 38, 40, 40], index=i)
print(s)
# name   year
# alice  2014    40
#        2015    42
#        2016    45
# bob    2014    38
#        2015    40
#        2016    40
# dtype: int64
# --------------------------------------------------------------------
# 改變 MultiIndex 的名字
s.index.names = ['Names', 'YEARS']

MultiIndex DataFrame 利用 from_product 可以方便地創(chuàng)建 MultiIndex。

index = pd.MultiIndex.from_product([[2013, 2014],
                                    [1, 2, 3]],
                                   names=['year',
                                          'visit'])

columns = pd.MultiIndex.from_product([['Bob', 'Sue'],
                                      ['avant', 'arrière']],
                                     names=['client',
                                            'pression'])

# on crée des pressions de pneus factices
data = 2 + np.random.rand(6, 4)

# on crée la DataFrame
mecanics_data = pd.DataFrame(data, index=index, columns=columns)
print(mecanics_data)

# client           Bob                 Sue          
# pression       avant   arrière     avant   arrière
# year visit                                        
# 2013 1      2.991843  2.795090  2.388053  2.243434
#      2      2.166630  2.491427  2.506627  2.657610
#      3      2.624870  2.897621  2.094361  2.656947
# 2014 1      2.823338  2.448971  2.155188  2.768642
#      2      2.094320  2.904009  2.141616  2.181879
#      3      2.880923  2.403404  2.916429  2.694188

MultiIndex DataFrame 的索引十分方便

mecanics_data.loc[2013, 'bob']
# pression     avant   arrière
# visit                       
# 1         2.991843  2.795090
# 2         2.166630  2.491427
# 3         2.624870  2.897621

# 為了獲取低級索引的數(shù)據(jù),用到元組:
mecanics_data.loc[(2013, 2), ('Bob', 'avant')]
# 2.166630

元組配合 slice,為 MultiIndex DataFrame 做切片:

print(mecanics_data.loc[slice((2013, 2), (2014, 1)), ('Sue', slice(None))])

# client           Sue          
# pression       avant   arrière
# year visit                    
# 2013 2      2.506627  2.657610
#      3      2.094361  2.656947
# 2014 1      2.155188  2.768642
# ------------------------------------------------------------------
# 對需要全部保留的維度,可以使用 ":",或者 slice(None)
print(mecanics_data.loc[(slice(None), slice(1, 2)), :])
# print(mecanics_data.loc[(slice(None), slice(1, 2)), slice(None)])

# client           Bob                 Sue          
# pression       avant   arrière     avant   arrière
# year visit                                        
# 2013 1      2.991843  2.795090  2.388053  2.243434
#      2      2.166630  2.491427  2.506627  2.657610
# 2014 1      2.823338  2.448971  2.155188  2.768642
#      2      2.094320  2.904009  2.141616  2.181879
# ------------------------------------------------------------------
# 除了 slice 還可以用 pd.IndexSlice
idx = pd.IndexSlice
# 此處 idx第一個維度為一級索引,第二個維度為二級索引,
# 相當(dāng)于 tuple 的第一個元素和第二個元素
print(mecanics_data.loc[idx[:, 1:2], idx['Sue', :]]) 
# client           Sue          
# pression       avant   arrière
# year visit                    
# 2013 1      2.388053  2.243434
#      2      2.506627  2.657610
# 2014 1      2.155188  2.768642
#      2      2.141616  2.181879

高級操作

concat 用于將兩個表拼接起來,它適用于兩個表有相同的 index 或者有相同的 columns。

df1 = pd.DataFrame(np.random.randint(1, 10, size=(2, 2)),
                   columns=list('ab'), index=list('xy'))
df2 = pd.DataFrame(np.random.randint(1, 10, size=(2, 2)),
                   columns=list('cd'), index=list('xy'))

print(df1)
#    a  b
# x  4  8
# y  1  7
print(df2)
#    c  d
# x  1  4
# y  3  3

print(pd.concat((df1, df2), axis=1))
#  a  b  c  d
# x  4  8  1  4
# y  1  7  3  3

concat 也適用于拼接 Series,但是不論是 DataFrame 還是 Series,它不會檢查各行的 index 是否重復(fù)。

s1 = pd.Series([30, 35], index=['alice', 'bob'])
s2 = pd.Series([32, 22, 29], index=['bill', 'alice', 'jo'])
pd.concat([s1, s2])
# alice    30
# bob      35
# bill     32
# alice    22
# jo       29
# dtype: int64
# 上面的拼接結(jié)果中有兩個 alice

一個解決方案是設(shè)置 verify_integrity 參數(shù),它會在遇到兩個相同 index 的時候報錯。但是這無疑會導(dǎo)致額外的計算,因此除非確實必要,一般不設(shè)置它。

try:
    pd.concat([s1, s2], verify_integrity=True)
except ValueError as e:
    print(f"erreur de concaténation:\n{e}")

# erreur de concaténation:
# Indexes have overlapping values: Index(['alice'], dtype='object')

設(shè)置拼接參數(shù)

p1 = pd.DataFrame(np.random.randint(1, 10, size=(2,2)),
                  columns=list('ab'), index=list('xy'))
#    a  b
# x  8  9
# y  7  5

p2 = pd.DataFrame(np.random.randint(1, 10, size=(2,2)),
                  columns=list('ab'), index=list('zt'))
#    c  d
# x  2  3
# y  8  3
# -------------------------------------------------------
# 如果將它們按行拼接,則會出現(xiàn) NaN:
print(pd.concat((p1, p2)))
#    a    b    c    d
# x  8.0  9.0  NaN  NaN
# y  7.0  5.0  NaN  NaN
# x  NaN  NaN  2.0  3.0
# y  NaN  NaN  8.0  3.0
# -------------------------------------------------------
# 設(shè)置 join 參數(shù)
print(pd.concat([p1, p2]), join='inner') # 只保留兩表的交集
# Empty DataFrame
# Columns: []
# Index: [x, y, x, y]

# -------------------------------------------------------
# 調(diào)用 reindex,保留我們想要的行
pd.concat([p1, p2], axis=1).reindex(['x'])
#    a  b  c  d
# x  8  9  2  3
# 如要保留所想要的列,只需要在 reindex 中傳入 axis=1 即可
pd.concat([p1, p2], axis=1).reindex(p2.columns, axis=1)
#    c  d
# x  2  3
# y  8  3

merge 適用于兩個表某相同,然后所有的融合都基于該列:

df1 = pd.DataFrame({'personnel':['Bob', 'Lisa', 'Sue'], 'groupe':['SAF', 'R&D', 'RH']})
df2 = pd.DataFrame({'personnel':['Lisa', 'Bob', 'Sue'], 'date embauche':[2004, 2008, 2014]})
print(df1)
#   personnel groupe
# 0       Bob    SAF
# 1      Lisa    R&D
# 2       Sue     RH
print(df2)
#   personnel  date embauche
# 0      Lisa           2004
# 1       Bob           2008
# 2       Sue           2014
# 上面兩個表中,personnel 列是相同的,因此,merge 時會按照 personnel 列對齊。
print(pd.merge(df1, df2))

#   personnel groupe  date embauche
# 0       Bob    SAF           2008
# 1      Lisa    R&D           2004
# 2       Sue     RH           2014

merge 默認采取 inner join 的策略,如果以某列為基準(zhǔn),那么最終結(jié)果中,只有同時出現(xiàn)在這兩列中的數(shù)據(jù)被保留。
總共有三種merge 的方式:

  • one-to-one, 即在兩表中的各基準(zhǔn)列內(nèi)部,不存在重復(fù)的數(shù)據(jù),是一一對應(yīng)的關(guān)系
  • many-to-one,其中一個表的基準(zhǔn)列中,存在重復(fù)數(shù)據(jù),而另一個表中基準(zhǔn)列內(nèi)部元素都是獨一無二的,因此,merge 時只需要把另一個表中的數(shù)據(jù)復(fù)制 n 份即可。
  • many-to-many,兩個表中基準(zhǔn)列內(nèi)部數(shù)據(jù)都有重復(fù),那么就對各行做笛卡爾乘積。
# ---------------------many-to-one---------------------
df1 = pd.DataFrame({'patient': ['Bob', 'Lisa', 'Sue'],
                    'repas': ['SS', 'SS', 'SSR']})
#   patient repas
# 0     Bob    SS
# 1    Lisa    SS
# 2     Sue   SSR

df2 = pd.DataFrame({'repas': ['SS', 'SSR'],
                    'explication': ['sans sel', 'sans sucre']})
#   repas explication
# 0    SS    sans sel
# 1   SSR  sans sucre
# df1 和 df2 以 repas 為基準(zhǔn)融合,而 df1 中的 repas 列有重復(fù)元素,
# 因此是 one-to-many 的問題,融合后結(jié)果如下:
print(pd.merge(df1, df2))
#   patient repas explication
# 0     Bob    SS    sans sel
# 1    Lisa    SS    sans sel
# 2     Sue   SSR  sans sucre
# ---------------------many-to-many---------------------
df1 = pd.DataFrame({'patient': ['Bob', 'Lisa', 'Sue'],
                    'repas': ['SS', 'SS', 'SSR']})
#   patient repas
# 0     Bob    SS
# 1    Lisa    SS
# 2     Sue   SSR

df2 = pd.DataFrame({'repas': ['SS', 'SS', 'SSR'],
                    'explication': ['sans sel', 'légumes', 'sans sucre']})
#   repas explication
# 0    SS    sans sel
# 1    SS     légumes
# 2   SSR  sans sucre
# 此時 df2 中 repas 列也有重復(fù)元素,依據(jù) many-to-many 的策略,融合結(jié)果如下
print(pd.merge(df1, df2))
#   patient repas explication
# 0     Bob    SS    sans sel
# 1     Bob    SS     légumes
# 2    Lisa    SS    sans sel
# 3    Lisa    SS     légumes
# 4     Sue   SSR  sans sucre
# 本質(zhì)上就是不停地重復(fù),遍歷所有可能。

merge 可以設(shè)置 on= 或者 left_on=、right_on= 顯式指定基準(zhǔn)列

# ------------------------------ on -------------------------------------
df1 = pd.DataFrame({'employee': ['Bob', 'Lisa', 'Sue'],
                    'group': ['Accounting', 'Engineering', 'HR']})
df2 = pd.DataFrame({'employee': ['Lisa', 'Bob', 'Sue'],
                    'hire_date': [2004, 2008, 2014]})
pd.merge(df1, df2, on='employee') # employee 出現(xiàn)在兩個表中
# ------------------------------left\right on-------------------------------------
df1 = pd.DataFrame({'employee': ['Bob', 'Lisa', 'Sue'],
                    'group': ['Accounting', 'Engineering', 'HR']})
df2 = pd.DataFrame({'name': ['Lisa', 'Bob', 'Sue'],
                    'hire_date': [2004, 2008, 2014]})
m = pd.merge(df1, df2, left_on='employee', right_on='name')
# 此時需要注意的問題是,left_on 和 right_on 的列都會被保留,但實際上這兩列是重復(fù)的。
#   employee        group  name  hire_date
# 0      Bob   Accounting   Bob       2008
# 1     Lisa  Engineering  Lisa       2004
# 2      Sue           HR   Sue       2014
# 可以用 
m.drop('name', axis=1) # 刪掉重復(fù)列,它會返回一個新的 DataFrame
 # 設(shè)置 inplace=True,在原 DataFrame 上做修改
m.drop('name', axis=1, inplace=True)

當(dāng)兩表中的基準(zhǔn)列元素不完全一致時,通過設(shè)置 how 有四種 merge 策略:

df1 = pd.DataFrame({'name': ['Bob', 'Lisa', 'Sue'],
                    'pulse': [70, 63, 81]})
#    name  pulse
# 0   Bob     70
# 1  Lisa     63
# 2   Sue     81

df2 = pd.DataFrame({'name': ['Eric', 'Bob', 'Marc'],
                    'weight': [60, 100, 70]})
#    name  weight
# 0  Eric      60
# 1   Bob     100
# 2  Marc      70
# 上述兩表以name為基準(zhǔn)列,但基準(zhǔn)列中只有 Bob 出現(xiàn)在兩個表內(nèi)。
# ------------------------默認情況下,how='inner'------------------------
# 只保留同時出現(xiàn)在兩個表中的數(shù)據(jù)
print(pd.merge(df1, df2, how='inner'))
#   name  pulse  weight
# 0  Bob     70     100
#  ------------------------ how=''outer" ------------------------
# 保留全部條目,缺失數(shù)據(jù)用 NaN 填充
print(pd.merge(df1, df2, how='outer'))
#    name  pulse  weight
# 0   Bob   70.0   100.0
# 1  Lisa   63.0     NaN
# 2   Sue   81.0     NaN
# 3  Eric    NaN    60.0
# 4  Marc    NaN    70.0
# ------------------------ how=''left" ------------------------
# 只保留 df1 的條目
print(pd.merge(df1, df2, how='left'))
#  name  pulse  weight
# 0   Bob     70   100.0
# 1  Lisa     63     NaN
# 2   Sue     81     NaN
# ------------------------ how=''right" ------------------------
和 how='left' 同理

groupby 按照某個指標(biāo)聚類,分別計算各類數(shù)據(jù)

import seaborn as sns
# 加載 titanic 數(shù)據(jù)
ti = sns.load_dataset('titanic').loc[:, ['survived', 'sex', 'class']]
# 計算平均生存率
ti.loc[:, 'survived'].mean()
# 0.38384
# 計算某個等級的生存率
ti.loc[ti.loc[:, 'class']=='Second', 'survived'].mean()
# 0.4728

# 將所有類別的數(shù)據(jù)綜合起來,一起計算(而不是每次設(shè)置條件做篩選)
# 例如,計算不同艙位等級的生存率
print(ti.groupby('class').mean())
#         survived
# class           
# First   0.629630
# Second  0.472826
# Third   0.242363
# 設(shè)置 as_index=False:
print(ti.groupby('class', as_index=False).mean())
#     class  survived
# 0   First  0.629630
# 1  Second  0.472826
# 2   Third  0.242363

# 按照多個指標(biāo)分類,計算各類數(shù)據(jù)
# 例如,同時按照艙位等級和性別分類,計算平均生存率
g = ti.groupby(['class', 'sex']).mean()
print(g)
#                survived
# class  sex             
# First  female  0.968085
#        male    0.368852
# Second female  0.921053
#        male    0.157407
# Third  female  0.500000
#        male    0.135447
g.index # 返回一個 MultiIndex
# MultiIndex([( 'First', 'female'),
#             ( 'First',   'male'),
#             ('Second', 'female'),
#             ('Second',   'male'),
#             ( 'Third', 'female'),
#             ( 'Third',   'male')],
#            names=['class', 'sex'])

groupby 返回值的屬性:

# -------------------------------------groups------------------------------------
d = pd.DataFrame({'key': list('ABCABC'), 'val': range(6)})
#   key  val
# 0   A    0
# 1   B    1
# 2   C    2
# 3   A    3
# 4   B    4
# 5   C    5
g = d.groupby('key')
# <pandas.core.groupby.generic.DataFrameGroupBy object at 0x7f3967995280>
g.groups # groups 屬性
# {'A': [0, 3], 'B': [1, 4], 'C': [2, 5]} 
# .groups 返回一個字典,鍵為組名,值為該組各條數(shù)據(jù)所在的行下標(biāo)

# ------------------------ get_group('A') -------------------------
d = pd.DataFrame({'key': list("ABCABC"), 
                'val1': range(6), 'val2': range(100, 106)})
#   key  val1  val2
# 0   A     0   100
# 1   B     1   101
# 2   C     2   102
# 3   A     3   103
# 4   B     4   104
# 5   C     5   105
g = d.groupby('key')
g.groups # {'A': [0, 3], 'B': [1, 4], 'C': [2, 5]}
g.get_group('A') 
#   key  val1  val2
# 0   A     0   100
# 3   A     3   103
# --------------------------.sum()['val2'] ----------------------------
g.sum()['val2'] 
# key
# A    203
# B    205
# C    207
# Name: val2, dtype: int64
# 同 g['val2'].sum() 
# ---------------------------- for (name, dataframe) in g ----------------------
import seaborn as sns
tips = sns.load_dataset('tips')
g = tips.groupby('day')
for (group, index) in g:
    print(f"On {group} the mean tip is {index['tip'].mean():.3}")

groupby 方法分發(fā)。groupby 返回的對象如果沒有實現(xiàn)某個 DataFrame的方法,該對象仍然可以調(diào)用,只不過是遍歷每個類別,分別調(diào)用。

g = tips.groupby('day')['total_bill']
g.discribe()
#       count  mean  std  min   25%   50%   75%   max
# day                                                
# Thur  62.00 17.68 7.89 7.51 12.44 16.20 20.16 43.11
# Fri   19.00 17.15 8.30 5.75 12.09 15.38 21.75 40.17
# Sat   87.00 20.44 9.48 3.07 13.91 18.24 24.74 50.81
# Sun   76.00 21.41 8.83 7.25 14.99 19.63 25.60 48.17

groupby().agg 方法:agg 中以 list/dict 形式傳入函數(shù)名(或名字的字符串),計算每個組的統(tǒng)計量。

tips.groupby('day').agg(['mean', 'std'])
# tips.groupby('day').agg([np.mean, np.std])
#      total_bill       tip      size     
#            mean  std mean  std mean  std
# day                                     
# Thur      17.68 7.89 2.77 1.24 2.45 1.07
# Fri       17.15 8.30 2.73 1.02 2.11 0.57
# Sat       20.44 9.48 2.99 1.63 2.52 0.82
# Sun       21.41 8.83 3.26 1.23 2.84 1.01
# 也可以以字典形式,為每個 column 設(shè)置 agg 函數(shù)
tips.groupby('day').agg({'tip': np.mean, 'total_bill': np.std})

groupby().filter() filter 內(nèi)傳入篩選條件,可以是 lambda 表達式

d = pd.DataFrame({'key': list('ABCABC'), 
                  'val1': range(6),
                  'val2' : range(100, 106)})
d_sub = d.groupby('key').filter(lambda x: x['val1'].sum()>3)

groupby().transform() transform 內(nèi)傳入變換函數(shù),如 lambda 表達式,變換函數(shù)將施加在每個子 group 上,一個經(jīng)典用例是用它來對每個 group 內(nèi)部中心化,或者用group 均值代替其中的 NaN。

r = np.random.normal(0.5, 2, 4)
d = pd.DataFrame({'key': list('ab'*2), 'data': r,'data2': r*2})
g = d.groupby('key')
g.transform(lambda x: x-x.mean())

以 titanic 的例子,我們希望得到這樣的表格:有三行,每行代表一個艙位級別;有兩列,每列代表一個性別。此時需要用到 pivot_table。pivot_table 相當(dāng)于把 groupby 的結(jié)果表示為二維表格。

g = ti.pivot_table('survived',  # survived 是需要處理的列
                            aggfunc = np.mean, # 需要對數(shù)據(jù)執(zhí)行的操作
                            index = 'class', # 行
                            columns = 'sex') # 設(shè)置列
print(g)
# sex       female      male
# class                     
# First   0.968085  0.368852
# Second  0.921053  0.157407
# Third   0.500000  0.135447

日期和時間序列

numpy 和 pandas 可以很好地處理各種格式的時間字符串,將其轉(zhuǎn)化為標(biāo)準(zhǔn)格式。同時提供了一系列方法,對時間序列求區(qū)間、采樣等等。

import numpy as np
import pandas as pd

#  --------------------------- numpy ---------------------------
# 設(shè)置日期
np.datetime64('2018-06-30')
# numpy.datetime64('2018-06-30')
# 設(shè)置日期和時間
np.datetime64('2018-06-30 08:35:23')
# numpy.datetime64('2018-06-30T08:35:23')
# 求兩個時間點之差
np.datetime64('2018-06-30 08:35:23')-np.datetime('2018-06-20 08:37:23')
# numpy.timedelta64(863880,'s')

# --------------------------- pandas -----------------------------
# 傳入一個時間字符串
pd.to_datetime('10 june 1973 8h30')
# Timestamp('1973-06-10 08:30:00')
# 傳入時間 list
pd.to_datetime(['10 june 1973 8h30', '22-JUNE-1973'])
# DatetimeIndex(['1973-06-10 08:30:00', '1973-06-22 00:00:00'], dtype='datetime64[ns]', freq=None)


# ----------------------- 生成時間序列 -------------------------------
index = pd.date_range('1 jan 2018', periods=10, freq='D') # D 表示以天為單位
# 除了D 還可以用 W(week) M(month)
# DatetimeIndex(['2018-01-01', '2018-01-02', '2018-01-03', '2018-01-04',
#                '2018-01-05', '2018-01-06', '2018-01-07', '2018-01-08',
#                '2018-01-09', '2018-01-10'],
#               dtype='datetime64[ns]', freq='D')

index = pd.date_range('1 jan 2018', periods=1000, freq='43h36t') # freq 也可以寫成 43h36min


# ------------------- 將時間序列作為index -------------------
s = pd.Series(np.random.randint(100, size=1000), index=index) 
s.head()
# 2018-01-01 00:00:00    13
# 2018-01-02 19:36:00    46
# 2018-01-04 15:12:00    23
# 2018-01-06 10:48:00    55
# 2018-01-08 06:24:00    60
# Freq: 2616T, dtype: int32

# ---------------------- 索引 --------------------------------
s['2018']  # 把2018年所有時間篩選出來
s['dec 2018'] # 也可以是 'Dec 2018' '12 2018' '2018-12' 等等,pandas 支持各種格式的時間字符串
#------------------- 切片 ------------------------------------
s['dec 2018': '3 jan 2019'] 

# -------------------- resample ---------------------------
# resample 是按照一定的規(guī)則將時間序列分組
s.resample('W-WED').mean() # 以每周三 W(week)-WED(wednesday) 為節(jié)點分組

# ----------------------- 處理錯誤 --------------------------
date = '100/06/2018'
# 默認會報錯
pd.to_datetime(date)
# ValueError: ('Unknown string format:', '100/06/2018')

# 出現(xiàn)錯誤時返回輸入的字符串
pd.to_datetime(date, errors='ignore')
# '100/06/2018'

# 將錯誤標(biāo)記為 NaT  (Not a time, 后續(xù)按 NaN 處理)
pd.to_datetime(data, errors='coerce')
# NaT
# 下例中最后一項為 NaT
d = pd.to_datetime(['jun 2018', '10/12/1980',
                    '25 january 2000', '100 june 1900'], 
                   errors='coerce')
print(d)
# DatetimeIndex(['2018-06-01', '1980-10-12', '2000-01-25', 'NaT'], dtype='datetime64[ns]', freq=None)

# 可以用處理 NaN 的方法來處理 NaT
d.fillna(pd.to_datetime('10 june 1980'))
# DatetimeIndex(['2018-06-01', '1980-10-12', '2000-01-25', '1980-06-10'], dtype='datetime64[ns]', freq=None)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • pandas Pandas是線上服務(wù)類型,數(shù)據(jù)分析和數(shù)據(jù)處理(在機器學(xué)習(xí)中數(shù)據(jù)處理) 數(shù)據(jù)分析三劍客: numpy...
    Galaxy_saturn閱讀 905評論 0 1
  • pandas入門 簡介 pandas包含的數(shù)據(jù)結(jié)構(gòu)和操作工具能快速簡單地清洗和分析數(shù)據(jù)。 pandas經(jīng)常與Num...
    python測試開發(fā)閱讀 2,471評論 1 16
  • 功能包括但不限于: 1.按軸自動或顯式數(shù)據(jù)對齊功能的數(shù)據(jù)結(jié)構(gòu)2.集成時間序列功能3.數(shù)學(xué)運算和約簡4.靈活處理缺失...
    Shinichi新一君閱讀 779評論 0 1
  • 5.3匯總和計算描述性統(tǒng)計 pandas對象擁有一組常用的數(shù)學(xué)和統(tǒng)計方法。他們大部分都屬于約簡和匯總統(tǒng)計,用于從S...
    凌岸_ing閱讀 1,815評論 0 1
  • 目錄 1.創(chuàng)建對象 2.查看數(shù)據(jù) 3.寫入數(shù)據(jù)(read_csv) 4.寫出/導(dǎo)出數(shù)據(jù)(to_csv) 5.讀取行...
    鯨魚醬375閱讀 1,258評論 0 2

友情鏈接更多精彩內(nèi)容