Python 數據分析5

數據規整化數據庫

  清理數組

  轉換dom

  合併ide

  重塑函數

 

數據庫風格的DataFrame合併

pd.merge(df1, df2)  
# 默認會將重疊列的列名看成鍵,最好顯式的指定下,另外merge默認是使用的inner join

pd.merge(df1, df2, on='key') 

pd.merge(df3, df4, left_on='lkey', right_on='rkey')
# 若是兩個對象的列名不一樣,須要分別指定,如上df3裏面的是lkey,df4裏面的是rkey

pd.merge(df1, df2, how='outer')
# how參數用來指定鏈接方式,總共是inner、left、right、outer四種方式

pd.merge(df1, df2, on='key', how='left')
pd.merge(df1, df2, how='inner')
# 多對多合併如此簡單

pd.merge(left, right, on=['key1', 'key2'], how='outer')
# 多個合併列的時候,列表

pd.merge(left, right, on='key1')
# 須要注意的是若是以key1進行鏈接,可是兩個對象裏面有其餘相同列名的列存在, left會被表示成key_x,right會被表示成key_y

pd.merge(left, right, on='key1', suffixes=('_left', '_right'))
# 能夠自定義的爲左右設定後綴,這樣至關於定製了列名

 

索引上的合併

pd.merge(left1, right1, left_on='key', right_index=True)
# 左邊是的key,右邊的是index

pd.merge(left1, right1, left_on='key', right_index=True, how='outer')
# 合併方式跟以前同樣仍是inner、outer、left、right

pd.merge(lefth, righth, left_on=['key1', 'key2'], right_index=True)
# 合併鍵是多個列用列表指明

pd.merge(lefth, righth, left_on=['key1', 'key2'], right_index=True, how='outer')
# 一樣能夠設置合併方式

pd.merge(left2, right2, how='outer', left_index=True, right_index=True)
# 兩個合併對象都經過index鏈接

left2.join(right2, how='outer')
# dataframe裏面提供了join方法,用來更方便的實現按索引合併,不過join支持的是左鏈接

left1.join(right1, on='key')
# 還支持參數dataframe的索引跟調用dataframe的列進行鏈接

left2.join([right2, another])
left2.join([right2, another], how='outer')
# 對於簡單的索引合併,你還能夠向join傳入一組DataFrame

 

軸向鏈接

剛剛上面講了數據層的橫向鏈接合併,如今是關於數據堆疊。NumPy的concatenation函數能夠用NumPy數組來作:spa

arr = np.arange(12).reshape((3, 4))
np.concatenate([arr, arr], axis=1)
# 默認axis=0,axis爲1的時候就會變成橫向的拼接

而在pandas裏面提供了concat函數3d

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

pd.concat([s1, s2, s3])
# concat能夠將值和索引粘合在一塊兒

pd.concat([s1, s2, s3], axis=1)
# concat是在axis=0上工做的,最終產生一個新的Series。若是傳入axis=1,則結果就會變成一個DataFrame(axis=1是列)

pd.concat([s1, s2, s3], axis=1)

pd.concat([s1, s4], axis=1, join='inner')
# join='inner' 能夠獲得兩個對象的交集

pd.concat([s1, s4], axis=1, join_axes=[['a', 'c', 'b', 'e']])
# 你能夠經過join_axes指定要在其它軸上使用的索引
# 會創建在另一個索引上

result = pd.concat([s1, s1, s3], keys=['one','two', 'three'])
result.unstack()
#有個問題,參與鏈接的片斷在結果中區分不開。假設你想要在鏈接軸上建立一個層次化索引。使用keys參數便可達到這個目的

pd.concat([s1, s2, s3], axis=1, keys=['one','two', 'three'])
# 若是沿着axis=1對Series進行合併,則keys就會成爲DataFrame的列頭

df1 = pd.DataFrame(np.arange(6).reshape(3, 2), index=['a', 'b', 'c'], columns=['one', 'two'])
df2 = pd.DataFrame(5 + np.arange(4).reshape(2, 2), index=['a', 'c'],columns=['three', 'four'])
pd.concat([df1, df2], axis=1, keys=['level1', 'level2'])
# 在dataframe裏面也是同理

pd.concat({'level1': df1, 'level2': df2}, axis=1)
# 若是傳入的不是列表而是字典,則健值就變成keys

pd.concat([df1, df2], axis=1, keys=['level1', 'level2'], names=['upper', 'lower'])
此外還有兩個用於管理層次化索引建立方式的參數(參見表8-3)。舉個例子,咱們能夠用names參數命名建立的軸級別

df1 = pd.DataFrame(np.random.randn(3, 4), columns=['a', 'b', 'c', 'd'])
df2 = pd.DataFrame(np.random.randn(2, 3), columns=['b', 'd', 'a'])
pd.concat([df1, df2], ignore_index=True)
# DataFrame的行索引不包含任何相關數據,在這種狀況下,傳入ignore_index=True便可

 

合併重複數據

