1. 程式人生 > >Pandas快速教程(4)-資料選取和索引

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的運用至關重要.