天天看點

像學R一樣學Python資料分析之基本資料管理

兩種基本資料結構

pandas具有兩種主要的資料結構,一種叫做 Series, 直譯就是序列, 另一種叫做 DataFrame, 直譯就是資料框。

這兩者與Python内置的資料結構,以及Numpy的ndarray資料結構最大的不同就在于,它們是由資料和資料标簽組成。說人話就是,它們讓Python成為了一個Excel。其中 DataFrane 簡單了解就是多列的 Series 。

一個示例

這裡用R語言實戰第二版的一個案例

本人目前工作的研究主題之一是男性和女性在上司各自企業方式上的不同。典型的問題如下。
  • 處于管理崗位的男性和女性在聽從上級的程度上是否有所不同?
  • 這種情況是否依國家的不同而有所不同,或者說這些由性别導緻的不同是否普遍存在?
像學R一樣學Python資料分析之基本資料管理

案例資料

要解決的問題如下:

  • 五個評分(q1到q5)需要組合起來,即為每位經理人生成一個平均服從程度得分。
  • 在問卷調查中,被調查者經常會跳過某些問題。例如,為4号經理人打分的上司跳過了問題4和問題5。我們需要一種處理不完整資料的方法,同時也需要将99歲這樣的年齡值重編碼為缺失值。
  • 一個資料集中也許會有數百個變量,但我們可能僅對其中的一些感興趣。為了簡化問題,我們往往希望建立一個隻包含那些感興趣變量的資料集。
  • 既往研究表明,上司行為可能随經理人的年齡而改變,二者存在函數關系。要檢驗這種觀點,我們希望将目前的年齡值重編碼為類别型的年齡組(例如年輕、中年、年長)。
  • 上司行為可能随時間推移而發生改變。我們可能想重點研究最近全球金融危機期間的服從行為。為了做到這一點,我們希望将研究範圍限定在某一個特定時間段收集的資料上(比如,2009年1月1日到2009年12月31日)。

建立資料集

根據表格,手動建立Series,DataFrame,

from pandas import Series, DataFrame
from numpy import nan as NA
import pandas as pd
import numpy as np
# 建立Series
manager = Series([1,2,3,4,5])
country = Series(['US','US','UK','UK','UK'])
gender = Series(['M','F','F','M','F'])
age = Series([32,45,25,39,99])
q1 = Series([5,3,3,3,2])
q2 = Series([4,5,5,3,2])
q3 = Series([5,2,5,4,1])
q4 = Series([5,5,5,NA,2])
q5 = Series([5,5,2,NA,1])
# 由Series組成DataFrame
leadership = DataFrame({'manager':manager,'country':country,'gender':gender,'age':age,
'q1':q1,'q2':q2,'q3':q3,'q4':q4,'q5':q5})
           

如果資料沒有寫完,增加額外列,

date = Series(['10/24/08','10/28/08','10/1/08','10/12/08','5/1/09'])
# 為不存在的列指派能夠建立新的一列。
leadership['date'] = date
# 檢視資料庫的值
leadership.values
# 檢視前後幾行
leadership.head(2)
leadership.tail(2)
           

手動建立資料的情況比較少,我們用pandas自帶的讀取函數導入一個以制表符分隔的格式化的文本檔案,然後看下資料結構。

原始資料有20列29850行,為10個對照組10個控制組在29850個基因上的表達量。

像學R一樣學Python資料分析之基本資料管理
  • 讀取資料,
In [1]: import pandas as pd
In [2]: data = pd.read_table("C:/Users/Xu/Desktop/Data.txt")
In [3]: type(data)
Out[3]: pandas.core.frame.DataFrame
           
  • 簡單檢視資料
# 資料框大小
In [4]: data.shape
Out[4]: (29849, 21)
# 前兩行
In [5]: data.head(2)
Out[5]: 
  Unnamed: 0  control1  control2  control3  control4  control5  control6  \
