乾淨整潔的數據是後續進行研究和分析的基礎。數據科學家們會花費大量的時間來清理數據集,毫不誇張地說,數據清洗會佔據他們80%的工作時間,而真正用來分析數據的時間只佔到20%左右。
所以,數據清洗到底是在清洗些什麼?
通常來說,你所獲取到的原始數據不能直接用來分析,因為它們會有各種各樣的問題,如包含無效信息,列名不規範、格式不一致,存在重複值,缺失值,異常值等
本文會給大家介紹一些Python中自帶的Pandas和NumPy庫進行數據清洗的實用技巧。
一、 read_csv 讀取文件這是讀取數據的入門級命令,在分析一個數據集的時候,很多信息其實是用不到的,因此,需要去除不必要的行或列。這裡以csv文件為例,在導入的時候就可以通過設置pd.read_csv()裡面的參數來實現這個目的。
先來感受一下官方文檔中給出的詳細解釋,裡面的參數是相當的多,本文只介紹比較常用的幾個,感興趣的話,可以好好研究一下文檔,這些參數還是非常好用的,能省去很多導入後整理的工作。
【header】默認header=0,即將文件中的0行作為列名和數據的開頭,但有時候0行的數據是無關的,我們想跳過0行,讓1行作為數據的開頭,可以通過將header設置為1來實現。
【usecols】根據列的位置或名字,如[0,1,2]或[『a』, 『b』, 『c』],選出特定的列。
【nrows】要導入的數據行數,在數據量很大、但只想導入其中一部分時使用。
【names】:指定自定義列名。此列表中不允許有重複項。
【index_col】: 指定用作數據框的行標籤的列,以字符串名稱或列索引的形式給出
【na_values】:指定數據框中需要被識別為NA/NaN 的字符串或字符串列表
當一列缺失時直接指定 na_values='-1'
當多列缺失時,增加字典的鍵值對即可 na_values={'a':'-1','b':'-10'}
當原始數據的列名不好理解,或者不夠簡潔時,可以用.rename()方法進行修改。這裡我們把英文的列名改成中文,先創建一個字典,把要修改的列名定義好,然後調用rename()方法。
new_names = {'舊列名': '新列名'}
df.rename(columns=new_names, inplace=True)
數據默認的索引是從0開始的有序整數,但如果想把某一列設置為新的索引,除了可以用read_csv()裡的參數index_col,還可以用.set_index()方法實現。
df.set_index('列名', inplace=True)
另外補充,如果數據經過刪除或結構調整後,我們可以重置索引,讓索引從0開始,依次排序。
df3.reset_index(drop=True)
字符串str操作是非常實用的,因為列中總是會包含不必要的字符,常用的方法如下:
lower()
upper()
str.lower() 是把大寫轉換成小寫,同理,str.upper()是把小寫轉換成大寫,將示例中用大寫字母表示的索引轉換成小寫。
capitalize()
設置首字母大寫
replace()
str.replace("a", "") 替換特定字符。這裡把列中的a去掉,替換成空字符。
strip()
去除字符串中的頭尾空格、以及\n \t。
split()
str.split('x') 使用字符串中的'x'字符作為分隔符,將字符串分隔成列表。這裡將列中的值以'.'進行分割。
get()
str.get() 選取列表中某個位置的值。接著上面分割後的結果,我們用str.get(0)取出列表中前一個位置的數值,生成新的一列。
contains()
str.contains() 判斷是否存在某個字符,返回的是布爾值。
find()
str.find("-")檢測字符串中是否包含"-",如果包含,則返回該子字符串開始位置的索引值;如果不包含,則返回-1。
學完基本的字符串操作方法,我們來看一下如何結合NumPy來提高字符串操作的效率。
我們可以將Pandas中的.str()方法與NumPy的np.where函數相結合,np.where函數是Excel的IF()宏的矢量化形式,它的語法如下:
np.where(condition, then, else)
如果condition條件為真,則執行then,否則執行else。這裡的condition條件可以是一個類數組的對象,也可以是一個布爾表達式,我們也可以利用np.where函數嵌套多個條件進行矢量化計算和判斷。
np.where(condition1, x1,
np.where(condition2, x2,
np.where(condition3, x3, ...)))
接下來就要對列中的字符串進行整理,除了利用循環和.str()方法相結合的方式進行操作,我們還可以選擇用applymap()方法,它會將傳入的函數作用於整個DataFrame所有行列中的每個元素。
先定義函數get_citystate(item),功能是只提取元素中的有效信息。然後,我們將這個函數傳入applymap(),並應用於df3,看起來是不是乾淨多了,結果如下:
六、copy如果你沒聽說過它的話,我不得強調它的重要性。輸入下面的命令:
import pandas as pd
df1 = pd.DataFrame({ a :[0,0,0], b : [1,1,1]})
df2 = df1
df2[ a ] = df2[ a ] + 1
df1.head()
你會發現df1已經發生了改變。這是因為df2 = df1並不是生成一個df1的複製品並把它賦值給df2,而是設定一個指向df1的指針。所以只要是針對df2的改變,也會相應地作用在df1上。為了解決這個問題,你既可以這樣做:
df2 = df1.copy()
也可以這樣做:
from copy import deepcopy
df2 = deepcopy(df1)
這個命令用於檢查值的分布。你想要檢查下「c」列中出現的值以及每個值所出現的頻率,可以使用:
df[ c ].value_counts()
下面是一些有用的小技巧/參數:
normalize = True:查看每個值出現的頻率而不是頻次數。
dropna = False: 把缺失值也保留在這次統計中。
sort = False: 將數據按照值來排序而不是按照出現次數排序。
df[『c].value_counts().reset_index(): 將這個統計錶轉換成pandas的dataframe並且進行處理。
八、lsin () ,依據指定ID來選取行lsin () 用於過濾數據幀。Isin () 有助於選擇特定列中具有特定(或多個)值的行。
在SQL中我們可以使用 SELECT * FROM … WHERE ID in (『A001』,『C022』, …)來獲取含有指定ID的記錄。如果你也想在Pandas中做類似的事情,你可以使用:
df_filter = df[ ID ].isin([ A001 , C022 ,...])
df[df_filter]
select_dtypes() 的作用是,基於 dtypes 的列返回數據幀列的一個子集。這個函數的參數可設置為包含所有擁有特定數據類型的列,亦或者設置為排除具有特定數據類型的列。
# We ll use the same dataframe that we used for read_csv
framex = df.select_dtypes(include="float64")# Returns only time column
pivot_table( ) 也是 Pandas 中一個非常有用的函數。如果對 pivot_table( ) 在 excel 中的使用有所了解,那麼就非常容易上手了。
pivot_table(data, values=None, index=None, columns=None,aggfunc='mean', fill_value=None, margins=False, dropna=True, margins_name='All')
pivot_table有四個最重要的參數index、values、columns、aggfunc
Index就是層次欄位,要通過透視表獲取什麼信息就按照相應的順序設置欄位
Values可以對需要的計算數據進行篩選
Columns類似Index可以設置列層次欄位,它不是一個必要參數,作為一種分割數據的可選方式
aggfunc參數可以設置我們對數據聚合時進行的函數操作
# Create a sample dataframe
school = pd.DataFrame({ "A" : [ "Jay" , "Usher" , "Nicky" , " Romero" , "Will" ],
"B" : [ "Masters" , "Graduate", "Graduate" , ''Masters" , "Graduate" ],
"C" : [26, 22, 20, 23, 24]})# Lets create a pivot table to segregate students based on age and course
table = pd.pivot_table(school, values = "A" , index =[ "B" , "C" ],
columns =[ "B" ], aggfunc = np.sum, fill_value="Not Available")
table
df=pd.read_csv('train.csv')
def missing_cal(df):
"""
df :數據集
return:每個變量的缺失率
"""
missing_series = df.isnull().sum()/df.shape[0]
missing_df = pd.DataFrame(missing_series).reset_index()
missing_df = missing_df.rename(columns={'index':'col',
0:'missing_pct'})
missing_df = missing_df.sort_values('missing_pct',ascending=False).reset_index(drop=True)
return missing_df
missing_cal(df)
如果需要計算樣本的缺失率分布,只要加上參數axis=1
十二、獲取分組裡最大值所在的行方法分為分組中有重複值和無重複值兩種。無重複值的情況。
df = pd.DataFrame({'Sp':['a','b','c','d','e','f'], 'Mt':['s1', 's1', 's2','s2','s2','s3'], 'Value':[1,2,3,4,5,6], 'Count':[3,2,5,10,10,6]})
df
df.iloc[df.groupby(['Mt']).apply(lambda x: x['Count'].idxmax())]
先按Mt列進行分組,然後對分組之後的數據框使用idxmax函數取出Count最大值所在的列,再用iloc位置索引將行取出。有重複值的情況
df["rank"] = df.groupby("ID")["score"].rank(method="min", ascending=False).astype(np.int64)
df[df["rank"] == 1][["ID", "class"]]
對ID進行分組之後再對分數應用rank函數,分數相同的情況會賦予相同的排名,然後取出排名為1的數據。
十三、多列合併為一行df = pd.DataFrame({'id_part':['a','b','c','d'], 'pred':[0.1,0.2,0.3,0.4], 'pred_class':['women','man','cat','dog'], 'v_id':['d1','d2','d3','d1']})
df.groupby(['v_id']).agg({'pred_class': [', '.join],'pred': lambda x: list(x),
'id_part': 'first'}).reset_index()
df = pd.DataFrame([['A',1],['A',3],['A',2],['B',5],['B',9]], columns = ['name','score'])
介紹兩種高效地組內排序的方法。
df.sort_values(['name','score'], ascending = [True,False])
df.groupby('name').apply(lambda x: x.sort_values('score', ascending=False)).reset_index(drop=True)
drinks = pd.read_csv('data/drinks.csv')
# 選擇所有數值型的列
drinks.select_dtypes(include=['number']).head()
# 選擇所有字符型的列
drinks.select_dtypes(include=['object']).head()
drinks.select_dtypes(include=['number','object','category','datetime']).head()
# 用 exclude 關鍵字排除指定的數據類型
drinks.select_dtypes(exclude=['number']).head()
df = pd.DataFrame({'列1':['1.1','2.2','3.3'],
'列2':['4.4','5.5','6.6'],
'列3':['7.7','8.8','-']})
df
df.astype({'列1':'float','列2':'float'}).dtypes
用這種方式轉換第三列會出錯,因為這列裡包含一個代表 0 的下劃線,pandas 無法自動判斷這個下劃線。為了解決這個問題,可以使用 to_numeric() 函數來處理第三列,讓 pandas 把任意無效輸入轉為 NaN。
df = df.apply(pd.to_numeric, errors='coerce').fillna(0)
方法一:只讀取切實所需的列,使用usecols參數
cols = ['beer_servings','continent']
small_drinks = pd.read_csv('data/drinks.csv', usecols=cols)
方法二:把包含類別型數據的 object 列轉換為 Category 數據類型,通過指定 dtype 參數實現。
dtypes ={'continent':'category'}
smaller_drinks = pd.read_csv('data/drinks.csv',usecols=cols, dtype=dtypes)
df = pd.DataFrame({'姓名':['張 三','李 四','王 五'],
'所在地':['北京-東城區','上海-黃浦區','廣州-白雲區']})
df
df.姓名.str.split(' ', expand=True)
11.把 Series 裡的列錶轉換為 DataFrame
df = pd.DataFrame({'列1':['a','b','c'],'列2':[[10,20], [20,30], [30,40]]})
df
df_new = df.列2.apply(pd.Series)
pd.concat([df,df_new], axis='columns')
希望本文的內容對大家的學習或者工作能帶來一定的幫助,每天進步一點點,加油~