天天看點

DataCamp “Data Scientist with Python track” 第七章 Cleaning Data in Python 學習筆記

Exploring your data

在資料分析過程中,我們總會遇到不那麼“幹淨”的資料,比如:Inconsistent column names、Missing data、Outliers、Duplicate rows、Untidy、Need to process columns、Column types can signal unexpected data values等等,是以在資料分析之前我們先要對資料進行處理。其中一個對資料進行檢驗的方法是“.info”,可以檢視有多少列缺失資料,或者不符合type的資料,比如下面的例子中,隻有122個population資料,而我們的期望是164個。

# Print the info of df
print(df.info())

# Print the info of df_subset
print(df_subset.info())
           
DataCamp “Data Scientist with Python track” 第七章 Cleaning Data in Python 學習筆記

除了“.info”以外我們還有很多種不同的檢驗資料的方法,比如“.describe()”,這讓我們可以得到中位數、最大值等等一些列分布情況,進而在這幾個值中找出資料的問題所在。

DataCamp “Data Scientist with Python track” 第七章 Cleaning Data in Python 學習筆記

還有在列中計算出現頻率的方法,可以用來驗證缺少多少組資料(注意這裡特意使用了兩種寫法,如果沒有特殊字元也不是python指令的話可以使用第二行的簡潔寫法):

# Print the value counts for 'Borough'
print(df['Borough'].value_counts(dropna=False))

# Print the value_counts for 'State'
print(df.State.value_counts(dropna = False))

# Print the value counts for 'Site Fill'
print(df['Site Fill'].value_counts(dropna = False))
           
Visual exploratory data analysis

在這之後我們還可以更加具象化我們的資料,比如通過各種圖表來檢驗資料:

# Import matplotlib.pyplot
import matplotlib.pyplot as plt

# Plot the histogram
df['Existing Zoning Sqft'].plot(kind='hist', rot=70, logx=True, logy=True)

# Display the histogram
plt.show()
           

*這裡要注明的幾個argument包括kind(圖表類型)、rot(Rotate the axis labels by 70 degrees),還有logx和logy(use a log scale for both axes)。

Boxplot是一個可以用來檢驗資料好壞的圖表,在生成的圖表中很容易就可以發現bad point:

# Import necessary modules
import pandas as pd
import matplotlib.pyplot as plt

# Create the boxplot
df.boxplot(column='initial_cost', by='Borough', rot=90)

# Display the plot
plt.show()
           
DataCamp “Data Scientist with Python track” 第七章 Cleaning Data in Python 學習筆記

當然還可以使用早期學習的kind='scatter'。

Tidying data for analysis

 從一篇關于Tidy Data的paper裡我們得知了關于Tidy Data的三個标準:

  • 每清單示的必須是不同的variables
  • 每行表示individual observations
  • Observational units form tables

使用Melting可以幫助我們将data轉換成Tidy Data,在pd.melt()中我們要定義幾個argument:frame(要生成的資料形式)、id_vars(将某列資料固定,以此順序生成其他資料)、value_vars(需要依照固定id_vars來生成的資料名稱,如果不特意注明的話将melting除了id_vars以外的所有資料列)以及var_name和value_name,最後的這兩個name是用來定義最終輸出的var和value兩列的名稱的。

# Melt airquality: airquality_melt
airquality_melt = pd.melt(frame=df, id_vars=['Month','Day'],value_vars=['Ozone', 'Solar.R', 'Wind', 'Temp'])
           

而另一種調整資料的方法是Pivoting,“.pivot_table()”,其中要定義的幾個argument分别是:index、columns、values和aggfunc,這更像是把某些value放到橫軸為columns、縱軸為index的表格中,但要注意的是一旦(index, columns)不僅僅隻有一個值,我們要使用aggfunc,将這些值取平均數或者其他操作。

DataCamp “Data Scientist with Python track” 第七章 Cleaning Data in Python 學習筆記
# Pivot airquality_melt: airquality_pivot
airquality_pivot = airquality_melt.pivot_table(index=['Month', 'Day'], columns='measurement', values='reading')
           

