pandas模塊的數據操作


數據操作

數據操作最重要的一步也是第一步就是收集數據,而收集數據的方式有很多種,第一種就是我們已經將數據下載到了本地,在本地通過文件進行訪問,第二種就是需要到網站的API處獲取數據或者網頁上爬取數據,還有一種可能就是你的公司里面有自己的數據庫,直接訪問數據庫里面的數據進行分析。需要注意的是我們不僅需要將數據收集起來還要將不同格式的數據進行整理,最后再做相應的操作。

1、數據導入、存儲

訪問數據是數據分析的所必須的第一步,只有訪問到數據才可以對數據進行分析。

1.1、文本格式

常用pandas解析函數:

pandas提供了一些用於將表格型數據讀取為DataFrame對象的函數。以下

函數 描述
read_csv 從文件、url或者文件型對象讀取分割好的數據,逗號是默認分隔符
read_table 從文件、url或者文件型對象讀取分割好的數據,制表符('\t')是默認分隔符
read_fwf 讀取定寬格式數據(無分隔符)
read_clipboard 讀取剪貼板中的數據,可以看做read_table的剪貼板。再將網頁轉換為表格
read_excel 從Excel的XLS或者XLSX文件中讀取表格數據
read_hdf 讀取pandas寫的HDF5文件
read_html 從HTML文件中讀取所有表格數據
read_json 從json字符串中讀取數據
read_pickle 從Python pickle格式中存儲的任意對象
read_msgpack 二進制格式編碼的pandas數據
read_sas 讀取存儲於sas系統自定義存儲格式的SAS數據集
read_stata 讀取Stata文件格式的數據集
read_feather 讀取Feather二進制文件格式
read_sql 將SQL查詢的結果(SQLAlchemy)讀取為pandas的DataFrame

我們可以通過上表對這些解析函數有一個簡單了解,其中read_csv和read_table是以后用得最多的兩個方法,接下來我們主要就這兩個方法測試。

1.1.1、read_csv

csv文件就是一個以逗號分隔字段的純文本文件,用於測試的文件是本身是一個Excel文件,需要修改一下擴展名,但是簡單的修改后綴名不行,還需要將字符編碼改變為utf-8,因為默認的是ASCII,否則是會報錯的。然后就可以通過read_csv將它讀入到一個DataFrame:

import pandas as pd

df = pd.read_csv("E:/Test/test3.csv")
df
    name    age sex
0   佩奇  18  女
1   喬治  19  男

數據處理

注意:

到這里可能就會有些人有疑問了,為什么我的文件路徑不對啊,那是因為在我們這個方法當中的路徑當它往左斜的時候需要用雙斜杠,否則就要使用右斜杠

1.1.2、read_table

還可以使用read_table,並且指定分隔符

import pandas as pd
df = pd.read_csv("E:/Test/test3.csv")
df

name    age sex
0   佩奇  18  女
1   喬治  19  男

數據處理_tabl

如果不指定分隔符,它的數據之間會有逗號。

以上只是簡單的讀取操作,

1.1.3、指定列名

pandas可以幫助我們自動分配列名,也可以自己指定列名
默認列名

import pandas as pd
df = pd.read_csv("E:/Test/test.csv",header=None)
df
    0   1   2   3   4   5
0   a   b   c   d   e   f
1   g   h   i   j   k   l
2   m   n   o   p   q   r

recv_table2

指定列名

import pandas as pd
df = pd.read_csv("E:/Test/test.csv",names=['數','據','分','析','真','好','玩'])
df

recv_table3

具體還有那些參數,通過表格展示一下,這些參數是read_csv和read_table共有的

參數 描述
path 表明文件系統位置的字符串、URL或者文件型對象
sep或delimiter 用於分隔每行字段的字符序列或正則表達式
header 用作列名的行號,默認是0(第一行),如果沒有為None
names 結果的列名列表,和header=None一起用
skiprows 從文件開頭起,需要跳過的行數或者行號列表
na_values 用NA替換的值序列(可以用來處理缺失值)
data_parser 用於解析日期的函數
nrows 從文件開頭處讀取的行數
chunksize 用於設置迭代的塊大小
encoding 設置文本編碼

1.1.4、分塊讀取文件

