Python 數據處理庫 pandas 入門教程
(點擊
上方公眾號
,可快速關注)
來源:強波的技術博客
http://qiangbo.space/2018-02-15/pandas_tutorial/
pandas是一個Python語言的軟體包,在我們使用Python語言進行機器學習編程的時候,這是一個非常常用的基礎編程庫。本文是對它的一個入門教程。
pandas提供了快速,靈活和富有表現力的數據結構,目的是使「關係」或「標記」數據的工作既簡單又直觀。它旨在成為在Python中進行實際數據分析的高級構建塊。
入門介紹
pandas適合於許多不同類型的數據,包括:
具有異構類型列的表格數據,例如SQL表格或Excel數據
有序和無序(不一定是固定頻率)時間序列數據。
具有行列標籤的任意矩陣數據(均勻類型或不同類型)
任何其他形式的觀測/統計數據集。
由於這是一個Python語言的軟體包,因此需要你的機器上首先需要具備Python語言的環境。關於這一點,請自行在網路上搜索獲取方法。
關於如何獲取pandas請參閱官網上的說明:
pandas Installation
。
通常情況下,我們可以通過
pip
來執行安裝:
sudo pip3 install
pandas
或者通過
conda
來安裝pandas:
conda install
pandas
目前(2018年2月)pandas的最新版本是v0.22.0(發布時間:2017年12月29日)。
我已經將本文的源碼和測試數據放到Github上: pandas_tutorial ,讀者可以前往獲取。
另外,pandas常常和NumPy一起使用,本文中的源碼中也會用到NumPy。
建議讀者先對NumPy有一定的熟悉再來學習pandas,我之前也寫過一個NumPy的基礎教程,參見這裡:Python 機器學習庫 NumPy 教程
核心數據結構
pandas最核心的就是
Series
和
DataFrame
兩個數據結構。
這兩種類型的數據結構對比如下:
名稱 | 維度 | 說明 |
---|---|---|
Series | 1維 | 帶有標籤的同構類型數組 |
DataFrame | 2維 | 表格結構,帶有標籤,大小可變,且可以包含異構的數據列 |
DataFrame可以看做是Series的容器,即:一個DataFrame中可以包含若干個Series。
註:在0.20.0版本之前,還有一個三維的數據結構,名稱為Panel。這也是pandas庫取名的原因:
pan
(el)-da
(ta)-s
。但這種數據結構由於很少被使用到,因此已經被廢棄了。Series
由於Series是一維結構的數據,我們可以直接通過數組來創建這種數據,像這樣:
# data_structure.py
import pandas
as
pd
import numpy
as
np
series1
=
pd
.
Series
([
1
,
2
,
3
,
4
])
(
"series1:
{}
"
.
format
(
series1
))
這段代碼輸出如下:
series1
:
0
1
1
2
2
3
3
4
dtype
:
int64
這段輸出說明如下:
輸出的最後一行是Series中數據的類型,這裡的數據都是
int64
類型的。
數據在第二列輸出,第一列是數據的索引,在pandas中稱之為
Index
。
我們可以分別列印出Series中的數據和索引:
# data_structure.py
(
"series1.values: {}
"
.
format
(
series1
.
values
))
(
"series1.index: {}
"
.
format
(
series1
.
index
))
這兩行代碼輸出如下:
series1
.
values
:
[
1
2
3
4
]
series1
.
index
:
RangeIndex
(
start
=
0
,
stop
=
4
,
step
=
1
)
如果不指定(像上面這樣),索引是[1, N-1]的形式。不過我們也可以在創建Series的時候指定索引。索引未必一定需要是整數,可以是任何類型的數據,例如字元串。例如我們以七個字母來映射七個音符。索引的目的是可以通過它來獲取對應的數據,例如下面這樣:
# data_structure.py
series2
=
pd
.
Series
([
1
,
2
,
3
,
4
,
5
,
6
,
7
],
index
=
[
"C"
,
"D"
,
"E"
,
"F"
,
"G"
,
"A"
,
"B"
])
(
"series2:
{}
"
.
format
(
series2
))
(
"E is {}
"
.
format
(
series2
[
"E"
]))
這段代碼輸出如下:
series2
:
C
1
D
2
E
3
F
4
G
5
A
6
B
7
dtype
:
int64
E
is
3
DataFrame
下面我們來看一下DataFrame的創建。我們可以通過NumPy的介面來創建一個4×4的矩陣,以此來創建一個DataFrame,像這樣:
# data_structure.py
df1
=
pd
.
DataFrame
(
np
.
arange
(
16
).
reshape
(
4
,
4
))
(
"df1:
{}
"
.
format
(
df1
))
這段代碼輸出如下:
df1
:
0
1
2
3
0
0
1
2
3
1
4
5
6
7
2
8
9
10
11
3
12
13
14
15
從這個輸出我們可以看到,默認的索引和列名都是[0, N-1]的形式。
我們可以在創建DataFrame的時候指定列名和索引,像這樣:
# data_structure.py
df2
=
pd
.
DataFrame
(
np
.
arange
(
16
).
reshape
(
4
,
4
),
columns
=
[
"column1"
,
"column2"
,
"column3"
,
"column4"
],
index
=
[
"a"
,
"b"
,
"c"
,
"d"
])
(
"df2:
{}
"
.
format
(
df2
))
這段代碼輸出如下:
df2
:
column1 column2 column3
column4
a
0
1
2
3
b
4
5
6
7
c
8
9
10
11
d
12
13
14
15
我們也可以直接指定列數據來創建DataFrame:
# data_structure.py
df3
=
pd
.
DataFrame
({
"note"
:
[
"C"
,
"D"
,
"E"
,
"F"
,
"G"
,
"A"
,
"B"
],
"weekday"
:
[
"Mon"
,
"Tue"
,
"Wed"
,
"Thu"
,
"Fri"
,
"Sat"
,
"Sun"
]})
(
"df3:
{}
"
.
format
(
df3
))
這段代碼輸出如下:
df3
:
note
weekday
0
C
Mon
1
D
Tue
2
E
Wed
3
F
Thu
4
G
Fri
5
A
Sat
6
B
Sun
請注意:
DataFrame的不同列可以是不同的數據類型
如果以Series數組來創建DataFrame,每個Series將成為一行,而不是一列
例如:
# data_structure.py
noteSeries
=
pd
.
Series
([
"C"
,
"D"
,
"E"
,
"F"
,
"G"
,
"A"
,
"B"
],
index
=
[
1
,
2
,
3
,
4
,
5
,
6
,
7
])
weekdaySeries
=
pd
.
Series
([
"Mon"
,
"Tue"
,
"Wed"
,
"Thu"
,
"Fri"
,
"Sat"
,
"Sun"
],
index
=
[
1
,
2
,
3
,
4
,
5
,
6
,
7
])
df4
=
pd
.
DataFrame
([
noteSeries
,
weekdaySeries
])
(
"df4:
{}
"
.
format
(
df4
))
df4的輸出如下:
df4
:
1
2
3
4
5
6
7
0
C
D
E
F
G
A
B
1
Mon Tue Wed Thu Fri Sat
Sun
我們可以通過下面的形式給DataFrame添加或者刪除列數據:
# data_structure.py
df3
[
"No."
]
=
pd
.
Series
([
1
,
2
,
3
,
4
,
5
,
6
,
7
])
(
"df3:
{}
"
.
format
(
df3
))
del
df3
[
"weekday"
]
(
"df3:
{}
"
.
format
(
df3
))
這段代碼輸出如下:
df3
:
note weekday
No
.
0
C
Mon
1
1
D
Tue
2
2
E
Wed
3
3
F
Thu
4
4
G
Fri
5
5
A
Sat
6
6
B
Sun
7
df3
:
note
No
.
0
C
1
1
D
2
2
E
3
3
F
4
4
G
5
5
A
6
6
B
7
Index對象與數據訪問
pandas的Index對象包含了描述軸的元數據信息。當創建Series或者DataFrame的時候,標籤的數組或者序列會被轉換成Index。可以通過下面的方式獲取到DataFrame的列和行的Index對象:
# data_structure.py
(
"df3.columns
{}
"
.
format
(
df3
.
columns
))
(
"df3.index
{}
"
.
format
(
df3
.
index
))
這兩行代碼輸出如下:
df3
.
columns
Index
([
"note"
,
"No."
],
dtype
=
"object"
)
df3
.
index
RangeIndex
(
start
=
0
,
stop
=
7
,
step
=
1
)
請注意:
Index並非集合,因此其中可以包含重複的數據
Index對象的值是不可以改變,因此可以通過它安全的訪問數據
DataFrame提供了下面兩個操作符來訪問其中的數據:
loc
:通過行和列的索引來訪問數據
iloc
:通過行和列的下標來訪問數據
例如這樣:
# data_structure.py
(
"Note C, D is:
{}
"
.
format
(
df3
.
loc
[[
0
,
1
],
"note"
]))
(
"Note C, D is:
{}
"
.
format
(
df3
.
iloc
[[
0
,
1
],
0
]))
第一行代碼訪問了行索引為0和1,列索引為「note」的元素。第二行代碼訪問了行下標為0和1(對於df3來說,行索引和行下標剛好是一樣的,所以這裡都是0和1,但它們卻是不同的含義),列下標為0的元素。
這兩行代碼輸出如下:
Note
C
,
D
is
:
0
C
1
D
Name
:
note
,
dtype
:
object
Note
C
,
D
is
:
0
C
1
D
Name
:
note
,
dtype
:
object
文件操作
pandas庫提供了一系列的
read_
函數來讀取各種格式的文件,它們如下所示:
read_csv
read_table
read_fwf
read_clipboard
read_excel
read_hdf
read_html
read_json
read_msgpack
read_pickle
read_sas
read_sql
read_stata
read_feather
讀取Excel文件
註:要讀取Excel文件,還需要安裝另外一個庫:
xlrd
通過pip可以這樣完成安裝:
sudo pip3 install
xlrd
安裝完之後可以通過pip查看這個庫的信息:
$
pip3 show xlrd
Name
:
xlrd
Version
:
1.1.0
Summary
:
Library
for
developers
to
extract data from Microsoft Excel
(
tm
)
spreadsheet files
Home
-
page
:
http
:
//www.python-excel.org/
Author
:
John Machin
Author
-
:
sjmachin
@
lexicon
.
net
License
:
BSD
Location
: /
Library
/
Frameworks
/
Python
.
framework
/
Versions
/
3.6
/
lib
/
python3
.
6
/
site
-
packages
Requires
:
接下來我們看一個讀取Excel的簡單的例子
:
# file_operation.py
import pandas
as
pd
import numpy
as
np
df1
=
pd
.
read_excel
(
"data/test.xlsx"
)
(
"df1:
{}
"
.
format
(
df1
))
這個Excel的內容如下:
df1
:
C
Mon
0
D
Tue
1
E
Wed
2
F
Thu
3
G
Fri
4
A
Sat
5
B
Sun
註:本文的代碼和數據文件可以通過文章開頭提到的Github倉庫獲取。
讀取CSV文件
下面,我們再來看讀取CSV文件的例子。
第一個CSV文件內容如下
$
cat
test1
.
csv
C
,
Mon
D
,
Tue
E
,
Wed
F
,
Thu
G
,
Fri
A
,
Sat
讀取的方式也很簡單:
# file_operation.py
df2
=
pd
.
read_csv
(
"data/test1.csv"
)
(
"df2:
{}
"
.
format
(
df2
))
我們再來看第2個例子,這個文件的內容如下:
$
cat
test2
.
csv
C
|
Mon
D
|
Tue
E
|
Wed
F
|
Thu
G
|
Fri
A
|
Sat
嚴格的來說,這並不是一個CSV文件了,因為它的數據並不是通過逗號分隔的。在這種情況下,我們可以通過指定分隔符的方式來讀取這個文件,像這樣:
# file_operation.py
df3
=
pd
.
read_csv
(
"data/test2.csv"
,
sep
=
"|"
)
(
"df3:
{}
"
.
format
(
df3
))
實際上,read_csv
支持非常多的參數用來調整讀取的參數,如下表所示:
參數 | 說明 |
---|---|
path | 文件路徑 |
sep或者delimiter | 欄位分隔符 |
header | 列名的行數,默認是0(第一行) |
index_col | 列號或名稱用作結果中的行索引 |
names | 結果的列名稱列表 |
skiprows | 從起始位置跳過的行數 |
na_values | 代替NA 的值序列 |
comment | 以行結尾分隔注釋的字元 |
parse_dates | 嘗試將數據解析為datetime 。默認為False |
keep_date_col | 如果將列連接到解析日期,保留連接的列。默認為False 。 |
converters | 列的轉換器 |
dayfirst | 當解析可以造成歧義的日期時,以內部形式存儲。默認為False |
data_parser | 用來解析日期的函數 |
nrows | 從文件開始讀取的行數 |
iterator | 返回一個TextParser對象,用於讀取部分內容 |
chunksize | 指定讀取塊的大小 |
skip_footer | 文件末尾需要忽略的行數 |
verbose | 輸出各種解析輸出的信息 |
encoding | 文件編碼 |
squeeze | 如果解析的數據只包含一列,則返回一個Series |
thousands | 千數量的分隔符 |
詳細的read_csv函數說明請參見這裡:pandas.read_csv
處理無效值
現實世界並非完美,我們讀取到的數據常常會帶有一些無效值。如果沒有處理好這些無效值,將對程序造成很大的干擾。
對待無效值,主要有兩種處理方法:直接忽略這些無效值;或者將無效值替換成有效值。
下面我先創建一個包含無效值的數據結構。然後通過pandas.isna
函數來確認哪些值是無效的:
# process_na.py
import pandas
as
pd
import numpy
as
np
df
=
pd
.
DataFrame
([[
1.0
,
np
.
nan
,
3.0
,
4.0
],
[
5.0
,
np
.
nan
,
np
.
nan
,
8.0
],
[
9.0
,
np
.
nan
,
np
.
nan
,
12.0
],
[
13.0
,
np
.
nan
,
15.0
,
16.0
]])
(
"df:
{}
"
.
format
(
df
));
(
"df:
{}
"
.
format
(
pd
.
isna
(
df
)));
****
這段代碼輸出如下:
df
:
0
1
2
3
0
1.0
NaN
3.0
4.0
1
5.0
NaN
NaN
8.0
2
9.0
NaN
NaN
12.0
3
13.0
NaN
15.0
16.0
df
:
0
1
2
3
0
False
True
False
False
1
False
True
True
False
2
False
True
True
False
3
False
True
False
False
忽略無效值
我們可以通過pandas.DataFrame.dropna
函數拋棄無效值:
# process_na.py
(
"df.dropna():
{}
"
.
format
(
df
.
dropna
()));
註:dropna
默認不會改變原先的數據結構,而是返回了一個新的數據結構。如果想要直接更改數據本身,可以在調用這個函數的時候傳遞參數 inplace = True
。
對於原先的結構,當無效值全部被拋棄之後,將不再是一個有效的DataFrame,因此這行代碼輸出如下:
df
.
dropna
()
:
Empty DataFrame
Columns
:
[
0
,
1
,
2
,
3
]
Index
:
[]
我們也可以選擇拋棄整列都是無效值的那一列:
# process_na.py
(
"df.dropna(axis=1, how="all"):
{}
"
.
format
(
df
.
dropna
(
axis
=
1
,
how
=
"all"
)));
註:axis=1
表示列的軸。how可以取值』any』或者』all』,默認是前者。
這行代碼輸出如下:
df
.
dropna
(
axis
=
1
,
how
=
"all"
)
:
0
2
3
0
1.0
3.0
4.0
1
5.0
NaN
8.0
2
9.0
NaN
12.0
3
13.0
15.0
16.0
替換無效值
我們也可以通過fillna
函數將無效值替換成為有效值。像這樣:
# process_na.py
(
"df.fillna(1):
{}
"
.
format
(
df
.
fillna
(
1
)));
這段代碼輸出如下:
df
.
fillna
(
1
)
:
0
1
2
3
0
1.0
1.0
3.0
4.0
1
5.0
1.0
1.0
8.0
2
9.0
1.0
1.0
12.0
3
13.0
1.0
15.0
16.0
將無效值全部替換成同樣的數據可能意義不大,因此我們可以指定不同的數據來進行填充。為了便於操作,在填充之前,我們可以先通過
rename
方法修改行和列的名稱:
# process_na.py
df
.
rename
(
index
=
{
0
:
"index1"
,
1
:
"index2"
,
2
:
"index3"
,
3
:
"index4"
},
columns
=
{
0
:
"col1"
,
1
:
"col2"
,
2
:
"col3"
,
3
:
"col4"
},
inplace
=
True
);
df
.
fillna
(
value
=
{
"col2"
:
2
},
inplace
=
True
)
df
.
fillna
(
value
=
{
"col3"
:
7
},
inplace
=
True
)
(
"df:
{}
"
.
format
(
df
));
這段代碼輸出如下:
df
:
col1 col2 col3 col4
index1
1.0
2.0
3.0
4.0
index2
5.0
2.0
7.0
8.0
index3
9.0
2.0
7.0
12.0
index4
13.0
2.0
15.0
16.0
處理字元串
數據中常常牽涉到字元串的處理,接下來我們就看看pandas對於字元串操作。
Series
的
str
欄位包含了一系列的函數用來處理字元串。並且,這些函數會自動處理無效值。
下面是一些實例,在第一組數據中,我們故意設置了一些包含空格字元串:
# process_string.py
import pandas
as
pd
s1
=
pd
.
Series
([
" 1"
,
"2 "
,
" 3 "
,
"4"
,
"5"
]);
(
"s1.str.rstrip():
{}
"
.
format
(
s1
.
str
.
lstrip
()))
(
"s1.str.strip():
{}
"
.
format
(
s1
.
str
.
strip
()))
(
"s1.str.isdigit():
{}
"
.
format
(
s1
.
str
.
isdigit
()))
在這個實例中我們看到了對於字元串
strip
的處理以及判斷字元串本身是否是數字,這段代碼輸出如下:
s1
.
str
.
rstrip
()
:
0
1
1
2
2
3
3
4
4
5
dtype
:
object
s1
.
str
.
strip
()
:
0
1
1
2
2
3
3
4
4
5
dtype
:
object
s1
.
str
.
isdigit
()
:
0
False
1
False
2
False
3
True
4
True
dtype
:
bool
下面是另外一些示例,展示了對於字元串大寫,小寫以及字元串長度的處理:
# process_string.py
s2
=
pd
.
Series
([
"Stairway to Heaven"
,
"Eruption"
,
"Freebird"
,
"Comfortably Numb"
,
"All Along the Watchtower"
])
(
"s2.str.lower():
{}
"
.
format
(
s2
.
str
.
lower
()))
(
"s2.str.upper():
{}
"
.
format
(
s2
.
str
.
upper
()))
(
"s2.str.len():
{}
"
.
format
(
s2
.
str
.
len
()))
該段代碼輸出如下:
s2
.
str
.
lower
()
:
0
stairway
to
heaven
1
eruption
2
freebird
3
comfortably
numb
4
all along the watchtower
dtype
:
object
s2
.
str
.
upper
()
:
0
STAIRWAY
TO
HEAVEN
1
ERUPTION
2
FREEBIRD
3
COMFORTABLY
NUMB
4
ALL ALONG THE WATCHTOWER
dtype
:
object
s2
.
str
.
len
()
:
0
18
1
8
2
8
3
16
4
24
dtype
:
int64
結束語
本文是pandas的入門教程,因此我們只介紹了最基本的操作。對於
MultiIndex/Advanced Indexing
Merge, join, concatenate
Computational tools
之類的高級功能,以後有機會我們再來一起學習。
讀者也可以根據下面的鏈接獲取更多的知識。
參考資料與推薦讀物
pandas官方網站
Python for Data Analysis
Pandas Tutorial: Data analysis with Python: Part 1
看完本文有收穫?請轉
發分享給更多人
關注「P
ython開發者」,提升Python技能
※不踩坑的Python爬蟲:如何在一個月內學會爬取大規模數據
※不懂 Python,你在 AI 時代將成為新「文盲」
TAG:Python開發者 |