Pandas快速教程(4)-資料選取和索引
Pandas中Axis的標籤(比如列標籤,行標籤)在資料分析,視覺化等中非常的重要,資料對齊的功能也是基於標籤實現的,正是這些標籤可以讓我們很便利的操作資料,比如選取資料或者構造資料的子集.這一章,我們將聚焦Pandas中的資料選取和索引.
一.列(columns)資料訪問的方式和區別
首先,舉個例子來介紹一下基本概念.
df = pd.DataFrame(np.random.randint(1,10, 8).reshape(4,2), index=list('abcd'), columns=['First', 'Secend']) df Out[19]: First Secend a 7 7 b 6 9 c 2 9 d 2 7
訪問df中列(columns)的資料有兩種不同的方式,為每種方式規定了不同的名稱.
操作符號[ ]的方式,被稱為標準訪問(standard)
操作符號 . 的方式,被稱為屬性訪問(attribute)
這兩種方式都可以訪問到df的列資料,比如:
df.First
Out[24]:
a 7
b 6
c 2
d 2
Name: First, dtype: int32
df['First']
Out[25]:
a 7
b 6
c 2
d 2
Name: First, dtype: int32
這兩種方式雖然都可以訪問資料,但還是存在一些區別的.
1.屬性訪問的方式是不能增加或者建立新的列的資料的,而標準訪問的方式是可以的
df.Three = list(range(len(df.index))) __main__:1: UserWarning: Pandas doesn't allow columns to be created via a new attribute name - see https://pandas.pydata.org/pandas-docs/stable/indexing.html#attribute-access df['Three'] = list(range(len(df.index))) df Out[30]: First Secend Three a 7 7 0 b 6 9 1 c 2 9 2 d 2 7 3
2.屬性訪問的方式中,屬性的命名(既列的名稱)是有限制的,是不能和一些已經存在的函式名或者說關鍵字衝突的,即使真的存在這樣的列名.而標準訪問是不受此限制的.
#為df新增一列min資料作為演示
df['min']=0
df
Out[34]:
First Secend Three min
a 7 7 0 0
b 6 9 1 0
c 2 9 2 0
d 2 7 3 0
df.min
Out[35]:
<bound method DataFrame.min of First Secend Three min
a 7 7 0 0
b 6 9 1 0
c 2 9 2 0
d 2 7 3 0>
df['min']
Out[36]:
a 0
b 0
c 0
d 0
Name: min, dtype: int64
二.行(rows)資料訪問的方式和區別
行資料的有三種方式或者說方法:
loc 基於行標籤(label)的訪問
iloc 基於行位置的訪問(position)
[ ] 標準方式中使用用range範圍
除了標準方式只能接受range範圍外,loc和iloc方法接受的可以是單一的label或position,也可以是多個label或position組成的列表,或者切片方式的range,還可以是boolean array ,甚至可以是callable函式.
需要注意的是,loc的切片方式是閉區間,比如a:c是包含c的,而iloc的切片是開區間,比如1:3是不包含3的.
df.loc['a':'c']
Out[45]:
First Secend Three min
a 7 7 0 0
b 6 9 1 0
c 2 9 2 0
df.iloc[0:2]
Out[46]:
First Secend Three min
a 7 7 0 0
b 6 9 1 0
df[:2]
Out[47]:
First Secend Three min
a 7 7 0 0
b 6 9 1 0
首先來看看loc方法的一些特性.
loc進行boolean選擇
df
Out[51]:
First Secend Three min
a 0 7 0 7
b 0 9 1 6
c 0 9 2 2
d 0 7 3 2
df.loc[:,df.loc['a']>0]
Out[52]:
Secend min
a 7 7
b 9 6
c 9 2
d 7 2
僅對於loc的label索引,如果索引是由多個label組成的列表,而且列表中包含不存在的label, 那麼目前0.23的版本雖然會返回結果,但將會提示錯誤.這時可以使用reindex方法代替.
df
Out[82]:
First Secend Three min
a 0 7 0 7
b 0 9 1 6
c 0 9 2 2
d 0 7 3 2
df.loc[['a','f']]
__main__:1: FutureWarning:
Passing list-likes to .loc or [] with any missing label will raise
KeyError in the future, you can use .reindex() as an alternative.
See the documentation here:
http://pandas.pydata.org/pandas-docs/stable/indexing.html#deprecate-loc-reindex-listlike
Out[83]:
First Secend Three min
a 0.0 7.0 0.0 7.0
f NaN NaN NaN NaN
df.reindex(['a','f'])
Out[84]:
First Secend Three min
a 0.0 7.0 0.0 7.0
f NaN NaN NaN NaN
如果資料的標籤是有序的,注意,一定是有序的.那麼即使切片range時的label不在資料的index中,也是可以自動識別有序標籤範圍內的資料,而不會報keyError.但前提一定是label是有序的,否則就會報keyError.
df.index=list('bcde')
df.sort_index()
Out[61]:
First Secend Three min
b 0 7 0 7
c 0 9 1 6
d 0 9 2 2
e 0 7 3 2
df.loc['a':'f']
Out[63]:
First Secend Three min
b 0 7 0 7
c 0 9 1 6
d 0 9 2 2
e 0 7 3 2
但如果資料的label是無序的,則會報錯.
df.index=list('okwq')
df.loc['a':'z']
Traceback (most recent call last):
File "<ipython-input-65-c6a4da260a0a>", line 1, in <module>
df.loc['a':'z']
........
........
KeyError: 'a'
再來說說iloc的一些特性
用iloc的方式選取資料,如果引數是單獨的position或者position組成的列表,如果position超出bound邊界範圍則或報IndexError的錯誤,但如果使用的是 range切片的方式越界,則只會返回一個空資料,而不會報錯.
df.index=range(len(df['First']))
df
Out[68]:
First Secend Three min
0 0 7 0 7
1 0 9 1 6
2 0 9 2 2
3 0 7 3 2
df.iloc[5]
Traceback (most recent call last):
File "<ipython-input-69-64ee54677b09>", line 1, in <module>
df.iloc[5]
..............
IndexError: single positional indexer is out-of-bounds
df.iloc[6:10]
Out[70]:
Empty DataFrame
Columns: [First, Secend, Three, min]
Index: []
前面提前loc和iloc是可以接受callable函式的
其實這個函式是有一定限制的函式.
The callable
must be a function with one argument (the calling Series, DataFrame or Panel) and that returns valid output for indexing.
例如:
df.loc[lambda df: df['min'] > 2, :]
Out[74]:
First Secend Three min
0 0 7 0 7
1 0 9 1 6
三.隨機抽樣
pandas中提供了sample方法進行隨機抽樣.
sample(n=None, frac=None, replace=False, weights=None, random_state=None, axis=None)
n:代表抽樣的數量
frac:表示整體資料的百分比,不能和n同時使用,自動四捨五入的計算出能抽取的部分
repalce:表示抽樣資料是否有重複值
weights:為每個樣本設定權重,必須與資料長度一致
random_state: 類似random.seed
axis:表示軸的方向,0表示行,1表示列.預設為行方向
舉例如下:
df.sample(n=2)
Out[86]:
First Secend Three min
d 0 7 3 2
c 0 9 2 2
df.sample(n=2,axis=1)
Out[87]:
min Three
a 7 0
b 6 1
c 2 2
d 2 3
df.sample(frac=0.4,axis=0)
Out[88]:
First Secend Three min
a 0 7 0 7
b 0 9 1 6
wei=[0.1,0.9,0.05,0.05]
df.sample(n=1,axis=0)
Out[90]:
First Secend Three min
c 0 9 2 2
df.sample(n=1,axis=0,weights=wei)
Out[93]:
First Secend Three min
b 0 9 1 6
df.sample(n=1,axis=0,weights=wei)
Out[94]:
First Secend Three min
b 0 9 1 6
四.標量的獲取和設定
同loc和iloc一樣,at和iat可以快速設定和獲取標量.
at和loc類似,採用label進行suoin
iat和iloc類似,採用position
同時,若使用at對不存在的label進行賦值,會自動對label所在的行和列進行NaN填充
df
Out[96]:
First Secend Three min
a 0 7 0 7
b 0 9 1 6
c 0 9 2 2
d 0 7 3 2
df.iat[0,0]
Out[97]: 0
df.at['a','First']
Out[98]: 0
df.at['b','Secend']=1000
df.iat[1,1]
Out[100]: 1000
df.at['e','five']=999
df
Out[103]:
First Secend Three min five
a 0.0 7.0 0.0 7.0 NaN
b 0.0 1000.0 1.0 6.0 NaN
c 0.0 9.0 2.0 2.0 NaN
d 0.0 7.0 3.0 2.0 NaN
e NaN NaN NaN NaN 999.0
五.Boolean的索引
使用bool值過濾資料是很常用的,但在pandas中,python的關鍵字or,and,not是不能使用的,所以提供了對應的快捷符號
快捷符號 | 含義 |
& | and |
| | or |
~ | not |
最常用的是對資料的某列進行篩選:
df[df['Secend']>500]
Out[118]:
First Secend Three min five
b 0.0 1000.0 1.0 6.0 NaN
python中的列表表示式和map函式可以用於更復雜的新增篩選:
df2 = pd.DataFrame({'a' : ['one', 'one', 'two', 'three', 'two', 'one', 'six'], 'b' : ['x', 'y', 'y', 'x', 'y', 'x', 'x'],'c' : np.random.randn(7)})
df2
Out[120]:
a b c
0 one x 0.493496
1 one y -1.173728
2 two y -0.409712
3 three x -0.327143
4 two y -0.196210
5 one x -0.531666
6 six x 0.647771
criterion = df2['a'].map(lambda x: x.startswith('t'))
In [155]: df2[criterion]
Out[155]:
a b c
2 two y 0.041290
3 three x 0.361719
4 two y -0.238075
#等同於上一個方法,但效率更低
df2[[x.startswith('t') for x in df2['a']]]
Out[156]:
a b c
2 two y 0.041290
3 three x 0.361719
4 two y -0.238075
df2[criterion & (df2['b'] == 'x')]
Out[157]:
a b c
3 three x 0.361719
六.isin的使用
isin方法提供了精確的篩選
先從Series來看看isin是怎麼工作的
總的來說,isin方法返回的是一個和Series長度相同的boolean值Series,然後可以利用該Boolean值Series進行篩選
s = pd.Series(np.arange(5), index=list('abcde'), dtype='int64')
s
Out[122]:
a 0
b 1
c 2
d 3
e 4
dtype: int64
s.isin([2, 4, 6])
Out[123]:
a False
b False
c True
d False
e True
dtype: bool
s[s.isin([2, 4, 6])]
Out[124]:
c 2
e 4
dtype: int64
對Series的Index也可以進行同樣的篩選
s[s.index.isin(['a','c'])]
Out[126]:
a 0
c 2
dtype: int64
與以上方法有相同作用的另一個方法是:
s.reindex(['a','c'])
Out[127]:
a 0
c 2
dtype: int64
對DataFrame物件來說,isin方法返回的是一個和當前DataFrame的shape屬性一致的一個Boolean值組成的DataFrame
df3 = pd.DataFrame({'vals': [1, 2, 3, 4], 'ids': ['a', 'b', 'f', 'n'], 'ids2': ['a', 'n', 'c', 'n']})
values = ['a', 'b', 1, 3]
df3.isin(values)
Out[130]:
ids ids2 vals
0 True True True
1 True False False
2 False False True
3 False False False
如果需要對指定列進行精確篩選,可以使用字典:
values = {'ids': ['a', 'b'], 'vals': [1, 3]}
df3.isin(values)
Out[132]:
ids ids2 vals
0 True False True
1 True False False
2 False False True
3 False False False
當DataFrame物件進行Boolean的DataFrame 物件的選取時,返回Ture位置的標量值,False的位置則會被NaN填充.
df3[df3.isin(values)]
Out[133]:
ids ids2 vals
0 a NaN 1.0
1 b NaN NaN
2 NaN NaN 3.0
3 NaN NaN NaN
配合any()或者all()方法將可以快速選取出符合標準的子集.
all(1)表示按行計算,預設為0按列計算.
values = {'ids': ['a', 'b'], 'ids2': ['a', 'c'], 'vals': [1, 3]}
row_mask = df3.isin(values).all(1)
df3[row_mask]
Out[171]:
ids ids2 vals
0 a a 1
七.where方法的使用
使用Boolean選取資料,只會返回為True的資料,所有返回的子集的shape可能會與原資料不同
使用where方法,則會返回和原資料shape相同的資料集
對於不滿足新增的,where方法會自動填充NaN
df
Out[179]:
First Secend Three Four
a 3 1 8 8
b 8 7 5 9
c 5 1 9 7
d 9 4 4 4
df.where(df>5)
Out[180]:
First Secend Three Four
a NaN NaN 8.0 8.0
b 8.0 7.0 NaN 9.0
c NaN NaN 9.0 7.0
d 9.0 NaN NaN NaN
同時,也可以為滿足新增的元素快速賦值:
df.where(df<5,10,inplace=True)
df
Out[183]:
First Secend Three Four
a 3 1 10 10
b 10 10 10 10
c 10 1 10 10
d 10 4 4 4
where 方法也是可以接受函式的:
df.where(lambda x:x==10,lambda x:x+10)
Out[186]:
First Secend Three Four
a 13 11 10 10
b 10 10 10 10
c 10 11 10 10
d 10 14 14 14
注意:where方法是操作False值.同時還有一個mask方法,用法與where方法一樣,但mask是與where方法相反的,操作時的True值
八.query 方法的使用
query方法是DataFrame物件專有的
query方法可以使用表示式來選取資料
先看個例子:
df = pd.DataFrame(np.random.randint(1, 10,18).reshape(6,-1), columns=list('abc'))
df
Out[188]:
a b c
0 2 2 5
1 1 9 8
2 2 3 4
3 7 4 3
4 9 7 7
5 5 3 4
df[(df.a < df.b) & (df.b < df.c)]
Out[189]:
a b c
2 2 3 4
df.query('(a < b) & (b < c)')
Out[190]:
a b c
2 2 3 4
query 方法通過使用列或者行的name來簡化一些更為複雜的表達,但前提是該name 一定是存在,如果name出現了重複,優先作為列的name 使用.比如:
df.index.name='a'
df.query('a > b')
Out[197]:
a b c
a
3 7 4 3
4 9 7 7
5 5 3 4
df.query('index > b')
Out[198]:
a b c
a
5 5 3 4
query方法的效率很高,而且可以接受各種複雜的表示式,詳見官網:
九.重複資料處理
pandas提供了duplicated和drop_duplicates兩個方法處理重複值
duplicated返回的是boolean值的Series
drop_duplicates是刪除重複值
為了判別重複值的定義,提供了keep引數
keep=first 預設的,表示保留最早出現的重複值
keep=last 表示保留最晚最現的重複值
keep=False 表示刪除所有重複值
df2 = pd.DataFrame({'a': ['one', 'one', 'two', 'two', 'two', 'three', 'four'],'b': ['x', 'y', 'x', 'y', 'x', 'x', 'x'], 'c': np.random.randn(7)})
df2
Out[269]:
a b c
0 one x -1.067137
1 one y 0.309500
2 two x -0.211056
3 two y -1.842023
4 two x -0.390820
5 three x -1.964475
6 four x 1.298329
In [270]: df2.duplicated('a')
Out[270]:
0 False
1 True
2 False
3 True
4 True
5 False
6 False
dtype: bool
In [271]: df2.duplicated('a', keep='last')
Out[271]:
0 True
1 False
2 True
3 True
4 False
5 False
6 False
dtype: bool
In [272]: df2.duplicated('a', keep=False)
Out[272]:
0 True
1 True
2 True
3 True
4 True
5 False
6 False
dtype: bool
In [273]: df2.drop_duplicates('a')
Out[273]:
a b c
0 one x -1.067137
2 two x -0.211056
5 three x -1.964475
6 four x 1.298329
In [274]: df2.drop_duplicates('a', keep='last')
Out[274]:
a b c
1 one y 0.309500
4 two x -0.390820
5 three x -1.964475
6 four x 1.298329
In [275]: df2.drop_duplicates('a', keep=False)
Out[275]:
a b c
5 three x -1.964475
6 four x 1.298329
也可以對多列去重,那麼將多列聯合起來再來識別是否重複
In [276]: df2.duplicated(['a', 'b'])
Out[276]:
0 False
1 False
2 False
3 False
4 True
5 False
6 False
dtype: bool
In [277]: df2.drop_duplicates(['a', 'b'])
Out[277]:
a b c
0 one x -1.067137
1 one y 0.309500
2 two x -0.211056
3 two y -1.842023
5 three x -1.964475
6 four x 1.298329
十.Index物件
Index物件在pandas中有這重要的地位,資料索引,資料自動對齊等功能對依靠Index物件實現.
Index物件的建立,只需要一個序列物件即可.
index=pd.Index(list('abcd'))
'd' in index
Out[206]: True
Index物件是有name屬性的,如果設定了,是可以在資料列印是顯示的.
index=pd.Index(list('abcd'),name='Index')
index
Out[208]: Index(['a', 'b', 'c', 'd'], dtype='object', name='Index')
因為Index物件涉及的資料對齊等功能設定,所以Index物件時不可變的(“mostly immutable”).也就是說是無法單個變更Index物件中的某個索引值的.
但Index的原屬性(metadata)是可以通過一些方法改變的.
例如name,level 等可以通過rename,set_names,set_levels方法進行變更,但這些方法預設是返回一個原物件的 copy, 如果要變更原物件的可以使用inplace引數
index
Out[208]: Index(['a', 'b', 'c', 'd'], dtype='object', name='Index')
index.rename('apple')
Out[209]: Index(['a', 'b', 'c', 'd'], dtype='object', name='apple')
index
Out[210]: Index(['a', 'b', 'c', 'd'], dtype='object', name='Index')
index.rename('water',inplace=True)
index
Out[212]: Index(['a', 'b', 'c', 'd'], dtype='object', name='water')
Index物件提供了集合運算功能.
union(|): 合集
intersection(&):交集
difference:差集
symmetirc_difference(^):既在a 中,又在b中,但不同時在a,b中
a=pd.Index(list('abcd'))
b=pd.Index(list('bcde'))
a | b
Out[215]: Index(['a', 'b', 'c', 'd', 'e'], dtype='object')
a & b
Out[216]: Index(['b', 'c', 'd'], dtype='object')
a.difference(b)
Out[217]: Index(['a'], dtype='object')
a ^ b
Out[218]: Index(['a', 'e'], dtype='object')
Index物件的修改有兩種方式
一是直接對物件的index屬性進行修改
而是使用set_index方法,將DataFrame的某一列指定為Index
df
Out[219]:
a b c
a
0 2 2 5
1 1 9 8
2 2 3 4
3 7 4 3
4 9 7 7
5 5 3 4
df.index=['First','Secned','Three','Four','Five','Six']
df
Out[221]:
a b c
First 2 2 5
Secned 1 9 8
Three 2 3 4
Four 7 4 3
Five 9 7 7
Six 5 3 4
df.set_index('c',drop=False)
Out[222]:
a b c
c
5 2 2 5
8 1 9 8
4 2 3 4
3 7 4 3
7 9 7 7
4 5 3 4
有set_index方法,就有reset_index方法,其實就是將set_index設定的廢棄,還原為預設的0到N-1的數字index
df.reset_index()
Out[224]:
index a b c
0 First 2 2 5
1 Secned 1 9 8
2 Three 2 3 4
3 Four 7 4 3
4 Five 9 7 7
5 Six 5 3 4
小結
本文大概介紹了Pandas 中有關資料選取和索引的知識點,理解該部分知識對理解Pandas的運用至關重要.