剛才我們讀取文件是把整個文件都讀取出來了,那接下來我們就嘗試分塊讀取文本文件,因為不是所有的文件內容都只有這么少,我們在實際運用當中會需要讀取文件的一個小片段。

讀取大文件的時候可以添加一個參數使得顯示的內容更加緊湊

import pandas as pd
pd.options.display.max_rows = 3
df = pd.read_csv("E:/Test/test.csv")
df

分塊讀取

還可以通過上面表格中提到的 nrows 參數選擇只讀取其中的幾行

1.2、二進制

1.2.1、pickle

在Python中有一個自帶的序列化模塊pickle,它是進行二進制格式操作存儲數據最高效、最方便的方式之一。在pandas中有一個to_pickle方法可以將數據以pickle格式寫入硬盤

import pandas as pd
df = pd.read_csv("E:/Test/test_j.csv")
df.to_pickle("E:/Test/df_pickle")

運行完之后會發現沒反應,但是可以打開你存儲的文件夾會發現這個pickle文件已經存到里面了。

雖然說這種方式非常方便,但是卻很難保證格式的長期有效性。一個今天被pickle化的對象可能明天會因為庫的新版本而無法反序列化.在pandas當中還支持其他的二進制格式。接着往下看

1.2.2、HDF5

HDF5主要用於存儲大量的科學數組數據。以C庫的形式提供,並且有許多其他語言的接口,例如:JAVA、Julia,當然還有我們的Python。HDF5中的HDF代表分層數據格式,每個HDF5文件可以存儲多個數據集並且支持元數據

pandas.read_hdf函數是使用HDF5格式的一個快捷方法

import pandas as pd
import numpy as np
frame = pd.DataFrame({'a':np.random.randn(100)})

frame.to_hdf("E:/Test/mydata.h5",'obj3',format='table')
pd.read_hdf('E:/Test/mydata.h5','obj3',where=['index < 5'])

HDF5

1.3、Web API

現在很多網站都有公開的API,通過JSON或者其他什么格式提供數據服務。那接下來,我們就通過Python的requests模塊訪問Web API。

import requests
import pandas as pd
url = "https://api.github.com/repos/pandas-dev/pandas/issues"
resp = requests.get(url)
data = resp.json()
# 因為data中的每個元素都是一個字典,可以直接將data傳給DataFrame,並且將其中自己喜歡的字段展示出來
issues = pd.DataFrame(data,columns=['number','title','labels','state'])
issues

web API

1.4、操作數據庫

在數據分析的操作當中,讀寫數據庫主要還是由pandas來進行操作。在工作環境最為常用的就是mysql數據庫,所以就以操作mysql作為示例:

從數據庫導出數據:

import pandas as pd
from sqlalchemy import create_engine
import pymysql

# 創建數據庫連接
conn = create_engine("mysql+pymysql://root:123@localhost:3306/pd_test")

sql = "select * from emp;"
# 通過sql語句以及連接查詢數據庫,最終返回的結果是一個DataFrame數組
df = pd.read_sql(sql,engine)
df

運行結果:

read_sql

將數據導入數據庫:

在pandas當中存在一個to_sql函數,可以將數據寫入數據庫,它支持兩類mysql引擎,一個是sqlalchemy,另一個是sqlliet3,但是由於sqlliet3寒就沒有更新,所以建議使用sqlalchemy。

# key為列,鍵為值
data = pd.DataFrame({'id':[1,2,3],
                    'name':['賈玲','沈騰','馬麗'],
                    'password':['123','456','789']})
# 第一個參數:新建的表名;第二個:數據庫連接
data.to_sql('pd_test1',conn,index = True)

通過可視化工具打開數據庫可以看見,一個DataFrame數組就直接存到數據庫里面了

to_sql

2、數據處理

經過前面的了解,對於數據的導入以及存儲已經有了一個簡單的了解,但是在實際的工作情況當中,拿到的數據不可能都是預想的格式,所以說,就需要通過一些文本處理工具將這種特殊數據從一種形式轉換為另一種可以接受的形式,正好在Python的pandas庫當中就為我們提供了一個高級、靈活和快速的工具集,將數據轉換成想要的格式。