還有一種數據組合問題不能用簡單的合併(merge)或鏈接(concatenation)運算來處理。好比說,你可能有索引所有或部分重疊的兩個數據集。舉個有啓發性的例子,咱們使用NumPy的where函數,它表示一種等價於面向數組的if-else:code

a = pd.Series([np.nan, 2.5, np.nan, 3.5, 4.5, np.nan], index=['f', 'e', 'd', 'c', 'b', 'a'])
b = pd.Series(np.arange(len(a), dtype=np.float64), index=['f', 'e', 'd', 'c', 'b', 'a'])
b[-1] = np.nan
np.where(pd.isnull(a), b, a)
# array([ 0. ,  2.5,  2. ,  3.5,  4.5,  nan])
where(if a then a else b)

b[:-2].combine_first(a[2:])
# 以b爲標準,不存在的索引用a內的索引
a    NaN
b    4.5
c    3.0
d    2.0
e    1.0
f    0.0

對於dataframe,也是一樣的orm

df1 = pd.DataFrame({'a': [1., np.nan, 5., np.nan], 'b': [np.nan, 2., np.nan, 6.], 'c': range(2, 18, 4)})
df2 = pd.DataFrame({'a': [5., 4., np.nan, 3., 7.], 'b': [np.nan, 3., 4., 6., 8.]})
df1.combine_first(df2)

 

重塑層次化索引

  • stack:將數據的列「旋轉」爲行。
  • unstack:將數據的行「旋轉」爲列。
data = pd.DataFrame(np.arange(6).reshape((2, 3)), index=pd.Index(['Ohio','Colorado'], name='state'),columns=pd.Index(['one', 'two', 'three'],name='number'))

result = data.stack()
# 對該數據使用stack方法便可將列轉換爲行,獲得一個Series
result.unstack()
# 對於一個層次化索引的Series,你能夠用unstack將其重排爲一個DataFrame
result.unstack(0)
result.unstack('state')
# 默認狀況下,unstack操做的是最內層(stack也是如此)。傳入分層級別的編號或名稱便可對其它級別進行unstack操做

s1 = pd.Series([0, 1, 2, 3], index=['a', 'b', 'c', 'd'])
s2 = pd.Series([4, 5, 6], index=['c', 'd', 'e'])
data2 = pd.concat([s1, s2], keys=['one', 'two'])
data2.unstack()
# 若是不是全部的級別值都能在各分組中找到的話,則unstack操做可能會引入缺失數據

data2.unstack().stack()
# stack默認會濾除缺失數據,所以該運算是可逆的
data2.unstack().stack(dropna=False)
# 能夠經過參數來取消濾缺失數據
df = pd.DataFrame({'left': result, 'right': result + 5}, columns=pd.Index(['left', 'right'], name='side'))
df.unstack('state')
# 在對DataFrame進行unstack操做時,做爲旋轉軸的級別將會成爲結果中的最低級別

df.unstack('state').stack('side')
# 當調用stack,咱們能夠指明軸的名字

 

'長格式'旋轉爲'寬格式'

pivoted = data.pivot('date', 'item', 'value')
# pivot內(行索引, 列索引, 值)

pivoted = data.pivot('date', 'item')
#若是有兩個同時須要重塑的數據列,忽略最後一個參數,獲得的DataFrame就會帶有層次化的列

unstacked = ldata.set_index(['date', 'item']).unstack('item')
# pivot其實就是用set_index建立層次化索引,再用unstack重塑

 

將'寬格式'旋轉爲'長格式'

旋轉DataFrame的逆運算是pandas.melt。它不是將一列轉換到多個新的DataFrame,而是合併多個列成爲一個,產生一個比輸入長的DataFrame對象

df = pd.DataFrame({'key': ['foo', 'bar', 'baz'], 'A': [1, 2, 3], 'B': [4, 5, 6], 'C': [7, 8, 9]})
melted = pd.melt(df, ['key'])
# key列多是分組指標,其它的列是數據值。當使用pandas.melt,咱們必須指明哪些列是分組指標。

reshaped = melted.pivot('key', 'variable', 'value')
# 使用pivot,能夠重塑回原來的樣子

reshaped.reset_index()
# 由於pivot的結果從列建立了一個索引,用做行標籤,咱們可使用reset_index將數據移回列

pd.melt(df, id_vars=['key'], value_vars=['A', 'B'])
# 還能夠指定列的子集,做爲值的列

pd.melt(df, value_vars=['A', 'B', 'C'])
pd.melt(df, value_vars=['key', 'A', 'B'])
# pandas.melt也能夠不用分組指標

 

移除重複數據

DataFrame的duplicated方法返回一個布爾型Series,表示各行是不是重複行(前面出現過的行)

data = pd.DataFrame({'k1': ['one', 'two'] * 3 + ['two'], 'k2': [1, 1, 2, 3, 3, 4, 4]})
data.duplicated()

data.drop_duplicates()
# drop_duplicates重複的列移除掉,返回dataframe,移除的列是後出現的重複列,即上面的值爲True的

