美国婴儿名字分析-不要再用英语教材里面的英文名了!!!

这篇文章主要是介绍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];
end
NamesData.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}];
end
NamesData.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))))];
end
HotNamesData = 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, 男孩名字top100
HotNamesData_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);

来源:知乎 www.zhihu.com

作者:知乎用户(登录查看详情)

【知乎日报】千万用户的选择,做朋友圈里的新鲜事分享大牛。
点击下载