一個沒有處理的數據集就和一個沒有化妝的女網紅一樣,所以我們要一步一步給你的數據化上妝,讓它們看起來更加漂亮!!!

2.1、處理缺失值

缺失數據在數據分析的應用當中是很常見的,pandas的目標之一就是盡可能的無影響的處理缺失值。在pandas當中使用浮點值NaN(Not a Number來表是缺失值)。處理缺失值的最常見的兩種的方法:第一個就是直接將那個值丟棄,第二種方式就是補全缺失值。

屬性 描述
dropna 根據各標簽的值中是否存在缺失數據對軸標簽進行過濾。
fillna 用指定值或插值方法(如ffill或bfill)填充缺失數據
isnull 返回一個含有布爾值的對象,這些布爾值表示哪些值是缺失值/NA
notnull isnull的否定式

2.1.1、過濾缺失值

data = pd.Series(['pandas','numpy',np.nan,'matplotlib'])
-------------------------------------------
data.isnull()
運行結果:
0    False
1    False
2     True
3    False
dtype: bool    # 返回一個布爾值數組
-------------------------------------------
data[0] = None  # python中內置的None值在對象數組種也可以作為NA
data.notnull()
運行結果:
0    False
1     True
2    False
3     True
dtype: bool
-------------------------------------------
data_1 = pd.Series([1,np.nan,3,np.nan,5.5,9])
data_1.dropna()  # 丟棄任何含有缺失值的行
運行結果:
0    1.0
2    3.0
4    5.5
5    9.0
dtype: float64
# data_1[data_1.notnull()]等價於data_1.dropna()

以上都是對Series這種一維數組的缺失值處理,如果對於DataFrame這種二維數組的處理就會出現一些問題。

df = pd.DataFrame([[1,3,5,7,9],[2,4,6,np.nan],[1.5,5.,6.3,np.nan],[4.,2.3,np.nan,8,9.],[np.nan,np.nan,np.nan,np.nan,np.nan]])
df
運行結果:
    0   1   2   3   4
0   1.0 3.0 5.0 7.0 9.0
1   2.0 4.0 6.0 NaN NaN
2   1.5 5.0 6.3 NaN NaN
3   4.0 2.3 NaN 8.0 9.0
4   NaN NaN NaN NaN NaN
-------------------------------------------
cleaned = df.dropna()
cleaned
運行結果:
    0   1   2   3   4
0   1.0 3.0 5.0 7.0 9.0
# 所有帶有缺失值的行全被丟棄了

對於這種情況可以通過傳遞參數來解決:

df.dropna(how="all")  # 丟棄全為缺失值的行
運行結果:
    0   1   2   3   4
0   1.0 3.0 5.0 7.0 9.0
1   2.0 4.0 6.0 NaN NaN
2   1.5 5.0 6.3 NaN NaN
3   4.0 2.3 NaN 8.0 9.0
-------------------------------------------
df[4] = np.nan
df.dropna(axis=1,how="all") # 丟棄全為缺失值的列
運行結果:
    0   1   2   3
0   1.0 3.0 5.0 7.0
1   2.0 4.0 6.0 NaN
2   1.5 5.0 6.3 NaN
3   4.0 2.3 NaN 8.0
4   NaN NaN NaN NaN
-------------------------------------------

2.1.2、補全缺失值

有時候在操作數據的時候可能不想過濾掉缺失數據,因為有可能會丟棄和他有關的其他數據,而是希望通過一些其他方式來填補那些缺失的地方。fillna方法是處理這些問題最主要的方法。

df.fillna(0)  # 替換缺失值為0
運行結果:
    0   1   2   3   4
0   1.0 3.0 5.0 7.0 0.0
1   2.0 4.0 6.0 0.0 0.0
2   1.5 5.0 6.3 0.0 0.0
3   4.0 2.3 0.0 8.0 0.0
4   0.0 0.0 0.0 0.0 0.0
-------------------------------------------
df.fillna({1:0.5,2:0})  # 對不同的列填充不同的值
    0   1   2   3   4
0   1.0 3.0 5.0 7.0 NaN
1   2.0 4.0 6.0 NaN NaN
2   1.5 5.0 6.3 NaN NaN
3   4.0 2.3 0.0 8.0 NaN
4   NaN 0.5 0.0 NaN NaN
-------------------------------------------
df.fillna(0,inplace=True)
df
運行結果:
    0   1   2   3   4
