05-1 pandas資料處理 刪除duplicated()、替換replace(),對映map(),重新命名rename()、聚合函式、排序take()、分組groupby()

luqin_發表於2019-03-24

pandas資料處理

1、刪除重複元素duplicated()

使用duplicated()函式檢測重複的行,返回元素為布林型別的Series物件,每個元素對應一行,如果該行不是第一次出現,則元素為True(是重複的)

  • 使用drop_duplicates()函式刪除重複的行
  • 使用duplicate()函式檢視重複的行
import numpy as np
import pandas as pd
from pandas import Series,DataFrame
data = [[100,100,100],[90,90,88],[100,100,100],[90,90,87],[100,100,100]]
columns = ['python','c++','java']
index = list('ABCDE')
df = DataFrame(data=data,index=index,columns=columns)
df
python c++ java
A 100 100 100
B 90 90 88
C 100 100 100
D 90 90 87
E 100 100 100
df.duplicated(keep="first") # 告訴我們 當前行是否重複
# 引數預設是 keep="first" 保留開始的 意思是如果發現很多重複的元素 第一個不算重複的 後面的才是 某一行重複 就返回True
結果為:
A    False
B    False
C     True
D    False
E     True
dtype: bool
df.duplicated(keep="last") # keep last 如果遇到重複的元素 最後一個不算重複的 前面的才算重複 這一行重複了 就是True
結果為:
A     True
B    False
C     True
D    False
E    False
dtype: bool
df.duplicated(keep=False) # 只要有和別人完全一樣的 不管在開頭還是結尾 都算重複 這一行如果是重複的就返回 True
結果為:
A     True
B    False
C     True
D    False
E     True
dtype: bool
df.drop_duplicates() # {'first', 'last', False}, default 'first'
python c++ java
A 100 100 100
B 90 90 88
D 90 90 87
df.drop_duplicates(keep="last") 
python c++ java
B 90 90 88
D 90 90 87
E 100 100 100
df.drop_duplicates(keep=False) 
python c++ java
B 90 90 88
D 90 90 87

2. 對映

對映的含義:建立一個對映關係列表,把values元素和一個特定的標籤或者字串繫結

包含三種操作:

  • replace()函式:替換元素
  • 最重要:map()函式:新建一列
  • rename()函式:替換索引

1) replace()函式:替換元素

使用replace()函式,對values進行替換操作

Series替換操作

  • 單值替換
    • 普通替換
    • 字典替換(推薦)
  • 多值替換
    • 列表替換
    • 字典替換(推薦)
s1 = Series(data = [100,'peppa',np.nan,'chengdu'])
s1
結果為:
0        100
1      peppa
2        NaN
3    chengdu
dtype: objec

單值替換 普通替換

s1.replace(to_replace="peppa",value="佩琪")
結果為:
0        100
1         佩琪
2        NaN
3    chengdu
dtype: object

單值替換 字典替換( )

s1.replace({"chengdu":"成都"})
結果為:
0      100
1    peppa
2      NaN
3       成都
dtype: object

多值替換 列表替換 s1.replace([要替換的值1,要替換的值2,…],[替換成什麼1,替換成什麼2,…])

s1.replace([100,np.nan],["滿分","空值"])
結果為:
0         滿分
1      peppa
2         空值
3    chengdu
dtype: object

多值替換 字典替換( { 要替換的值:替換成什麼,要替換的值:替換成什麼 } )

s1.replace({100:"滿分","peppa":"佩琪"})
結果為:
0         滿分
1         佩琪
2        NaN
3    chengdu
dtype: object

Series引數說明:

  • method:對指定的值使用相鄰的值填充
  • limit:設定填充次數
s2 = Series(data=np.array([80,100,100,100,89,78]))
s2
結果為:
0     80
1    100
2    100
3    100
4     89
5     78
dtype: int32

如果指定value不好 還可以找值來填充

s2.replace(to_replace=100,method="bfill") # 從後面找值來替換當前值
結果為:
0    80
1    89
2    89
3    89
4    89
5    78
dtype: int32
s2.replace(to_replace=100,method="ffill") # 從前面找
結果為:
0    80
1    80
2    80
3    80
4    89
5    78
dtype: int32
s2.replace(to_replace=100,method="ffill",limit=1) # limit 指定是最多往前或者往後找幾個, 如果找不到就不填充了 ,limit 預設是None不限制
結果為:
0     80
1     80
2    100
3    100
4     89
5     78
dtype: int32

DataFrame替換操作

  • 單值替換

    • 普通替換
    • 按列指定單值替換{列標籤:目標值}
  • 多值替換

    • 列表替換
    • 單字典替換(推薦)
