資料科學的原理與技巧 一、資料科學的生命週期
一、資料科學的生命週期
原文:DS-100/textbook/notebooks/ch01
譯者:飛龍
自豪地採用谷歌翻譯
在資料科學中,我們使用大量不同的資料集來對世界做出結論。在這個課程中,我們將通過計算和推理思維的雙重視角,來討論資料科學的關鍵原理和技術。實際上,這涉及以下過程:
- 提出一個問題
- 獲取和清理資料
- 進行探索性資料分析
- 用預測和推理得出結論
在這個過程的最後一步之後,通常出現更多的問題,因此我們可以反覆地執行這個過程,來發現我們的世界的新特徵。這個正反饋的迴圈對我們的工作至關重要,我們稱之為資料科學生命週期。
如果資料科學的生命週期與它說的一樣容易進行,那麼就不需要該主題的教科書了。幸運的是,生命週期中的每個步驟都包含眾多挑戰,這些挑戰揭示了強大和通常令人驚訝的見解,它們構成了使用資料在思考後進行決策的基礎。
和 Data8 一樣,我們將以一個例子開始。
譯者注:Data8 是 DS100 是先修課。我之前翻譯了它的課本,《計算與推斷思維 中文版》。
關於本書
在我們繼續之前,重要的是說出我們對讀者的假設。
在本書中,我們將當作你已經上完了 Data8 或者其他一些類似的東西。 特別是,我們假定你對以下主題有一定了解(同時給出 Data8 課本的頁面連結)。
另外,我們假設你已經上完了 CS61A 或者其他類似的東西,因此除了特殊情況外,不會解釋 Python 的語法。
譯者注:CS61A(SICP Python)是電腦科學的第一門課,中文版講義請見《SICP Python 中文版》。
DS100 的學生
回想一下,資料科學生命週期涉及以下大致的步驟:
- 問題表述:
- 我們想知道什麼,或者我們想要解決什麼問題?
- 我們的假設是什麼?
- 我們的成功指標是什麼?
- 資料採集和清洗:
- 我們有什麼資料以及需要哪些資料?
- 我們將如何收集更多資料?
- 我們如何組織資料來分析?
- 探索性資料分析:
- 我們是否有了相關資料?
- 資料有哪些偏差,異常或其他問題?
- 我們如何轉換資料來實現有效的分析?
- 預測和推斷:
- 這些資料說了世界的什麼事情?
- 它回答我們的問題,還是準確地解決問題?
- 我們的結論有多健壯?
問題表述
我們想知道 DS100 中的學生姓名的資料,是否向我們提供了學生本身的其他資訊。 雖然這是一個模糊的問題,但這足以讓我們處理我們的資料,我們當然可以在問題變得更加精確的時候提出問題。
資料採集和清洗
在 DS100 中,我們將研究收集資料的各種方法。
我們首先看看我們的資料,這是我們從以前的 DS100 課程中下載的學生姓名的名單。
如果你現在不瞭解程式碼,請不要擔心;我們稍後會更深入地介紹這些庫。 相反,請關注我們展示的流程和圖表。
import pandas as pd
students = pd.read_csv('roster.csv')
students
Name | Role | |
---|---|---|
0 | Keeley | Student |
1 | John | Student |
2 | BRYAN | Student |
... | ... | ... |
276 | Ernesto | Waitlist Student |
277 | Athan | Waitlist Student |
278 | Michael | Waitlist Student |
279 行 × 2 列
我們很快可以看到,資料中有一些奇怪的東西。 例如,其中一個學生的姓名全部是大寫字母。 另外,Role
列的作用並不明顯。
在 DS100 中,我們將研究如何識別資料中的異常並執行修正。 大寫字母的差異將導致我們的程式認為'BRYAN'
和'Bryan'
是不同的名稱,但他們對於我們的目標是相同的。 我們將所有名稱轉換為小寫來避免這種情況。
students['Name'] = students['Name'].str.lower()
students
Name | Role | |
---|---|---|
0 | keeley | Student |
1 | john | Student |
2 | bryan | Student |
... | ... | ... |
276 | ernesto | Waitlist Student |
277 | athan | Waitlist Student |
278 | michael | Waitlist Student |
279 行 × 2 列
現在我們的資料有了更容易處理的格式,我們繼續進行探索性資料分析。
探索性資料分析(EDA)
術語探索性資料分析(簡稱 EDA)是指發現我們的資料特徵的過程,這些特徵為未來的分析提供資訊。
這是上一頁的students
表:
students
Name | Role | |
---|---|---|
0 | keeley | Student |
1 | john | Student |
2 | bryan | Student |
... | ... | ... |
276 | ernesto | Waitlist Student |
277 | athan | Waitlist Student |
278 | michael | Waitlist Student |
279 行 × 2 列
我們留下了許多問題。 這個名單中有多少名學生? Role
列是什麼意思? 我們進行 EDA 來更全面地瞭解我們的資料。
在 DS100 中,我們將研究探索性資料分析和實踐,來分析新資料集。
通常,我們通過重複提出簡單問題,他們有關我們想知道的資料,來探索資料。 我們將以這種方式構建我們的分析。
我們的資料集中有多少學生?
print("There are", len(students), "students on the roster.")
# There are 279 students on the roster.
一個自然的後續問題是,這是否是完整的學生名單。 在這種情況下,我們碰巧知道這個列表包含班級中的所有學生。
Role
欄位的含義是什麼?
理解欄位的含義,通常可以通過檢視欄位資料的唯一值來實現:
students['Role'].value_counts().to_frame()
Role | |
---|---|
Student | 237 |
Waitlist Student | 42 |
我們可以在這裡看到,我們的資料不僅包含當時註冊了課程的學生,還包含等候名單上的學生。 Role
列告訴我們每個學生是否註冊。
那名稱呢? 我們如何總結這個欄位?
在 DS100 中,我們將處理許多不同型別的資料(不僅僅是數字),而且我們將研究面向不同型別的資料的技術。
好的起點可能是檢查字串的長度。
sns.distplot(students['Name'].str.len(), rug=True, axlabel="Number of Characters")
# <matplotlib.axes._subplots.AxesSubplot at 0x10e6fd0b8>
這種視覺化向我們展示了,大多數名稱的長度在 3 到 9 個字元之間。 這給了我們一個機會,來檢查我們的資料是否合理 - 如果有很多名稱長度為 1 個字元,我們就有充分的理由重新檢查我們的資料。
名稱裡面有什麼?
雖然這個資料集非常簡單,但我們很快就會看到,僅僅是名稱就可以揭示我們班級的相當多的資訊。
名稱裡面有什麼
到目前為止,我們已經對我們的資料提出了一個大致的問題:“DS100 中的學生名稱是否告訴我們該課程的任何資訊?”
通過將所有名稱轉換為小寫字母,我們完成一些資料清理工作。 在我們的探索性資料分析過程中,我們發現,我們的名單包含班級和候補名單中的大約 270 個學生姓名,而大部分名稱長度在 4 到 8 個字元之間。
根據名稱,我們還能發現班級的什麼其他資訊? 我們可能會考慮資料集中的單個名稱:
students['Name'][5]
# 'jerry'
從這個名稱中我們可以推斷出,這個學生可能是一個男生。我們也可以猜測學生的年齡。例如,如果我們知道,傑裡在 1998 年是一個非常受歡迎的嬰兒名稱,那麼我們可能會猜測這個學生大約二十歲。
這個想法給了我們兩個需要調查的新問題:
- “DS100 中的學生名稱,是否告訴了我們課堂上的性別分佈?”
- “DS100 中的第一批學生,是否告訴了我們課堂上的年齡分佈?”
為了調查這些問題,我們需要一個資料集,它將姓名與性別和年份相關聯。方便的是,美國社會保障部門線上提供這樣一個資料集:https://www.ssa.gov/oact/babynames/index.html。他們的資料集記錄了嬰兒出生時的名稱,因此通常稱為嬰兒名稱資料集。
我們將從下載開始,然後將資料集載入到 Python 中。再次,不要擔心理解第一章中的程式碼。理解整個過程更重要。
import urllib.request
import os.path
data_url = "https://www.ssa.gov/oact/babynames/names.zip"
local_filename = "babynames.zip"
if not os.path.exists(local_filename): # if the data exists don't download again
with urllib.request.urlopen(data_url) as resp, open(local_filename, 'wb') as f:
f.write(resp.read())
import zipfile
babynames = []
with zipfile.ZipFile(local_filename, "r") as zf:
data_files = [f for f in zf.filelist if f.filename[-3:] == "txt"]
def extract_year_from_filename(fn):
return int(fn[3:7])
for f in data_files:
year = extract_year_from_filename(f.filename)
with zf.open(f) as fp:
df = pd.read_csv(fp, names=["Name", "Sex", "Count"])
df["Year"] = year
babynames.append(df)
babynames = pd.concat(babynames)
babynames
Name | Sex | Count | Year | |
---|---|---|---|---|
0 | Mary | F | 9217 | 1884 |
1 | Anna | F | 3860 | 1884 |
2 | Emma | F | 2587 | 1884 |
... | ... | ... | ... | ... |
2081 | Verna | M | 5 | 1883 |
2082 | Winnie | M | 5 | 1883 |
2083 | Winthrop | M | 5 | 1883 |
1891894 行 × 4 列
ls -alh babynames.csv
# -rw-r--r-- 1 sam staff 30M Jan 22 15:31 babynames.csv
看起來,資料集包含名稱,嬰兒性別,具有該名稱的嬰兒數量以及這些嬰兒的出生年份。 為了確認,我們從檢查來自 SSN 的資料集描述:https://www.ssa.gov/oact/babynames/background.html。
所有名稱均來自 1879 年後美國出生人口的社保卡申請。請注意,很多 1937 年以前出生的人從未申請過社保卡,所以他們的名字不包含在我們的資料中。 對於其他申請人,我們的記錄可能不會顯示出生地點,並且他們的姓名也不會包含在我們的資料中。
所有資料均來自截至我們的 2017 年 3 月社保卡申請記錄的 100% 樣本。
這個資料的一個有用的視覺化,是繪製每年出生的男性和女性嬰兒的數量:
pivot_year_name_count = pd.pivot_table(
babynames, index='Year', columns='Sex',
values='Count', aggfunc=np.sum)
pink_blue = ["#E188DB", "#334FFF"]
with sns.color_palette(sns.color_palette(pink_blue)):
pivot_year_name_count.plot(marker=".")
plt.title("Registered Names vs Year Stratified by Sex")
plt.ylabel('Names Registered that Year')
這個繪圖讓我們質疑,1880 年的美國是否有嬰兒。上面引用的一句話有助於解釋:
請注意,很多 1937 年以前出生的人從未申請過社保卡,所以他們的名字不包含在我們的資料中。 對於其他申請人,我們的記錄可能不會顯示出生地點,並且他們的姓名也不會包含在我們的資料中。
我們還可以在上圖中清楚地看到嬰兒潮的時期。
從名字推斷性別
我們使用這個資料集來估計我們班的男女生人數。 與我們班的名單一樣,我們先將名稱小寫:
babynames['Name'] = babynames['Name'].str.lower()
babynames
Name | Sex | Count | Year | |
---|---|---|---|---|
0 | mary | F | 9217 | 1884 |
1 | anna | F | 3860 | 1884 |
2 | emma | F | 2587 | 1884 |
... | ... | ... | ... | ... |
2081 | verna | M | 5 | 1883 |
2082 | winnie | M | 5 | 1883 |
2083 | winthrop | M | 5 | 1883 |
1891894 行 × 4 列
然後,我們計算對於每個名字,共有多少個男嬰和女嬰出生:
sex_counts = pd.pivot_table(babynames, index='Name', columns='Sex', values='Count',
aggfunc='sum', fill_value=0., margins=True)
sex_counts
Sex | F | M | All |
---|---|---|---|
Name | |||
aaban | 0 | 96 | 96 |
aabha | 35 | 0 | 35 |
aabid | 0 | 10 | 10 |
... | ... | ... | ... |
zyyon | 0 | 6 | 6 |
zzyzx | 0 | 5 | 5 |
All | 170639571 | 173894326 | 344533897 |
96175 行 × 3 列
為了決定一個名字是男性還是女性,我們可以計算出這個名字給女性嬰兒的次數比例。
prop_female = sex_counts['F'] / sex_counts['All']
sex_counts['prop_female'] = prop_female
sex_counts
Sex | F | M | All | prop_female |
---|---|---|---|---|
Name | ||||
aaban | 0 | 96 | 96 | 0.000000 |
aabha | 35 | 0 | 35 | 1.000000 |
aabid | 0 | 10 | 10 | 0.000000 |
... | ... | ... | ... | ... |
zyyon | 0 | 6 | 6 | 0.000000 |
zzyzx | 0 | 5 | 5 | 0.000000 |
All | 170639571 | 173894326 | 344533897 | 0.495277 |
96175 行 × 4 列
然後,我們可以定義一個函式,查詢給定名稱的女性比例。
def sex_from_name(name):
if name in sex_counts.index:
prop = sex_counts.loc[name, 'prop_female']
return 'F' if prop > 0.5 else 'M'
else:
return None
sex_from_name('sam')
# 'M'
嘗試在這個框中輸入一些名稱,來檢視這個函式是否輸出你期望的內容:
interact(sex_from_name, name='sam');
我們在班級名單中,使用最可能的性別標記每個名稱。
students['sex'] = students['Name'].apply(sex_from_name)
students
Name | Role | sex | |
---|---|---|---|
0 | keeley | Student | F |
1 | john | Student | M |
2 | bryan | Student | M |
... | ... | ... | ... |
276 | ernesto | Waitlist Student | M |
277 | athan | Waitlist Student | M |
278 | michael | Waitlist Student | M |
279 行 × 3 列
現在,估計我們有多少男女學生就很容易了:
students['sex'].value_counts()
'''
M 144
F 92
Name: sex, dtype: int64
'''
從名稱推斷年齡
我們可以採用類似的方法來估計班級的年齡分佈,將每個姓名對映到資料集中的平均年齡。
def avg_year(group):
return np.average(group['Year'], weights=group['Count'])
avg_years = (
babynames
.groupby('Name')
.apply(avg_year)
.rename('avg_year')
.to_frame()
)
avg_years
avg_year | |
---|---|
Name | |
aaban | 2012.572917 |
aabha | 2013.714286 |
aabid | 2009.500000 |
... | ... |
zyyanna | 2010.000000 |
zyyon | 2014.000000 |
zzyzx | 2010.000000 |
96174 行 × 1 列
def year_from_name(name):
return (avg_years.loc[name, 'avg_year']
if name in avg_years.index
else None)
# Generate input box for you to try some names out:
interact(year_from_name, name='fernando');
students['year'] = students['Name'].apply(year_from_name)
students
Name | Role | sex | year | |
---|---|---|---|---|
0 | keeley | Student | F | 1998.147952 |
1 | john | Student | M | 1951.084937 |
2 | bryan | Student | M | 1983.565113 |
... | ... | ... | ... | ... |
276 | ernesto | Waitlist Student | M | 1981.439873 |
277 | athan | Waitlist Student | M | 2004.397863 |
278 | michael | Waitlist Student | M | 1971.179231 |
279 行 × 4 列
之後,繪製年份的分佈情況很容易:
sns.distplot(students['year'].dropna());
為了計算平均年份:
students['year'].mean()
# 1983.846741800525
這使得它看起來像是,學生平均是 35 歲。 這是一個大學本科課程,所以我們預計平均年齡在 20 歲左右。為什麼我們的估計會如此之遠?
作為資料科學家,我們經常遇到不符合我們預期的結果,並且必須做出判斷,我們的結果是由我們的資料,我們的流程還是不正確的假設造成的。 不可能定義適用於所有情況的規則。 相反,我們將為你提供工具來重新檢查資料分析的每一步,並告訴你如何使用它們。
在這種情況下,我們意想不到的結果,最可能是因為大多數名字都是舊的。 例如,在我們的資料記錄中,約翰這個名字在整個歷史中都相當流行,這意味著我們可能會猜測約翰出生於 1950 年左右。我們可以通過檢視資料來確認:
names = babynames.set_index('Name').sort_values('Year')
john = names.loc['john']
john[john['Sex'] == 'M'].plot('Year', 'Count');
如果我們相信,我們班沒有人超過 40 歲或低於 10 歲(我們可以通過在課上觀察我們的教室發現),我們可以通過僅檢查 1978 年之間的資料,將其納入我們的分析中。我們將很快討論資料操作,並且你可能會重新分析這個示例,來確定納入這一先驗是否會提供更明智的結果。
相關文章
- 資料科學的原理與技巧二、資料生成資料科學
- 大資料與生命科學大資料
- 人的資料科學與機器資料科學資料科學
- Python資料科學(一) python與資料科學應用(Ⅰ)Python資料科學
- 資料科學家的命令列技巧資料科學命令列
- 讓科學重回資料科學資料科學
- Python資料科學(三) python與資料科學應用(Ⅲ)Python資料科學
- Python資料科學(二) python與資料科學應用(Ⅱ)Python資料科學
- 資料科學資料科學
- 資料科學入門 (一) —— 資料資料科學
- 【資料科學家】如何成為一名資料科學家?資料科學
- 資料科學的整合與細分資料科學
- 著陸資料科學工作的8個技巧!資料科學
- 2022年學習資料科學的技巧:資料科學
- 做資料科學領域的「召喚師」,組織一場人人可參與的資料科學比賽資料科學
- 資料科學求職建議:掌握5種型別的資料科學專案資料科學求職型別
- 資料科學的基本內容資料科學
- 通往資料科學之路資料科學
- 資料分析與資料探勘 - 04科學計算
- 資料科學、資料工程學習路線資料科學
- 科學研究與大資料概念的濫用大資料
- 你與資料科學家只差這26條python技巧資料科學Python
- 公民資料科學家的侷限性資料科學
- 足球比賽中的資料科學資料科學
- 資料科學中的 Spark 入門資料科學Spark
- 初創公司資料科學專案全流程指南,一位資深資料科學家的經驗談資料科學
- Python資料科學(七) 資料清理(Ⅱ)Python資料科學
- Python資料科學(六) 資料清理(Ⅰ)Python資料科學
- 使用知識圖解開生命科學資料挑戰圖解
- 什麼是資料科學?資料科學
- 大資料:商業革命與科學革命大資料
- Python資料科學(八)- 資料探索與資料視覺化Python資料科學視覺化
- 學習資料科學的五大免費資源資料科學
- 資料科學家需要的基礎技能資料科學
- 資料科學、機器學習和AI的區別資料科學機器學習AI
- 資料科學家的15項原則資料科學
- Python資料科學(四) 資料收集系列Python資料科學
- 資料科學50年,資料科學家是否依然是21世紀最性感的職業?資料科學