美國嬰兒名字分析-不要再用英語教材裡面的英文名了!!!
來自專欄 MATLAB
這篇文章主要是介紹live script和table相關函數的使用, 還不涉及到機器學習.
記者: live script+table給你什麼樣的感覺? 相比比較流行的python數據工具鏈呢?
菡姐: live script + table給我的感覺呢?感覺比jupyter notebook + pandas好多了!MATLAB有workspace, jupyter notebook沒有, 就好比汽車沒有碼速表一樣!spyder也有workspace, 但是它不能顯示所有的數據類型, 而且數據量一大, 容易卡死.
哎呦, 我超喜歡玩MATLAB的!
我之前寫過:
菡姐:MATLAB的數據分析工具鏈菡姐:使用MATLAB數據分析工具鏈進行實戰: 看電影真的是男女有別現在就用上面提到的數據分析工具鏈進行實戰.
使用的例子是《利用Python進行數據分析》 麥金尼 (Wes McKinney), 唐學韜, 等【摘要 書評 試讀】圖書 裡面第二章的第三個例子.
(沒看過這本書的讀者不需要買這本書, 除非你想用python進行數據分析)
之所以使用這個例子, 是因為:
1 我對這個例子比較熟悉, 之前學pandas時, 好好研究過這個例子.
2 網上能夠下載到數據.
3 可以做對照, 確保我用MATLAB做出的結果與用pandas做出的結果是一致的, 防止出錯.
這個例子所需要的數據可以在這下載:
wesm/pydata-book
OK, 正式開始了.
文章比較長, 我先說一下大概的內容吧:
1 分析了一下英文教材裡面的常見名的歷史流行度, 發現這些名字以前很流行, 現在已經冷門了.
2 名字多樣性逐漸提高了, 而且女孩的多樣性明顯高於男孩的.
3 名字的尾字母的流行度也在發生變化.
4 發現了一組"變性"的名字!!!
先給live script的截圖, 方便讀者直接看到運行結果
再給轉化為普通script的程序, 方便讀者在自己電腦上運行.
數據下載完後, 可以直接運行了, 使用R2018a以上的版本(groupsummary是R2018a的新函數, 如果改成findgroup+splitapply的話, 更老一些的版本應該也能運行).
%% 美國嬰兒名字分析-不要再用英語教材裡面的英文名了!!!%% 合併數據(1880年至2010年)% 一個文件只存一年的嬰兒名字的統計數據.% % 如圖所示:% % % % 每個文件裡面的格式是這樣的:% % % % 第一列是嬰兒的名字, 第二列是嬰兒的性別, 第三列是那一年該性別, 起該名字的嬰兒的數量. %%years_set = 1880:2010;filename = sprintf(ch02\names\yob%d.txt, years_set(1));NamesData = readtable(filename);NamesData.Properties.VariableNames = {name, sex, births};NamesData.year = years_set(1)*ones(height(NamesData), 1);for ii = 2:length(years_set) filename = sprintf(ch02\names\yob%d.txt, years_set(ii)); piece = readtable(filename); piece.Properties.VariableNames = {name, sex, births}; piece.year = years_set(ii)*ones(height(piece), 1); NamesData = [NamesData;piece];endNamesData.sex = categorical(NamesData.sex);summary(NamesData);%% % 發現了一些有趣的點: % % 女性的名字多樣性高於男性(100萬:69萬), 注意這裡面名字沒有根據年份去重, 但不影響結論.head(NamesData)tail(NamesData)%% % 查看了一下合併數據的頭部與尾部, 大致確認全部都合併了進去.%% 各個年份的出生人數(按照性別分組)%%total_births = groupsummary(NamesData, {year, sex}, sum, births)girl_births = total_births.sum_births(total_births.sex == F);boy_births = total_births.sum_births(total_births.sex == M);figure;plot(years_set, girl_births);hold on;plot(years_set, boy_births);legend(女孩, 男孩, Location, best)xlabel(年份);ylabel(人數);%% % 可以看出, 總體趨勢上, 美國出生人數是逐年增加的.% % 題外話, 可以看出, 男女比例也在發生巨大的變化, 但這不是本文的研究對象.%% 計算佔比% 上一節分析出, 總體趨勢上, 美國出生人數是逐年增加的, 因此, 簡單的根據人數來判斷是否熱門是不合理的, 佔比這個指標更合理一些.%%G = findgroups(NamesData.year, NamesData.sex);prop_cell = splitapply(@(x) {x./sum(x)}, NamesData.births, G);prop = zeros(0, 1);for ii = 1:length(prop_cell) prop = [prop;prop_cell{ii}];endNamesData.prop = prop;head(NamesData)%% % 添加了佔比這個變數, 表示: 該性別該年份該名字的人數/(該性別該年份的總人數)%% 過濾掉冷門名字%%length(unique(NamesData.name))%% % 1880年到2010年, 總共出現過88496個名字!% % 實在是太多了.% % 為了方便計算, 過濾掉冷門名字, 保留熱門名字.% % 熱門名字的具體定義為: 該年份, 該性別人數排名前1000名的名字.% % (如果該年份該性別的名字種類小於1000個, 那麼全體都是熱門名字)idx_hot = zeros(0, 1);for ii = 1:length(unique(G)) idx = find(G == ii); births = NamesData.births(idx); [~, I] = sort(births, descend); idx_hot = [idx_hot;idx(I(1:min(1000, length(I))))];endHotNamesData = NamesData(idx_hot, :);height(HotNamesData)length(unique(G))*1000%% % 最後行數略小於理論上限, 說明確實存在特殊情況(如果該年份該性別的名字種類小於1000個, 那麼全體都是熱門名字)head(HotNamesData)tail(HotNamesData)%% 來看看那些過時的名字(比如經常出現在英語教材裡面的名字, 男生: Bill, Bob, John, Tom, 女生: Ann, Mary, Jane, Rose)%%births_year_name = groupsummary(HotNamesData, {year, name}, sum, births);births_year_name.GroupCount = []; % 必須加上這句, 之前debug了不少時間:(births_year_name = unstack(births_year_name, sum_births, name);figure;plot(births_year_name.year, births_year_name.Bill);figure;plot(births_year_name.year, births_year_name.Bob);figure;plot(births_year_name.year, births_year_name.John);figure;plot(births_year_name.year, births_year_name.Tom);figure;plot(births_year_name.year, births_year_name.Ann);figure;plot(births_year_name.year, births_year_name.Mary);figure;plot(births_year_name.year, births_year_name.Jane);figure;plot(births_year_name.year, births_year_name.Rose);%% % 可以看出, 很多經常出現在我們英語教材裡面的英文名已經過時了% % 所以, 給自己起英文名的時候, 盡量規避這些名字, 要不然美國人想: 這年輕人怎麼起了個爺爺奶奶級的名字?%% 名字多樣性分析%%total_prop = groupsummary(HotNamesData, {year, sex}, sum, prop);total_prop_girl = total_prop(total_prop.sex == F, {year, sum_prop});total_prop_boy = total_prop(total_prop.sex == M, {year, sum_prop});figure;plot(total_prop_girl.year, total_prop_girl.sum_prop);hold on;plot(total_prop_boy.year, total_prop_boy.sum_prop);legend(女孩, 男孩, Location, best);xlabel(年份);title(前1000名佔比總和)%% % 可以看到1880年時, 前1000名已經能全覆蓋了, 而到了2010年, 前1000名只能覆蓋73%(女生), 84%(男生).% % 說明名字的多樣性在增加, 女生比男生更多樣.G = findgroups(HotNamesData.year, HotNamesData.sex);diversity = splitapply(@(x) find(cumsum(sort(x, descend)) > 0.5, 1), HotNamesData.prop, G);diversity_girl = diversity(1:2:end);diversity_boy = diversity(2:2:end);figure;plot(unique(HotNamesData.year), diversity_girl);hold on;plot(unique(HotNamesData.year), diversity_boy);legend(女孩, 男孩, Location, best);xlabel(年份);title(前多少個名字佔比50%?)%% % 這張圖可以得到相同的結論: 名字的多樣性在增加, 女生比男生更多樣.%% 尾字母的演化%%NamesData.lastletter = cellfun(@(x) x(end), NamesData.name);births_stats = groupsummary(NamesData, {lastletter, sex, year}, sum, births);births_stats = births_stats(ismember(births_stats.year, [1910, 1960, 2010]), :);births_stats = removevars(births_stats, GroupCount);births_stats = unstack(births_stats, sum_births, sex);births_stats_girl = births_stats(:, [1, 2, 3]);births_stats_boy = births_stats(:, [1, 2, 4]);births_stats_girl = unstack(births_stats_girl, F, year);births_stats_boy = unstack(births_stats_boy, M, year);letters = births_stats_girl.lastletter;letters = categorical(cellstr(letters));births_stats_girl = births_stats_girl{:, 2:end};births_stats_boy = births_stats_boy{:, 2:end};births_stats_girl(isnan(births_stats_girl)) = 0;births_stats_boy(isnan(births_stats_boy)) = 0;size(births_stats_girl)size(births_stats_boy)%% % 26行3列, 說明整理對了, 26個字母, 3個年份的數據figure;bar(letters, bsxfun(@rdivide, births_stats_girl, sum(births_stats_girl)));legend(1910, 1960, 2010, Location, best);xlabel(名字的最後一個字母);ylabel(佔比);title(女孩);figure;bar(letters, bsxfun(@rdivide, births_stats_boy, sum(births_stats_boy)));legend(1910, 1960, 2010, Location, best);xlabel(名字的最後一個字母);ylabel(佔比);title(男孩);%% % 可以發現:% % 女孩名字的最後一個字母, a越來越多了, e越來越少了.% % 男孩名字的最後一個字母, n越來越多了.%% "變性"的名字(L|esl開頭的名字)|% L|esl開頭的名字有: Leslie, Lesley, Leslee, Lesli, Lesly|)....% % 這些名字逐漸女性化了.%%names = string(HotNamesData.name);idx_Lesl = startsWith(names, Lesl);HotNamesData_Lesl = HotNamesData(idx_Lesl, :);groupsummary(HotNamesData_Lesl, name, sum, births)%% % 其中叫"Leslie"的人最多.Lesl_year_sex = groupsummary(HotNamesData_Lesl, {year, sex}, sum, births);Lesl_year_sex = removevars(Lesl_year_sex, GroupCount);Lesl_year_sex = unstack(Lesl_year_sex, sum_births, sex);Lesl_year_sex.total = Lesl_year_sex.F + Lesl_year_sex.M;Lesl_year_sex.F_ratio = Lesl_year_sex.F./Lesl_year_sex.total;Lesl_year_sex.M_ratio = Lesl_year_sex.M./Lesl_year_sex.total;figure;plot(Lesl_year_sex.year, Lesl_year_sex.F_ratio);hold on;plot(Lesl_year_sex.year, Lesl_year_sex.M_ratio);legend(女孩, 男孩, Location, best);xlabel(年份);title(Lesl開頭名字的性別佔比)%% % 可以看到, 之前是男性化的名字, 逐漸變成女性化的名字!!!% %
如果有幫助的話, 讚賞一下吧. 花了一個下午做出來的.
----更新分割線--------------------------------------------------------------------------
有人對top100的名字有興趣, 我將2010年的流行名字進行了提取,並且畫成了詞雲.
女孩名字top100:
男孩名字top100:
代碼如下, 在上面代碼的下面補充上即可:
%% 女孩名字top100, 男孩名字top100HotNamesData_2010 = HotNamesData(HotNamesData.year == 2010, :);HotNamesData_2010_girl = HotNamesData_2010(HotNamesData_2010.sex == F, :);HotNamesData_2010_boy = HotNamesData_2010(HotNamesData_2010.sex == M, :);[~, idx_girl] = sort(HotNamesData_2010_girl.births, descend);top100_2010_girl = HotNamesData_2010_girl(idx_girl(1:100), :);[~, idx_boy] = sort(HotNamesData_2010_boy.births, descend);top100_2010_boy = HotNamesData_2010_boy(idx_boy(1:100), :);figure;wordcloud(top100_2010_girl.name, top100_2010_girl.births);figure;wordcloud(top100_2010_boy.name, top100_2010_boy.births);
推薦閱讀:
※【IoT:從零開始的MATLAB學習】 2 偏好設置&幫助文檔
※MATLAB Graph Object(3): 展示關係網
※圖像處理與特徵提取 —— 從 MATLAB 到 Python(一)圖像、矩陣與數據的讀寫
※如何算是精通MATLAB?
※%(default) colormap(parula)