df = pd.read_excel("../data/data.xls",sheet_name=0)
df
0 1 2 3 4
A NaN NaN NaN Beijing
B 69.0 142.0 29 Beijing
C 111.0 7.0 2 Beijing
D 139.0 19.0 125 shanghai
E 12.0 66.0 Beijing shanghai

普通的單值替換

df.replace(to_replace='Beijing',value='北京')
0 1 2 3 4
A NaN NaN NaN 北京
B 69.0 142.0 29 北京
C 111.0 7.0 2 北京
D 139.0 19.0 125 shanghai
E 12.0 66.0 北京 shanghai

按列指定單值換目標值 ({列索引,待替換值},目標值)

df.replace({4:'Beijing'},'首都')
0 1 2 3 4
A NaN NaN NaN 首都
B 69.0 142.0 29 首都
C 111.0 7.0 2 首都
D 139.0 19.0 125 shanghai
E 12.0 66.0 Beijing shanghai

多值替換 列表替換,replace([要替換的1,要替換的2…],[替換成1,替換成2])

df.replace([66,'甲','shanghai'],[100,'first','上海'])
0 1 2 3 4
A first NaN NaN NaN Beijing
B 69.0 142.0 29 Beijing
C 111.0 7.0 2 Beijing
D 139.0 19.0 125 上海
E 12.0 100.0 北京 上海

多值替換 字典替換,replace({要替換的1:替換成的值1,要替換的2:替換成的值2,…}) 可以將沒有的值也放在這裡 不會報錯 將來可以整個專案使用一個過濾器,我們可以在 字典裡面寫很多值 字典中的值即使找不到也不會報錯

df.replace({66:100,'乙':'second','Beijing':'BEIJING','沒有的值':'也可以放'})
0 1 2 3 4
A NaN NaN NaN BEIJING
B second 69.0 142.0 29 BEIJING
C 111.0 7.0 2 BEIJING
D 139.0 19.0 125 shanghai
E 12.0 100.0 BEIJING shanghai

注意:DataFrame中,無法使用method和limit引數

============================================

練習19:

假設張三李四的成績單裡有滿分的情況,老師認為是作弊,把所有滿分的情況(包括150,300分)都記0分,如何實現?

============================================

data = [[150,300],[150,300]]
index = ["張三","李四"]
columns = ["數學","理綜"]
df = DataFrame(data,index,columns)
df
數學 理綜
張三 150 300
李四 150 300

替換多個

df.replace({150:0,300:0})
df.replace([150,300],[0,0])
數學 理綜
張三 0 0
李四 0 0

單值替換

df1 = df.replace(to_replace=150,value=0)
df1 = df.replace({"數學":150},0)
數學 理綜
張三 0 300
李四 0 300
df1.replace(to_replace=300,value=0)
df1.replace({"理綜":300},0)
數學 理綜
張三 0 300
李四 0 300

2) map()函式:新建一列

  • map(字典) 字典的鍵要足以匹配所有的資料,否則出現NaN
  • map()可以對映新一列資料
  • map()中可以使用lambd表示式
  • map()中可以使用方法,可以是自定義的方法

注意 map()中不能使用sum之類的函式,for迴圈

score = pd.read_excel('./data.xls',sheet_name=1)
score
姓名 語文 數學 python php
0 小明 90 98 90 98
1 小紅 44 89 44 89
2 小芳 98 90 90 98
3 小李 89 44 44 89
4 李元芳 78 98 98 87
5 狄仁傑 66 44 44 89

對映字典

map_dic = {'小明':'北京','小紅':'上海','小芳':'北京',
 '小李':'廣州','李元芳':'成都','狄仁傑':'成都'}

map函式不是DataFrame的方法,而是Sereis物件的方法, 可以傳入對映字典

score["姓名"].map(map_dic)
結果為:
0    北京
1    上海
2    北京
3    廣州
4    成都
5    成都
Name: 姓名, dtype: object
score["所在城市"] = score["姓名"].map(map_dic) # 可以傳入字典
score
姓名 語文 數學 python php 所在城市
0 小明 90 98 90 98 北京
1 小紅 44 89 44 89 上海
2 小芳 98 90 90 98 北京
3 小李 89 44 44 89 廣州
4 李元芳 78 98 98 87 成都
5 狄仁傑 66 44 44 89 成都

還可以傳入 拉姆達表示式 如 lambda x:x+10

score["python"]= score["python"].map(lambda x:x+10)
score        #python資料加10
姓名 語文 數學 python php 所在城市
0 小明 90 98 100 98 北京
1 小紅 44 89 54 89 上海
2 小芳 98 90 100 98 北京
3 小李 89 44 54 89 廣州
4 李元芳 78 98 108 87 成都
5 狄仁傑 66 44 54 89 成都

還可以傳入自定義函式

def fn(x):
    return x-20