這兩個方法默認會判斷所有列,你也能夠指定部分列進行重複項判斷。假設咱們還有一列值,且只但願根據k1列過濾重複項

data['v1'] = range(7)
data.drop_duplicates(['k1'])

data.drop_duplicates(['k1', 'k2'], keep='last')
# duplicated和drop_duplicates默認保留的是第一個出現的值組合。傳入keep='last'則保留最後一個

 

利用函數或映射進行數據轉換

data = pd.DataFrame({'food': ['bacon', 'pulled pork', 'bacon','Pastrami', 'corned beef', 'Bacon','pastrami', 'honey ham', 'nova lox']
                  ,'ounces': [4, 3, 12, 6, 7.5, 8, 3, 5, 6]})   

假設你想要添加一列表示該肉類食物來源的動物類型。咱們先編寫一個不一樣肉類到動物的映射:

meat_to_animal = {
  'bacon': 'pig',
  'pulled pork': 'pig',
  'pastrami': 'cow',
  'corned beef': 'cow',
  'honey ham': 'pig',
  'nova lox': 'salmon'
}

Series的map方法能夠接受一個函數或含有映射關係的字典型對象,可是這裏有一個小問題,即有些肉類的首字母大寫了,而另外一些則沒有。所以,咱們還須要使用Series的str.lower方法,將各個值轉換爲小寫

data['animal'] = data['food'].str.lower().map(meat_to_animal)

也能夠傳入一個可以完成所有這些工做的函數

data['food'].map(lambda x: meat_to_animal[x.lower()])

替換值

data = pd.Series([1., -999., 2., -999., -1000., 3.])
data.replace(-999, np.nan)
# 將-999替換成NA值

data.replace([-999, -1000], np.nan)
# 將多個值轉換成NA值

data.replace([-999, -1000], [np.nan, 0])
#要讓每一個值有不一樣的替換值,能夠傳遞一個替換列表

data.replace({-999: np.nan, -1000: 0})
# 傳入的參數也能夠是字典

筆記:data.replace方法與data.str.replace不一樣,後者作的是字符串的元素級替換。

 

重命名軸索引

data = pd.DataFrame(np.arange(12).reshape((3, 4)), index=['Ohio', 'Colorado', 'New York'], columns=['one', 'two', 'three', 'four'])
transform = lambda x: x[:4].upper()
data.index.map(transform)

data.index = data.index.map(transform)  # 修改index

若是想要建立數據集的轉換版(而不是修改原始數據),比較實用的方法是rename

data.rename(index=str.title, columns=str.upper)

rename能夠結合字典型對象實現對部分軸標籤的更新:

data.rename(index={'OHIO': 'INDIANA'}, columns={'three': 'peekaboo'})

rename能夠實現複製DataFrame並對其索引和列標籤進行賦值。若是但願就地修改某個數據集,傳入inplace=True便可

data.rename(index={'OHIO': 'INDIANA'}, inplace=True)

 

離散化和麪元劃分

爲了便於分析,連續數據經常被離散化或拆分爲面元。

ages = [20, 22, 25, 27, 21, 23, 37, 31, 61, 45, 41, 32]

bins = [18, 25, 35, 60, 100]
cats = pd.cut(ages, bins)
# 將age列表數據劃分到面元,至關於歸類到區間

cats.codes
# array([0, 0, 0, 1, 0, 0, 2, 1, 3, 2, 2, 1], dtype=int8)

cats.categories
# IntervalIndex([(18, 25], (25, 35], (35, 60], (60, 100]], closed='right', dtype='interval[int64]')

pd.value_counts(cats)

跟「區間」的數學符號同樣,圓括號表示開端,而方括號則表示閉端(包括)。哪邊是閉端能夠經過right=False進行修改

cats = pd.cut(ages, bins, right=False)

你可 以經過傳遞一個列表或數組到labels,設置本身的面元名稱:

group_names = ['Youth', 'YoungAdult', 'MiddleAged', 'Senior']
pd.cut(ages, bins, labels=group_names)

若是向cut傳入的是面元的數量而不是確切的面元邊界,則它會根據數據的最小值和最大值計算等長面元。下面這個例子中,咱們將一些均勻分佈的數據分紅四組:

data = np.random.rand(20)
pd.cut(data, 4, precision=2)
# 選項precision=2,限定小數只有兩位。

qcut是一個很是相似於cut的函數,它能夠根據樣本分位數對數據進行面元劃分。根據數據的分佈狀況,cut可能沒法使各個面元中含有相同數量的數據點。而qcut因爲使用的是樣本分位數,所以能夠獲得大小基本相等的面元

data = np.random.randn(1000)
cats = pd.qcut(data, 4)

pd.value_counts(cats)
# 能夠發現四個區間是同樣大的都是250個元素

與cut相似,你也能夠傳遞自定義的分位數(0到1之間的數值,包含端點):

pd.qcut(data, [0, 0.1, 0.5, 0.9, 1.])
相關文章
相關標籤/搜索