最後我們迎來了一種剝離資料的方法,在我們原本的資料中,有很多資料都以組合形式出現,比如性别和年齡的組合:“m22”,而我們在分析過程中往往需要将這些組合資料分離開,這裡就引入了Spliting。對于一般資料,比如“m22”,我們可以用下面的方式建立一個column:

# Create the 'gender' column
tb_melt['gender'] = tb_melt.variable.str[0]
           

但是一旦組合資料中出現一些符号,比如“Cases_Guinea”,上面提到的方法就無法實作剝離資料的功能,是以我們用到了split函數,首先告訴python我們要在哪個字元處分離資料,然後利用“.get()”得到兩部分資料:

# Melt ebola: ebola_melt
ebola_melt = pd.melt(ebola, id_vars=['Date', 'Day'], var_name='type_country', value_name='counts')

# Create the 'str_split' column
ebola_melt['str_split'] = ebola_melt.type_country.str.split('_')

# Create the 'type' column
ebola_melt['type'] = ebola_melt.str_split.str.get(0)

# Create the 'country' column
ebola_melt['country'] = ebola_melt.str_split.str.get(1)

# Print the head of ebola_melt
print(ebola_melt.head())
           
Combining data for analysis

在資料中大部分的資料都是獨立存在的,我們不可能每天攜帶一個巨大的資料集進行分析,是以在分析的過程中我們都是将需要的資料集進行提取和合并之後再進行分析等操作的。Globbing可以幫助我們按照一定的模式篩選檔案:Wildcards包括*和?,如果我們想篩選csv檔案時可以使用:“*.csv”,當查找字元時可以使用“file_?.csv”,而輸出結果是一個關于檔案名稱的list。

*注意我們在代碼中定義了篩選的模式pattern

# Import necessary modules
import glob
import pandas as pd

# Write the pattern: pattern
pattern = '*.csv'

# Save all file matches: csv_files
csv_files = glob.glob(pattern)

# Print the file names
print(csv_files)

# Load the second file into a DataFrame: csv2
csv2 = pd.read_csv(csv_files[1])

# Print the head of csv2
print(csv2.head())
           

我們可以使用merge方法對資料集進行合并,我們pass兩個量(檔案)給merge,并且告訴function我們按照什麼方式合并(比如相同的兩列):

# Merge the DataFrames: m2o
m2o = pd.merge(left=site, right=visited, left_on='name', right_on='site')
           

對于一些資料因為datatype不一樣而無法合并的問題,我們可以通過對資料進行類型轉變進而完成合并:

# Convert the sex column to type 'category'
tips.sex = tips.sex.astype('category')
           
# Convert 'total_bill' to a numeric dtype
tips['total_bill'] = pd.to_numeric(tips['total_bill'], errors='coerce')
           
Using regular expressions to clean strings

在資料科學中我們往往需要一些表示資料的方法(regular expression),進而讓系統更容易知道我們想表達什麼:

DataCamp “Data Scientist with Python track” 第七章 Cleaning Data in Python 學習筆記

在下面這個練習中,我們可以使用re package和資料表達方式來生成一個模式的電話号碼并檢視目标号碼是否與其比對:

# Import the regular expression module
import re

# Compile the pattern: prog
prog = re.compile('\d{3}-\d{3}-\d{4}')

# See if the pattern matches
result = prog.match('123-456-7890')
print(bool(result))

# See if the pattern matches
result2 = prog.match('1123-456-7890')
print(bool(result2))
           

也可以使用re package将資料中的特定目标提取出來:

* "\d" is the pattern required to find digits. This should be followed with a "+" so that the previous element is matched one or more times. This ensures that "10" is viewed as one number and not as "1" and "0". 

# Import the regular expression module
import re

# Find the numeric values: matches
matches = re.findall('\d+', 'the recipe calls for 10 strawberries and 1 banana')

# Print the matches
print(matches)
           