score["php"]= score["php"].map(fn) #這裡一定要注意,是把函式名傳入,千萬不要加小括號去呼叫    
score
姓名 語文 數學 python php 所在城市
0 小明 90 98 100 18 北京
1 小紅 44 89 54 9 上海
2 小芳 98 90 100 18 北京
3 小李 89 44 54 9 廣州
4 李元芳 78 98 108 7 成都
5 狄仁傑 66 44 54 9 成都
data = np.random.randint(0,150,size=(5,3))
columns = ['python','java','php']
index = ['peppa','mery','tom','jack','rose']
df = DataFrame(data,index,columns)
df
python java php
peppa 23 98 81
mery 33 31 74
tom 59 24 101
jack 56 143 96
rose 94 13 89
def map_score(x):
    if x>120:
        return "exellent"
    elif x<90:
        return "failed"
    else:
        return "pass" 
df["phpx"]= df["php"].map(map_score)
df
python java php phpx
peppa 23 98 81 failed
mery 33 31 74 failed
tom 59 24 101 pass
jack 56 143 96 pass
rose 94 13 89 failed

3) rename()函式:替換索引

對DataFrame的索引名進行更改,仍然是新建一個字典

score = pd.concat((df,df),keys=['A','B'],axis=1)
score
A B
python java php phpx python java php phpx
peppa 23 98 81 failed 23 98 81 failed
mery 33 31 74 failed 33 31 74 failed
tom 59 24 101 pass 59 24 101 pass
jack 56 143 96 pass 56 143 96 pass
rose 94 13 89 failed 94 13 89 failed
map_dic = {'peppa':'帥氣','mery':'美麗','python':'蟒蛇',
  'java':'咖啡','php':'拍黃片','A':'上','B':'下'}         
score.rename(map_dic) # 預設是替換 行的名稱
A B
python java php phpx python java php phpx
帥氣 23 98 81 failed 23 98 81 failed
美麗 33 31 74 failed 33 31 74 failed
tom 59 24 101 pass 59 24 101 pass
jack 56 143 96 pass 56 143 96 pass
rose 94 13 89 failed 94 13 89 failed
score.rename(columns=map_dic) # 指定columns可以對列名稱進行替換
蟒蛇 咖啡 拍黃片 phpx 蟒蛇 咖啡 拍黃片 phpx
peppa 23 98 81 failed 23 98 81 failed
mery 33 31 74 failed 33 31 74 failed
tom 59 24 101 pass 59 24 101 pass
jack 56 143 96 pass 56 143 96 pass
rose 94 13 89 failed 94 13 89 failed
score.rename(columns=map_dic,level=0) # 通過level引數 可以指定具體對哪一層級進行替換  
python java php phpx python java php phpx
peppa 23 98 81 failed 23 98 81 failed
mery 33 31 74 failed 33 31 74 failed
tom 59 24 101 pass 59 24 101 pass
jack 56 143 96 pass 56 143 96 pass
rose 94 13 89 failed 94 13 89 failed
score.rename(columns=map_dic,level=1) # 0 是最外層 
A B
蟒蛇 咖啡 拍黃片 phpx 蟒蛇 咖啡 拍黃片 phpx
peppa 23 98 81 failed 23 98 81 failed
mery 33 31 74 failed 33 31 74 failed
tom 59 24 101 pass 59 24 101 pass
jack 56 143 96 pass 56 143 96 pass
rose 94 13 89 failed 94 13 89 failed
score.rename(columns=map_dic,level=-1)
A B
蟒蛇 咖啡 拍黃片 phpx 蟒蛇 咖啡 拍黃片 phpx
peppa 23 98 81 failed 23 98 81 failed
mery 33 31 74 failed 33 31 74 failed
tom 59 24 101 pass 59 24 101 pass
jack 56 143 96 pass 56 143 96 pass
rose 94 13 89 failed 94 13 89 failed

使用rename()函式替換行索引

  • index 替換行索引
  • columns 替換列索引
  • level 指定多維索引的維度

3. 使用聚合操作對資料異常值檢測和過濾

使用 df.describe() 函式檢視每一列的描述性統計量

data = np.random.randn(1000,5)
df = DataFrame(data)
df
df.describe() 
0 1 2 3 4
count 1000.000000 1000.000000 1000.000000 1000.000000 1000.000000
mean -0.041642 0.013773 0.014445 0.019873 0.042694
std 1.025455 1.013594 0.983300 1.006718 0.968992
min -2.977845 -3.063590 -3.032535 -2.888868 -3.191746
25% -0.703325 -0.712109 -0.645296 -0.644217 -0.597502
50% -0.054138 0.057187 0.001426 0.020060 0.057830
75% 0.664146 0.704886 0.653665 0.690365 0.725881
max 3.777147 3.113222 3.149599 3.305668 3.050308

使用std()函式可以求得DataFrame物件每一列的標準差