0       A1BG  6.917468  6.308350  5.318841  5.886811  5.082975  5.629453
1   A1BG-AS1  7.862730  7.065809  6.783732  6.275773  3.063104  5.131017
           

在繼續介紹資料管理前,先簡單介紹一下panda的index對象。pandas使用索引對象管理軸标簽(行列名),它不可被輕易修改。因為Index對象的存在,不同來源的資料能夠進行對齊,還能根據索引重新排序。

  • 選取資料框元素, 就是能夠提取某幾行,某幾列,或者某一個元素。
# 按行選取元素, 提供單個索引,或者是list
leadership.ix[1]
leadership.ix[[1,2,3]] # 或leadership.ix[1:3]
# 按列選取元素
leadership[[1,2,3]]
leadership[[1]]
leadership['q1']
leadership.ix[:,1:3] 
## 特别的,還可以根據列名,選取一個範圍
leadership.ix[:,'q1':'q5']

# 按行,按列,選取局部元素, [行,列]
leadership.ix[1,2]
leadership.ix[1:2,2:3]
leadership.ix[1:3,2:4]
           

注: Python以0為基, 是以leadership.ix[[1]]選取的是第二行,并且leadership.ix[1:3]是優先根據索引名,而leadership[1:3]則是根據位置順序。 目前來看直接用

[]

有很多小問題,是以建議都改用

ix[]

基本資料管理

下面使用的資料來自于前面導入的

data

, 模仿《R語言實戰》的基本資料管理章節編排。

建立新變量

比如說建立一個總分,是q1-q5的總計

# 用到numpy的通用數學函數, 其中axis=0表示每一列的計算結果,axis=1表示所有行的運算結果
In [29]: total = np.sum(leadership.ix[:,'q1':'q5'], axis=1)
In [30]: total
Out[30]: 
0    24.0
1    20.0
2    20.0
3    10.0
4     8.0
dtype: float64
## np.sum計算的時候會無視掉缺失值NA
# 可以直接增加到資料框内
leadership['total'] = np.sum(leadership.ix[:,'q1':'q5'], axis=1)
           

變量重編碼

重編碼涉及到同一變量和/或其他變量的現有值建立新值的過程。比如說,将一個連續性變量修改為一組類别值;将誤編碼的值替換成正确值;基于一組分數線建立一個表示及格/不及格的變量。

比如說在leadership裡面的年齡中有一個是99歲,按照嘗試來看就是錯的,是以需要把他重編碼為NA。

# 方式一
## .ix 比 .loc使用更加廣泛,對于初學者來說沒有差異
leadership['age'][leadership['age'] > 99] = NA
leadership.ix[:,'age'][leadership.ix[:,'age'] >= 99] = NA
leadership.loc[:,('age')][leadership['age'] == 99] = NA
# 方式二
leadership.ix[leadership['age'] > 99,'age'] = NA
leadership.loc[leadership['age'] == 99, 'age']
           

看起來第一種方法用了很多,但是都屬于

chained indexing

, 直譯就是連鎖索引,也就是連續用了

[]

。這個其實我沿用了R語言的習慣,

leadership$age[leadership$age == 99]

,pandas在處理

chained indexing

如果發現存在指派現象,就會報錯或者警告。

問題來自于底層Python代碼處理

chained indexing

時是傳回視圖(views)還是複制(copy),畢竟還會導緻性能上的降低。是以建議第二種。

注: 視圖和複制是兩個不同的概念,如果你将視圖指派給新變量,對新變量的操作會影響到原始資料,而如果将原始資料的一個複制指派給新變量,那麼對新變量的操作就與原始資料無關。

下一步,我們還可以把大于75定義為older, 55和75間定義為midlle aged, 小于55則是young。

leadership['agecat'] = np.where(leadership['age'] > 75, 'Elder', np.where(np.logical_and(leadership['age']<=75, leadership['age'] >= 45), "Middle Aged", "Young" ))
           