Duplicate and missing data

在資料進行中常見有相同的備援資料和缺失資料(NaN),我們可以使用“.drop_duplicates()” 指令來删除完全一樣的兩行資料,同樣我們也可以使用相似的指令來drop missing data:“.dropna()”,進而使得資料隻包括沒有missing data的列,但是這樣有可能使得drop掉的列過多而影響後續操作,是以在這種情況下我們可以使用“.fillna()”指令,進而使得NaN資料變成我們設計的新資料。

在完成上述資料的處理之後,我們可以使用assert檢驗我們對資料的處理是否成功,如果資料沒有按照我們标注的标準生成,assert會生成錯誤提醒(注意看兩行代碼中的注釋部分):

# Use the pd.notnull() function on ebola (or the .notnull() method of ebola) and chain two .all() methods (that is, .all().all()). The first .all() method will return a True or False for each column, while the second .all() method will return a single True or False
assert ebola.notnull().all().all()

# Chain two all() methods to the Boolean condition (ebola >= 0).
assert (ebola >= 0).all().all()
           
Case study

熟悉了本章所有知識點之後我們就可以進行整體的case study,其中有一道題很有意思,可以通過代碼熟悉題目:

def check_null_or_valid(row_data):
    """Function that takes a row of data,
    drops all missing values,
    and checks if all remaining values are greater than or equal to 0
    """
    no_na = row_data.dropna()[1:-1]
    numeric = pd.to_numeric(no_na)
    ge0 = numeric >= 0
    return ge0

# Check whether the first column is 'Life expectancy'
assert g1800s.columns[0] == 'Life expectancy'

# Check whether the values in the row are valid
assert g1800s.iloc[:, 1:].apply(check_null_or_valid, axis=1).all().all()

# Check that there is only one instance of each country
assert g1800s['Life expectancy'].value_counts()[0] == 1
           

另一個有趣的例子,讓我重新對melt指令有了認識:

# Melt gapminder: gapminder_melt
gapminder_melt = pd.melt(gapminder, id_vars='Life expectancy')

# Rename the columns
gapminder_melt.columns = ['country', 'year', 'life_expectancy']

# Print the head of gapminder_melt
print(gapminder_melt.head())
           

下面的這段代碼需要了解一下,包括字元部分、inverse部分和“.loc[]”部分:

# Create the series of countries: countries
countries = gapminder['country']

# Drop all the duplicates from countries
countries = countries.drop_duplicates()

# Write the regular expression: pattern
pattern = '^[A-Za-z\.\s]*$'

# Create the Boolean vector: mask
mask = countries.str.contains(pattern)

# Invert the mask: mask_inverse
mask_inverse = ~mask

# Subset countries using mask_inverse: invalid_countries
invalid_countries = countries.loc[mask_inverse]

# Print invalid_countries
print(invalid_countries)
           

最後我附上一個比較繁瑣的例子,其中第二行将life_expectancy列生成了hist圖,第三行中将gapminder中的year列和取完平均數的life_expectancy列通過“.groupby()”進行了合并,以及最後一行将兩個DataFrame儲存成了csv檔案:

# Add first subplot
plt.subplot(2, 1, 1) 

# Create a histogram of life_expectancy
gapminder.life_expectancy.plot(kind='hist')

# Group gapminder: gapminder_agg
gapminder_agg = gapminder.groupby('year')['life_expectancy'].mean()

# Print the head of gapminder_agg
print(gapminder_agg.head())

# Print the tail of gapminder_agg
print(gapminder_agg.tail())

# Add second subplot
plt.subplot(2, 1, 2)

# Create a line plot of life expectancy per year
gapminder_agg.plot()

# Add title and specify axis labels
plt.title('Life expectancy over the years')
plt.ylabel('Life expectancy')
plt.xlabel('Year')

# Display the plots
plt.tight_layout()
plt.show()

# Save both DataFrames to csv files
gapminder.to_csv('gapminder.csv')
gapminder_agg.to_csv('gapminder_agg.csv')