像學R一樣學Python數(shù)據(jù)分析之基本數(shù)據(jù)管理

兩種基本數(shù)據(jù)結構

pandas具有兩種主要的數(shù)據(jù)結構,一種叫做 Series, 直譯就是序列, 另一種叫做 DataFrame, 直譯就是數(shù)據(jù)框。

這兩者與Python內置的數(shù)據(jù)結構,以及Numpy的ndarray數(shù)據(jù)結構最大的不同就在于,它們是由數(shù)據(jù)和數(shù)據(jù)標簽組成。說人話就是,它們讓Python成為了一個Excel。其中 DataFrane 簡單理解就是多列的 Series 。

一個示例

這里用R語言實戰(zhàn)第二版的一個案例

本人當前工作的研究主題之一是男性和女性在領導各自企業(yè)方式上的不同。典型的問題如下。

  • 處于管理崗位的男性和女性在聽從上級的程度上是否有所不同?
  • 這種情況是否依國家的不同而有所不同,或者說這些由性別導致的不同是否普遍存在?
案例數(shù)據(jù)

要解決的問題如下:

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

創(chuàng)建數(shù)據(jù)集

根據(jù)表格,手動創(chuàng)建Series,DataFrame,

from pandas import Series, DataFrame
from numpy import nan as NA
import pandas as pd
import numpy as np
# 創(chuàng)建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})

如果數(shù)據(jù)沒有寫完,增加額外列,

date = Series(['10/24/08','10/28/08','10/1/08','10/12/08','5/1/09'])
# 為不存在的列賦值能夠創(chuàng)建新的一列。
leadership['date'] = date
# 查看數(shù)據(jù)庫的值
leadership.values
# 查看前后幾行
leadership.head(2)
leadership.tail(2)

手動創(chuàng)建數(shù)據(jù)的情況比較少,我們用pandas自帶的讀取函數(shù)導入一個以制表符分隔的格式化的文本文件,然后看下數(shù)據(jù)結構。
原始數(shù)據(jù)有20列29850行,為10個對照組10個控制組在29850個基因上的表達量。

  • 讀取數(shù)據(jù),
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
  • 簡單查看數(shù)據(jù)
# 數(shù)據(jù)框大小
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

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

  • 選取數(shù)據(jù)框元素, 就是能夠提取某幾行,某幾列,或者某一個元素。
# 按行選取元素, 提供單個索引,或者是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] 
## 特別的,還可以根據(jù)列名,選取一個范圍
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]是優(yōu)先根據(jù)索引名,而leadership[1:3]則是根據(jù)位置順序。 目前來看直接用[]有很多小問題,所以建議都改用ix[]

基本數(shù)據(jù)管理

下面使用的數(shù)據(jù)來自于前面導入的data, 模仿《R語言實戰(zhàn)》的基本數(shù)據(jù)管理章節(jié)編排。

創(chuàng)建新變量

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

# 用到numpy的通用數(shù)學函數(shù), 其中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
# 可以直接增加到數(shù)據(jù)框內
leadership['total'] = np.sum(leadership.ix[:,'q1':'q5'], axis=1)

變量重編碼

重編碼涉及到同一變量和/或其他變量的現(xiàn)有值創(chuàng)建新值的過程。比如說,將一個連續(xù)性變量修改為一組類別值;將誤編碼的值替換成正確值;基于一組分數(shù)線創(chuàng)建一個表示及格/不及格的變量。

比如說在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, 直譯就是連鎖索引,也就是連續(xù)用了[]。這個其實我沿用了R語言的習慣,leadership$age[leadership$age == 99],pandas在處理chained indexing如果發(fā)現(xiàn)存在賦值現(xiàn)象,就會報錯或者警告。

問題來自于底層Python代碼處理chained indexing時是返回視圖(views)還是復制(copy),畢竟還會導致性能上的降低。所以建議第二種。

注: 視圖和復制是兩個不同的概念,如果你將視圖賦值給新變量,對新變量的操作會影響到原始數(shù)據(jù),而如果將原始數(shù)據(jù)的一個復制賦值給新變量,那么對新變量的操作就與原始數(shù)據(jù)無關。

下一步,我們還可以把大于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,會遇到報錯,這是因為存在缺失值。

變量重命名

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

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

缺失值處理

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

# 查看是否有缺失
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表示是否原始數(shù)據(jù)上操作
leadership.dropna(axis=1, how='all', inplace=True)

關于插值,比較復雜,以后講解

日期值

Python標準庫自帶日期(date)和時間(time)數(shù)據(jù)的數(shù)據(jù)類型,主要用的是dateime, time, calenda模塊,在其中datetime.datetime是用得最多數(shù)據(jù)類型。pandas用to_datetime解析不同日期的表達方式。

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

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

類型轉換

變量可以用isstance進行判斷, 判斷所屬對象

isinstance(leadership, DataFrame)
# True

數(shù)據(jù)結構之間的轉變,則直接用對應的構建函數(shù)即可

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

查看數(shù)據(jù)類型則可以用.dytpe

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

而數(shù)據(jù)類型轉換可以用.astype完成

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

數(shù)據(jù)排序

pandas排序可以根據(jù)索引(by index),也可以根據(jù)數(shù)值(by values)
如果根據(jù)索引,分為行名(axis=0)或者是列名(axis=1)

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

如果根據(jù)數(shù)值, 可以提供多個列,然后指定每列的升降序

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

數(shù)據(jù)集的合并

數(shù)據(jù)集的合并分為添加列或是添加行。pandas具備按軸(行或列)自動或顯式數(shù)據(jù)對其功能。并且底層是C編寫,所以處理合并速度極快。以官方文檔為例, 主要回到兩個函數(shù)pd.concat,pd.appendpd.merge

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

對于pd.merge而言,如果用過R語言的merge或者是SQL等關系型數(shù)據(jù)庫的連接操作,基本上能很快理解。

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')

對于數(shù)據(jù)庫類型,有4種連接方式要注意,inner, outer, left, right??聪聢D進行了。

merge

而另一類pd.concat則是根據(jù)軸的標簽進行合并。

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)

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

第一步,下載操作數(shù)據(jù),并解壓

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

第二步,合并數(shù)據(jù)
使用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)

數(shù)據(jù)集取子集

這部分內容在前面有所提及,這里在基礎上繼續(xù)介紹

  • 選入(保留)變量,可以認為是選擇列
    選入變量,可以通過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)]
  • 選入觀測
    選入或剔除觀測(行)通常是成功的數(shù)據(jù)準備和數(shù)據(jù)分析的一個關鍵方面。比如說讓我們選取30歲以上的男性
leadership.ix[np.logical_and(leadership.gender == 'M', leadership.age > 30),:]

你會感覺這樣寫代碼太長了,我們需要一個類似R的subset的函數(shù),在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表示是否放回。
# 可以按數(shù)量
leadership.sample(n=2, replace=True)
# 可以按照比例
leadership.sample(frac=0.5, replace=True)

小結

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

當我們學會基本的數(shù)據(jù)處理之后,我們接著就可以根據(jù)不計其數(shù)函數(shù)進行更高級的操作。

當你看完后續(xù)的部分,你就能掌握復雜數(shù)據(jù)集的多數(shù)工具。無論你走到哪里,都將成為數(shù)據(jù)分析師艷羨的人物!

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容