這裡用到Numpy的人二進制ufunc中的元素級比較運算, np.where,np.logical_and。如果用np.greater和np.less,會遇到報錯,這是因為存在缺失值。

變量重命名

如果對現有的名字不滿意,可以對行名(index),列名(columns)進行修改

# 先檢視列名
leadership.columns
# 函數采用rename, 參數為columns, index,可以用字典指定置換映射
leadership = leadership.rename(columns={'q1': 'item1','q2':'item2','q3':'item3','q4':'item4','q5':'item5'})

           

缺失值處理

真實世界的資料有可能存在殘缺,在pandas中庸NaN表示缺失或NA值,用isnull和notnull函數進行檢測

# 檢視是否有缺失
pd.isnull(leadership)
pd.notnull(leadership)
## 或
leadership.isnull()
leadership.notnull()
           

缺失值的處理方法簡單分為兩種,一種是過濾,

dropna

或者是填充,

fillna

如果缺失部分較少,剔除後對結果沒有影響,采用第一種,否則用第二種。

# 預設是axis=0, how='any',也就是提出有一個是NA的行
newdata = leadership.dropna()
# 按列剔除
leadership.dropna(axis=1)
# 僅剔除都是NA所在行
leadership.dropna(axis=1, how='all')
# inplace表示是否原始資料上操作
leadership.dropna(axis=1, how='all', inplace=True)
           

關于插值,比較複雜,以後講解

日期值

Python标準庫自帶日期(date)和時間(time)資料的資料類型,主要用的是dateime, time, calenda子產品,在其中

datetime.datetime

是用得最多資料類型。pandas用

to_datetime

解析不同日期的表達方式。

# pd.to_datetime
leadership['date'] = pd.to_datetime(leadership['date'])
           

更多和日期值相關的内容留待時間序列部分介紹。

類型轉換

變量可以用

isstance

進行判斷, 判斷所屬對象

isinstance(leadership, DataFrame)
# True
           

資料結構之間的轉變,則直接用對應的建構函數即可

isinstance(leadership['item5'], Series)
# True
np.array(leadership['item5'])
# array([5, 5, 2, 1], dtype=int64)
           

檢視資料類型則可以用

.dytpe

leadership['date'].dtype
# dtype('<M8[ns]')
           

而資料類型轉換可以用

.astype

完成

leadership['item5'].dtype
# dtype('float64')
leadership['item5'] = leadership['item5'].astype(np.int64)
           

資料排序

pandas排序可以根據索引(by index),也可以根據數值(by values)

如果根據索引,分為行名(axis=0)或者是列名(axis=1)

## 首先用reindex打亂順序
unsorted = leadership.reindex(index=[4,2,1,3,0])
## 用sort_index()排序,參數有ascending,inplace, axis
resorted = leadership.sort_index()
           

如果根據數值, 可以提供多個列,然後指定每列的升降序

leadership.sort_values(by=['item1','item2'], ascending=[False,True])
           

資料集的合并

資料集的合并分為添加列或是添加行。pandas具備按軸(行或列)自動或顯式資料對其功能。并且底層是C編寫,是以處理合并速度極快。以

官方文檔

為例, 主要回到兩個函數

pd.concat

pd.append

pd.merge

  • pd.merge 可以根據一個或多個鍵将不同的DataFrame中的行連結起來,類似于R的merge,但是速度更快
  • pd.concat 可以根據一條軸将多個對象堆疊在一起。

對于

pd.merge

而言,如果用過R語言的merge或者是SQL等關系型資料庫的連接配接操作,基本上能很快了解。

df1 = DataFrame({'key':['b','b','a','c','a','a','b'], 'data1':range(7)})
df2 = DataFrame({'key':['a','b','d'], 'data2':range(3)})
# 沒有顯示指明鍵
pd.merge(df1,df2)
# 使用on,進行指明,如果左右不同,則需要用left_on, right_on指定
pd.merge(df1, df2, on='key')
           

對于資料庫類型,有4種連接配接方式要注意,inner, outer, left, right。看下圖進行了。