0   1.0 3.0 5.0 7.0 0.0
1   2.0 4.0 6.0 0.0 0.0
2   1.5 5.0 6.3 0.0 0.0
3   4.0 2.3 0.0 8.0 0.0
4   0.0 0.0 0.0 0.0 0.0

使用fillna還可以進行插值

df1 = pd.DataFrame(np.random.randn(6,3))
df1
運行結果:
    0           1           2
0   0.475304    -1.274166   1.467016
1   -0.113910   0.935197    -1.008954
2   0.218006    0.209405    0.224217
3   1.878587    0.492774    -1.391237
4   -0.702284   0.863064    0.939069
5   -1.450531   0.994467    0.265843
# 一組數據數
-------------------------------------------
df1.iloc[2:,1] = np.nan  # 將第二列第三行以后的數據都轉換為缺失值
df1.iloc[4:,2] = np.nan  # 將第三列第五行以后的數據都轉換為缺失值
運行結果:
    0           1           2
0   1.073202    0.644249    -0.089127
1   -0.028500   0.479373    -0.271212
2   1.575710    NaN         -0.119795
3   -0.202480   NaN         0.385250
4   -1.090317   NaN         NaN
5   0.985767    NaN         NaN
-------------------------------------------
df1.fillna(method='ffill')
運行結果:
    0           1           2
0   1.073202    0.644249    -0.089127
1   -0.028500   0.479373    -0.271212
2   1.575710    0.479373    -0.119795
3   -0.202480   0.479373    0.385250
4   -1.090317   0.479373    0.385250
5   0.985767    0.479373    0.385250
# 參數limit還可以設置替換的行
參數 說明
value 用於填充缺失值的標量值或字典對象
method 插值方式。如果函數調用時未指定其他參數的話,默認為"ffill"
axis 待填充的軸,默認axis=0
inplace 修改調用者對象而不產生副本
limit 可以連續填充的最大數量

2.2、數據轉換

2.2.1、刪除重復值

data = pd.DataFrame({"k1":['one','two'] * 3 + ['two'],
                    "k2":[1,1,2,3,3,4,4]})   
data
運行結果:
    k1  k2
0   one 1
1   two 1
2   one 2
3   two 3
4   one 3
5   two 4
6   two 4
-----------------------------------------------
data.duplicated()  # 返回一個布爾型Series,表示各行是否是重復行
0    False
1    False
2    False
3    False
4    False
5    False
6     True
dtype: bool
-----------------------------------------------
data.drop_duplicates()  # 返回一個DataFrame,重復的數組會標為False
    k1  k2
0   one 1
1   two 1
2   one 2
3   two 3
4   one 3
5   two 4   
# 重復的行被刪除了
-----------------------------------------------

以上方法是針對全部列,還可以通過傳入參數對部分指定列進行重復項判斷。

data['v1'] = range(7)  # 添加一個v1列
data.drop_duplicates(['k1','k2'])  # 可以傳入一個列表,指定列,默認保留第一個出現的值組合
    k1  k2  v1
0   one 1   0
1   two 1   1
2   one 2   2
3   two 3   3
4   one 3   4
5   two 4   5
-----------------------------------------------
data.drop_duplicates(['k1','k2'],keep='last')  # 保留最后一個出現的值組合
    k1  k2  v1
0   one 1   0
1   two 1   1
2   one 2   2
3   two 3   3
4   one 3   4
6   two 4   6

2.2.2、使用函數或映射進行數據轉換

data = pd.DataFrame({"goods":['Iphone','HUAWEI','SAMSUNG','MI','OPPO'], "price":[6000,4000,5000,3000,3000]})
data
運行結果:
    goods   price
0   Iphone  6000
1   HUAWEI  4000
2   SAMSUNG 5000
3   MI      3000
4   OPPO    3000

現在有這樣一組數據,假設現在需要加上一列數據。將每樣商品的出產國家加上

# 先定義一個不同商品到產地的映射
produce_nation = {
    "iphone":'America',
    "huawei":'China',
    "samsung":'Korea',
    "mi":'China',
    "oppo":'China'
}
# 因為定義的商品全是大寫,需要全部轉換為小寫
lower_cased = data['goods'].str.lower()