df.std()
結果為:
0    1.025455
1    1.013594
2    0.983300
3    1.006718
4    0.968992
dtype: float64

根據每一列或行的標準差,對DataFrame元素進行過濾。

藉助any()或all()函式, 測試是否有True,有一個或以上返回True,反之返回False

對每一列應用篩選條件,去除標準差太大的資料

# 尋找異常資料 太大的 或者 太小的
df.mean() # 各個列的平均值 是一個Series
df - df.mean() # DataFrame - Series DataFrame中的每一行都和Series做減法 對應的列相減
df - df.mean() > 3*df.std()
(df - df.mean() > 3*df.std()).any()
(df - df.mean() > 3*df.std()).any(axis=1)  #
df[(df - df.mean() > 3*df.std()).any(axis=1)] #找到異常的行

刪除特定索引df.drop(labels,inplace = True)

drop_idx = df[(df - df.mean() > 3*df.std()).any(axis=1)].index
df.drop(drop_idx).shape

============================================

練習:

新建一個形狀為10000*3的標準正態分佈的DataFrame(np.random.randn),去除掉所有滿足以下情況的行:其中任一元素絕對值大於3倍標準差

============================================

4. 排序

使用.take()函式排序

  • take()函式接受一個索引列表,用數字表示
  • eg:df.take([1,3,4,2,5])

可以藉助np.random.permutation()函式隨機排序

data = np.random.randint(0,100,size=(5,5))
index = list('ABCDE')
columns = list('甲乙丙丁戊')
df = DataFrame(data=data,index=index,columns=columns)
df
A 19 22 59 34 79
B 43 71 71 25 77
C 46 63 43 14 66
D 89 46 86 33 40
E 36 79 66 68 67
df.take([3,2,1]) # 按照索引去取行 順序隨意 還可以重複 還可以不完全
D 89 46 86 33 40
C 46 63 43 14 66
B 43 71 71 25 77
df.take([3,3,3,3]) 
D 89 46 86 33 40
D 89 46 86 33 40
D 89 46 86 33 40
D 89 46 86 33 40
np.random.permutation(5)
結果為:
array([3, 1, 4, 2, 0])
df.take(np.random.permutation(5)) # 隨機排序 (行不會少 也不會重複 只是順序隨機變換)
B 43 71 71 25 77
D 89 46 86 33 40
C 46 63 43 14 66
A 19 22 59 34 79
E 36 79 66 68 67

隨機抽樣

當DataFrame規模足夠大時,直接使用np.random.randint()函式,就配合take()函式實現隨機抽樣

df.take(np.random.randint(0,5,size=2)) # 隨機抽樣
B 43 71 71 25 77
C 46 63 43 14 66

5. 資料分類處理【重點】

資料聚合是資料處理的最後一步,通常是要使每一個陣列生成一個單一的數值。

資料分類處理:

  • 分組:先把資料分為幾組
  • 用函式處理:為不同組的資料應用不同的函式以轉換資料
  • 合併:把不同組得到的結果合併起來

資料分類處理的核心:

 - groupby()函式
 - groups屬性檢視分組情況

In [547]:

df = DataFrame({'item':['蘋果','香蕉','橘子','香蕉','橘子','蘋果','蘋果'],
                'price':[4,3,3,2.5,4,2,2.8],
               'color':['red','yellow','yellow','green','green','green','yello'],
               'weight':[12,20,50,30,20,44,37]})
df        
item price color weight
0 蘋果 4.0 red 12
1 香蕉 3.0 yellow 20
2 橘子 3.0 yellow 50
3 香蕉 2.5 green 30
4 橘子 4.0 green 20
5 蘋果 2.0 green 44
6 蘋果 2.8 yello 37
  • 根據item分組,通過groups屬性檢視結果
df.groupby("item").groups
結果為:
{'橘子': Int64Index([2, 4], dtype='int64'),
 '蘋果': Int64Index([0, 5, 6], dtype='int64'),
 '香蕉': Int64Index([1, 3], dtype='int64')}
  • 獲取weight的總和
df.groupby("item")["weight"].sum() #各類水果的總重量
結果為:
item
橘子    70
蘋果    93
香蕉    50
Name: weight, dtype: int64
  • 把總和跟df進行merge合併
df2 = DataFrame(df.groupby("item")["weight"].sum())
df2
weight
item
橘子 70
蘋果 93
香蕉 50
pd.merge(df,df2,on="item",how="outer",suffixes=["","_total"])
item price color weight weight_total
0 蘋果 4.0 red 12 93
1 蘋果 2.0 green 44 93
2 蘋果 2.8 yello 37 93
3 香蕉 3.0 yellow 20 50
4 香蕉 2.5 green 30 50
5 橘子 3.0 yellow 50 70
6 橘子 4.0 green 20 70

相關文章