像學R一樣學Python資料分析之基本資料管理

merge

而另一類

pd.concat

則是根據軸的标簽進行合并。

s1 = Series([0,1], index=['a','b'])
s2 = Series([2,3,4], index=['c','d','e'])
s3 = Series([5,6], index=['f','g'])
pd.concat([s1,s2,s3], axis=1)
           

合并操作可以用來完成生信程式設計直播第四題:多個同樣的行列式檔案合并起來

第一步,下載下傳操作資料,并解壓

wget ftp://ftp.ncbi.nlm.nih.gov/geo/series/GSE48nnn/GSE48213/suppl/GSE48213_RAW.tar
tar -x GSE48213_RAW.tar
gzip -d *.gz
mkidr GSE48213_RAW
mv *.txtGSE48213_RAW
           

第二步,合并資料

使用

pd.merge

的代碼搬運自生信技能樹使用者end2end

import pandas
import os
name_list=os.listdir("GSE48213_RAW")
fram_list=[pd.read_table("GSE48213_RAW/%s"%name) for name in name_list]
fram=fram_list[0]
for i in range(1,len(fram_list)):
    fram=pandas.merge(fram,fram_list[i])
fram.to_csv("result.csv",index=False)
           

如果在讀取表格的時候設定基因名為軸索引(行名),那麼就可以用

pd.concat

import pandas as pd
import os
name_list=os.listdir("GSE48213_RAW")
each = [pd.read_table("GSE48213_RAW/%s"%name,header=0,index_col=0) for name in name_list]
total  = pd.concat(each, axis=1)
total.to_csv("result.csv",index=True)
           

資料集取子集

這部分内容在前面有所提及,這裡在基礎上繼續介紹

  • 選入(保留)變量,可以認為是選擇列

    選入變量,可以通過

    DataFrame.ix[行索引, 列索引]

    這樣記号來通路
new_data =  leadership.ix[:,6:10]
           

其中

:,

表示選取所有行。

如果是直接選取某一個列,pandas用

.

對應R的

$

leadership.date
0   2008-10-24
1   2008-10-28
2   2008-10-01
4   2009-05-01
Name: date, dtype: datetime64[ns]
           
  • 剔除(丢棄)變量

    比如說我們想剔除q3和q4兩列

myvars = leadership.columns.isin(['item4','item5'])
new_data = leadership.ix[:,np.logical_not(myvars)]
           
  • 選入觀測

    選入或剔除觀測(行)通常是成功的資料準備和資料分析的一個關鍵方面。比如說讓我們選取30歲以上的男性

leadership.ix[np.logical_and(leadership.gender == 'M', leadership.age > 30),:]
           

你會感覺這樣寫代碼太長了,我們需要一個類似R的subset的函數,在pandas對應的是query.

leadership.query('age <35 and gender == "M"')
           
  • 随機抽樣

    pandas提供了sample方法用于對樣本進行抽樣。

leadership.sample(n=None, frac=None, replace=False, weights=None, random_state=None, axis=None)
# replace表示是否放回。
# 可以按數量
leadership.sample(n=2, replace=True)
# 可以按照比例
leadership.sample(frac=0.5, replace=True)
           

小結

本文講解了大量的基礎知識。我們以R語言實戰的一個資料為例,講解了如何在Python如何建立一個DataFrame對象(手動或導入),然後根據已有變量建立新變量,對變量重編碼,重命名變量。之後是缺失資料的處理,對于pandas,這部分介紹用dropna按行或按列丢棄,然後是日期值部分。 關于資料轉換,分為資料結構和資料類型兩部分。之後介紹了資料合并,并且以表達量矩陣合并為例實際操作,最後是資料取子集和抽樣。

當我們學會基本的資料處理之後,我們接着就可以根據不計其數函數進行更進階的操作。

當你看完後續的部分,你就能掌握複雜資料集的多數工具。無論你走到哪裡,都将成為資料分析師豔羨的人物!