# 通過map映射添加一列出產國家數據
data['nation'] =  lower_cased.map(produce_nation)
運行結果:
    goods   price   nation
0   Iphone  6000    America
1   HUAWEI  4000    China
2   SAMSUNG 5000    Korea
3   MI      3000    China
4   OPPO    3000    China

也可以通過一個函數完成這些工作:

data['goods'].map(lambda x:produce_nation[x.lower()])
運行結果:
0    America
1      China
2      Korea
3      China
4      China
Name: goods, dtype: object

2.2.3、替換值

利用fillna()方法填充缺失數據是值替換的一種特殊情況。接下來可以使用replace方法進行替換值操作。

data = pd.Series([1., -999., 2., -999., -1000., 3.])
data   # 生成一個Series數組
運行結果:
0       1.0
1    -999.0
2       2.0
3    -999.0
4   -1000.0
5       3.0
dtype: float64
----------------------------------------------
# 其中-999可能是缺失值的某一標識,需要使用NA替代這些值
1、
data.replace(-999, np.nan)  # 通過replace產生一個新的Series,除非傳入inplace=True
運行結果:
0       1.0
1       NaN
2       2.0
3       NaN
4   -1000.0
5       3.0
dtype: float64
2、
data.replace([-999, -1000], np.nan)  # 一次性替換多個值
3、
data.replace([-999, -1000], [np.nan, 0])  # 替換不同的值,通過列表
4、
data.replace({-999: np.nan, -1000: 0})  # 替換不同的值,通過字典

2.2.4、重命名軸索引

data =  pd.DataFrame(np.arange(12).reshape((3,4)),index=['a','b','c'],columns=['one','two','three','four'])
data
one two three four
a 0 1 2 3
b 4 5 6 7
c 8 9 10 11

想要給數據集重新轉換數據集的名稱,但是還不修改原有數據集,就可以使用一個非常nb的方法rename

data.rename(index=str.title,columns=str.upper)
ONE TWO THREE FOUR
A 0 1 2 3
B 4 5 6 7
C 8 9 10 11

以上只是對軸索引的名稱進行整列或是整行修改,有時候我們需要替換索引中的部分值,這就可以結合字典型對象來使用

data.rename(index={'a':"sean"},
           columns={'two':"tank"})
one tank three four
sean 0 1 2 3
b 4 5 6 7
c 8 9 10 11

補充:還有一個參數inplace=True,可以修改原有的數據集

2.2.5、離散化和分箱

我們在進行數據分析的時候,一些連續的數值經常需要進行離散化,或者需要分離成一個個"小塊"可以說"箱子"來進行分析。目前假設我們有某項研究的一組人員的數據,現在需要將他們進行分組,放入離散的年齡框當中

ages = [19,35,22,20,27,22,30,23,26,31,39,29,66,34,55,45,41,32,25]

現在需要將這些年齡分為:18-25,26-35,36-60以及61及以上的若干數組。

bins = [18,25,35,60,100]
cats = pd.cut(ages,bins)
cats
[(18, 25], (25, 35], (18, 25], (18, 25], (25, 35], ..., (35, 60], (35, 60], (35, 60], (25, 35], (18, 25]]
Length: 19
Categories (4, interval[int64]): [(18, 25] < (25, 35] < (35, 60] < (60, 100]]

以上是將ages列表中不同的數據,分別放入不同的箱子當中。pandas返回的對象也和我們平時所見的不太一樣,是一個特殊的Categorical對象。

cats.codes  # 指明了ages中標簽對應的數據所在的`箱子`
array([0, 1, 0, 0, 1, 0, 1, 0, 1, 1, 2, 1, 3, 1, 2, 2, 2, 1, 0],
      dtype=int8)
cats.categories  # 返回所有的箱子
IntervalIndex([(18, 25], (25, 35], (35, 60], (60, 100]]
              closed='right',
              dtype='interval[int64]')
pd.value_counts(cats)  # 對不同箱子中的數據進行計數
(25, 35]     8
(18, 25]     6
(35, 60]     4
(60, 100]    1
dtype: int64

表示箱子的符號與區間的數學符號是一致的,小括號表明是開放的,中括號表明他是封閉的,也就是顧頭不顧尾。可以自己改變參數right=False來改變哪一邊封閉

pd.cut(ages,[18,26,36,61,100],right=False)
[[18, 26), [26, 36), [18, 26), [18, 26), [26, 36), ..., [36, 61), [36, 61), [36, 61), [26, 36), [18, 26)]
Length: 19
Categories (4, interval[int64]): [[18, 26) < [26, 36) < [36, 61) < [61, 100)]

通過labels參數傳遞一個列表或者數組來傳入自定義的箱名

group_names = ['first',"second",'third','fourth']
pd.cut(ages,bins,labels=group_names)
[first, second, first, first, second, ..., third, third, third, second, first]
Length: 19
Categories (4, object): [first < second < third < fourth]

如果我們傳給cut一個整數個的箱子代替顯示的箱邊,pandas會自動根據數據當中的最大值和最小值計算出等長的箱子。

data = np.random.rand(20)
pd.cut(data,4,precision=2)  # precision 精確度,用於控制數位
[(0.75, 0.99], (0.27, 0.51], (0.75, 0.99], (0.75, 0.99], (0.27, 0.51], ..., (0.75, 0.99], (0.034, 0.27], (0.034, 0.27], (0.51, 0.75], (0.034, 0.27]]
Length: 20
Categories (4, interval[float64]): [(0.034, 0.27] < (0.27, 0.51] < (0.51, 0.75] < (0.75, 0.99]]

使用cut方法通常不會使我們每個箱子具有相同數據量的數值,但是qcut,它是基於樣本分為數進行分箱的,可以通過qcut得到等長的箱子。

data = np.random.randn(1000)  # 生產正態分布的數組
cats = pd.qcut(data,4)  # 切成四份
pd.value_counts(cats)
(0.714, 3.153]      250
(0.0908, 0.714]     250
(-0.592, 0.0908]    250
(-3.242, -0.592]    250
dtype: int64

2.2.6、檢測和過濾異常值

data = pd.DataFrame(np.random.randn(1000,4))
data.describe()
0 1 2 3
count 1000.000000 1000.000000 1000.000000 1000.000000
mean 0.051415 0.025348 -0.022929 -0.022217
std 0.974445 1.001188 0.988803 0.970864
min -2.954484 -3.145678 -2.807353 -3.001159
25% -0.601312 -0.606223 -0.684640 -0.666618
50% 0.053944 0.025130 -0.000365 -0.032060
75% 0.693812 0.688407 0.658130 0.626298
max 2.861130 3.532333 3.326404 3.127593

現在需要找到一列數據中絕對值大於三的值

col = data[2]
col[np.abs(col) > 3]
164    3.027784
831    3.326404
Name: 2, dtype: float64

要找出所有值中大於3或者小於-3的行,可以對布爾值DataFrame使用any方法

data[(np.abs(data) > 3).any(1)]
0 1 2 3
164 -1.156633 -1.104849 3.027784 -0.174189
624 0.054955 -3.145678 -0.778852 -2.280175
664 0.031400 0.274078 -0.924499 -3.001159
692 1.397606 3.532333 0.127610 0.816575
725 -1.336891 0.206885 0.677548 3.127593
831 -0.406038 0.283722 3.326404 -0.907127

限制值在-3到3之間

data[np.abs(data) > 3] = np.sign(data) * 3
data.describe()
0 1 2 3
count 1000.000000 1000.000000 1000.000000 1000.000000
mean 0.051415 0.024962 -0.023283 -0.022344
std 0.974445 0.999009 0.987664 0.970454
min -2.954484 -3.000000 -2.807353 -3.000000
25% -0.601312 -0.606223 -0.684640 -0.666618
50% 0.053944 0.025130 -0.000365 -0.032060
75% 0.693812 0.688407 0.658130 0.626298
max 2.861130 3.000000 3.000000 3.000000

np.sign(data)根據數據中的值的正負分別生成1和-1

np.sign(data).head()
0 1 2 3
0 1.0 -1.0 1.0 1.0
1 -1.0 1.0 1.0 1.0
2 1.0 -1.0 -1.0 1.0
3 1.0 1.0 1.0 -1.0
4 1.0 1.0 -1.0 -1.0


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM