使用分类数据创建景观并进行无监督分析以获得更深入的洞见的教程
数据科学领域正在不断发展,新的角色和功能不断涌现。传统的数据科学角色正在演变为十多个新角色,如数据工程师、ML工程师、产品数据分析师、研究科学家、云数据工程师等等。在这篇博客中,我们将加载数据科学薪资数据集,并创建一个景观,以检查角色如何基于位置、远程工作、职称、经验水平以及与薪资的关系相互关联。我将演示创建这样一个景观的步骤以及如何使用无监督聚类来深入分析样本。聚类分析使用库clusteval进行,而散点图使用scatterd和d3blocks创建交互式图。
介绍
数据科学是当今数字领域中变化最快的领域之一。数据科学家的角色是解决复杂问题并推动知情决策。基础知识包括统计技术、机器学习算法和数据可视化。但是,在创建产品方面,还需要深入了解工程知识以及数据治理、伦理和隐私等方面的知识。作为数据科学家所需的专业知识取决于具体的角色描述和业务等方面。现在有许多新的角色,如数据工程师、ML工程师、产品数据分析师、研究科学家、云数据工程师等等。每个角色也随着经验水平的不同而发生变化。入门级职位通常需要在高级数据科学家的指导下工作,协助数据预处理并为模型开发做出贡献。随着职业发展,中级角色需要更深入地了解统计分析、特征工程和算法选择。高级数据科学家通常领导项目,指导初级团队成员,并为战略规划做出贡献。
数据科学领域的薪资因经验、行业、国家和高级资格而异。多年来,地点在数据科学机会和薪资中发挥了非常重要的作用。像硅谷、纽约市和旧金山这样的技术中心提供高薪职位。然而,在过去的几年中,从不同的全球位置工作变得更加容易和被接受。让我们看看是否可以使用数据科学薪资数据集来确认其中一些趋势。
如果您发现本文有所帮助,欢迎关注我的更多类似主题的文章。如果您正在考虑成为小猪AI的会员,可以使用我的推荐链接来支持我的工作。价格与一杯咖啡相同,但这样可以让您每个月阅读无限的文章!
分析所使用的库。
我们将使用七个库来帮助加载数据集、执行预处理步骤、分析数据、创建可视化和创建数据科学景观。如果您想深入了解细节,我建议阅读下面的博客。
#轻松导入数据集pip install datazets#独热编码pip install df2onehot#PCA分析。创建可解释的双图pca#具有自动评估的聚类pip install clusteval#创建漂亮的散点图pip install scatterd#创建漂亮的交互式散点图pip install d3blocks# HNET用于聚类标签的关联分析pip install hnet
PCA负载是什么以及如何有效使用Biplots?
获取主成分分析最大收益的实用指南
towardsdatascience.com
从数据到聚类;你的聚类何时足够好?
聚类方法可以找到合理的聚类和隐藏的宝石,但你需要正确的聚类评估…
towardsdatascience.com
从聚类到洞察;下一步
学习如何定量检测哪些特征推动了聚类的形成
towardsdatascience.com
通过使用D3js和Python使您的散点图最大化互动
散点图非常有用,特别是当它们具有缩放和刷选功能时。
towardsdatascience.com
使用显著关联的网络探索和理解数据
探索以理解您的数据可以使成功或项目失败之间的差异!
towardsdatascience.com
数据科学薪资数据集
数据科学薪资数据集来源于ai-jobs.net [1],也作为Kaggle比赛 [2]开放。该数据集包含4134个样本的11个特征。这些样本来自全球,并每周更新,时间从2020年到现在(可能是2023年年初)。数据集已发布在公共领域中,可以免费使用。让我们加载数据并查看变量。
# 导入库import datazets as dz# 获取数据科学薪资数据集df = dz.get('ds_salaries.zip')# 特征如下df.columns# 'work_year' > 支付薪资的年份。# 'experience_level' > 在工作期间的经验水平。# 'employment_type' > 就业类型:兼职、全职、合同或自由职业者。# 'job_title' > 角色名称。# 'salary' > 支付的总毛薪资金额。# 'salary_currency' > 支付薪资的货币(ISO 4217代码)。# 'salary_in_usd' > 转换后的美元薪资。# 'employee_residence' > 居住地的主要国家。# 'remote_ratio' > 远程工作:少于20%、部分、超过80%# 'company_location' > 雇主主要办事处所在国家。# 'company_size' > 公司在该年份工作的人数的平均值。# 仅选择欧洲国家# 欧洲国家 = ['SM', 'DE', 'GB', 'ES', 'FR', 'RU', 'IT', 'NL', 'CH', 'CF', 'FI', 'UA', 'IE', 'GR', 'MK', 'RO', 'AL', 'LT', 'BA', 'LV', 'EE', 'AM', 'HR', 'SI', 'PT', 'HU', 'AT', 'SK', 'CZ', 'DK', 'BE', 'MD', 'MT']# df['europe'] = np.isin(df['company_location'], 欧洲国家)
图1显示了前两个工作职称的摘要以及薪资分布情况。前两个面板是全球的,而后两个面板仅适用于欧洲。尽管这些图表很有信息量,但它们显示了平均值,而不知道位置、经验水平、远程工作、国家等在特定环境中的相关性。例如:在小公司远程工作的初级数据工程师的薪资与具有其他属性的经验丰富的数据工程师是否更相似?此类问题可以通过下面部分中所示的分析更好地回答。
数据预处理
数据科学薪资数据集是一个混合数据集,包含连续和分类变量。我们将执行无监督分析并创建数据科学领域。但在进行任何预处理之前,我们需要删除冗余特征,例如 salary_currency
和 salary
,以防止多重共线性问题。此外,我们将从数据集中排除变量 salary_in_usd
并将其存储为目标变量 y
,因为我们不希望由于薪资本身而发生分组。基于聚类,我们可以调查是否可以将任何检测到的分组与薪资相关联。清理后的数据集结果具有相同的8个特征和4134个样本。
# 将薪资存储在单独的目标变量中.y = df ['salary_in_usd']# 删除冗余变量df.drop(labels=['salary_currency', 'salary', 'salary_in_usd'], inplace=True, axis=1)# 使分类变量更易于理解。df['experience_level'] = df['experience_level'].replace({'EN':'Entry-level', 'MI':'Junior Mid-level', 'SE':'Intermediate Senior-level', 'EX':'Expert Executive-level / Director'}, regex=True)df['employment_type'] = df['employment_type'].replace({'PT':'Part-time', 'FT':'Full-time', 'CT':'Contract', 'FL':'Freelance'}, regex=True)df['company_size'] = df['company_size'].replace({'S':'Small (less than 50)', 'M':'小猪AI (50 to 250)', 'L':'Large (>250)'}, regex=True)df['remote_ratio'] = df['remote_ratio'].replace({0:'No remote', 50:'Partially remote', 100:'>80% remote'}, regex=True)df['work_year'] = df['work_year'].astype(str)df.shape# (4134, 8)
下一步是将所有测量值转换为相同的测量单位。为了做到这一点,我们将小心地执行独热编码,并注意我们可能会引入的多重共线性。换句话说,当我们将任何分类变量转换为多个独热变量时,我们引入了一个偏差,使我们能够基于来自同一分类列的两个或多个特征完美地预测一个特征(即独热编码特征的总和始终为一)。这被称为虚拟陷阱,我们可以通过简单地删除一列来打破线性链来防止它。 df2onehot
包含虚拟陷阱保护功能。这个功能比仅删除每个类别的一个独热列稍微先进一些,因为它仅在由于其他清理操作(例如每个独热特征的最小样本数或删除布尔特征中的 False
状态)仍未打破线性链时才删除一个独热列。
# 导入库from df2onehot import df2onehot# 进行独热编码并删除任何多重共线性,以防止虚拟陷阱。dfhot = df2onehot(df, remove_multicollinearity=True, y_min=5, verbose=4)['onehot']print(dfhot)# work_year_2021 ... company_size_Small (less than 50)# 0 False ... False# 1 False ... False# 2 False ... False# 3 False ... False# 4 False ... False# ... ... ...# 4129 False ... False# 4130 True ... False# 4131 False ... True# 4132 False ... False# 4133 True ... False# [4134 rows x 115 columns]
在我们的情况下,我们将删除包含少于5个样本的独热编码特征(y_min=5
),并删除多重共线性以防止虚拟陷阱(remove_multicollinearity=True
)。这导致相同的4134个样本具有115个独热编码特征。
PCA 分析表明薪资受经验水平、公司规模和位置驱动。
我们将使用 PCA 分析预处理后的数据集,并通过载荷和视觉检查确定样本之间的相关性。请参阅下面的代码段,了解如何执行 PCA 分析。阅读此博客以获取更多详细信息。样本关系与特征的载荷的结果显示在双图中(图2)。请花时间理解它,它包含了大量的信息。首先,这个2D空间中的每个点都是4134个样本中的一个,两个点之间的距离描述了它们基于115个特征的相似性。两个点之间距离越近,它们在某种程度上就越相似。在图的背景中显示了具有红光的密度图。高密度和低密度区域以这种方式突出显示。每个点的大小是薪资,较高的薪资与较大的大小相关。颜色基于 job_title
,标记设置为 experience_level
。
# 导入库from pca import pca# 初始化model = pca(normalize=False)# 使用PCA拟合模型model.fit_transform(dfhot)# 制作双图model.biplot(labels=df['job_title'], s=y/500, marker=df['experience_level'], n_feat=10, density=True, fontsize=0, jitter=0.05, alpha=0.8, color_arrow='#000000', arrowdict={'color_text': '#000000', 'fontsize': 32}, figsize=(40, 30), verbose=4, )
如果我们看一下图2,我们可以看到第1个主成分包含18.9%的解释方差,第2个主成分包含14.7%。这表明前两个成分捕获了数据中大部分解释方差,这使得进一步解释样本关系和负载值变得有价值。载荷(黑色箭头)描述了原始特征中哪些特征负责主成分中的方差。最大的载荷来自类别work_year
,将数据分成两个雪茄形状(从左下向右上),而experience level
和company size
则创建了样本的长尾部分,朝向右上角。
具有较大形状的样本表示较高的薪资,并且似乎聚集在一起(左下角)。左下角的许多样本具有长方形形状,表示高级别的经验水平。我们向右上的尾部移动,点的大小就会变小(相对较少的薪资),并且包含混合形状,表示各种经验水平。为了进行视觉调查,我们可以应用不同的颜色,如图3所示。总体而言,我们可以看到样本按年份聚集,并呈雪茄状形。多年来,经验水平,公司规模和远程工作的模式也出现了。
# 导入库from scatterd import scatterd# 创建具有不同着色的各种散点图。model.scatter(labels=df['company_size'], s=y/500, marker=df['experience_level'], density=True, fontsize=20, jitter=0.05, alpha=0.8, figsize=(40, 30), verbose=4, grid=True, legend=True, )
当我们根据国家对样本进行着色时(图4),欧洲国家主要出现在尾部(灰色)。这些结果表明,薪酬较高的工作主要在欧洲以外,具有高级别的经验水平,并在大公司工作。远程工作似乎没有什么区别。这种趋势似乎在2020年到2023年期间也没有改变。
创建数据科学职位和薪资景观。
为了创建景观,我们将使用t-分布随机邻居嵌入(t-SNE)。在预处理步骤中,删除work_year
列以避免任何年份上的分离(跨年份的模式非常相似)。在t-SNE嵌入后,我们可以使用scatterd
库在2D空间中散布数据点。该库突出显示密集区域(density=True
),并向较少密集的区域提供透明度(gradient=opaque
),从而使散点图整洁。现在,在图5中可以找到该景观。就像在前面的部分中一样,样本按职称着色,标记设置为经验水平,点的大小基于美元的薪资。
# 导入库from scatterd import scatterdfrom sklearn.manifold import TSNE# 从数据框中删除工作年份df.drop(labels=['work_year'], inplace=True, axis=1)# 创建新的无工作年份的独热矩阵dfhot = df2onehot(df, remove_multicollinearity=True, y_min=5, verbose=4)['onehot']# 使用tSNE进行特征嵌入X = TSNE(n_components=2, init='pca', perplexity=100).fit_transform(dfhot.values)# 导入库fig, ax = scatterd(X[:, 0], X[:, 1], marker=df['experience_level'], s=y/500, labels=df['job_title'], fontsize=0, density=True, args_density={'alpha': 0.4}, gradient='opaque', edgecolor='#000000', jitter=1, grid=True, legend=False, figsize=(40, 30), )
驱动要素的检测。
为了确定哪些要素驱动样本的分组,我们需要调查景观中的分组(图5)。我们将分两个部分来解决这个问题,首先进行聚类,然后进行丰富分析。这两个步骤都可以使用clusteval
库来执行。让我们加载该库并开始对数据进行聚类。
聚类方法是DBSCAN
,评估方法是Silhouette score
。在clusteval
中,对DBSCAN的epsilon参数进行了网格搜索,并使用最佳Silhouette得分进行聚类。在我们的情况下,检测到24个簇(从簇0到23)。结果显示在下面的代码部分和图6中,图6显示了DBSCAN的网格搜索优化情况,以及Silhouette系数。有关更多详细信息,建议阅读博客:从数据到簇:何时您的聚类已足够好?
# 导入库from clusteval import clusteval# 初始化clustevalce = clusteval(cluster='dbscan', metric='euclidean', linkage='complete', min_clust=7, normalize=True, verbose='info')# 聚类评估results = ce.fit(X)
对于检测到的簇标签,我们可以使用丰富分析分析驱动簇标签的要素。有关更多详细信息,请阅读从簇到见解;下一步。下面的代码部分描述了哪些要素与每个簇标签显着相关。我们可以手动探索这些结果,但我们也可以制作散点图,将前n个要素叠加在散点图上(图7和图8)。
# 为每个聚类标签计算富集ce.enrichment(df)# 显示与聚类标签显著关联的类别print(ce.results['enrichment'])# category_label P ... category_name Padj# 0 初级水平 8.988604e-31 ... 经验水平 5.954950e-27# 1 初中级 3.818216e-294 ... 经验水平 2.547895e-290# 2 中级高级 5.812236e-51 ... 经验水平 3.857000e-47# 3 初中级 4.519280e-43 ... 经验水平 2.997639e-39# 4 初中级 1.477602e-68 ... 经验水平 9.821622e-65# .. ... ... ... ... ...# 146 小猪AI (50到250) 6.991347e-12 ... 公司规模 4.603802e-08# 147 大型 (>250) 1.424008e-61 ... 公司规模 9.459684e-58# 148 小型 (少于50) 1.487384e-55 ... 公司规模 9.874743e-52# 149 小猪AI (50到250) 4.985496e-22 ... 公司规模 3.296410e-18# 150 小猪AI (50到250) 1.461693e-06 ... 公司规模 9.553627e-03# [151 行 x 11 列]# 使用富集结果创建散点图。ce.scatter(n_feat=4, s=y/500, jitter=0.05, fontsize=14, density=True, params_scatterd={'marker':df['experience_level'], 'gradient':'opaque', 'dpi':200}, figsize=(40,30))# 使用富集结果创建密集区域。ce.scatter(n_feat=4, s=0, jitter=0.05, fontsize=14, density=True, params_scatterd={'marker':df['experience_level'], 'gradient':'opaque', 'dpi':200}, figsize=(40,30))
使用D3Blocks进行交互式可视化。
最后,我们将创建一个交互式散点图,包括2D PCA和t-SNE嵌入。这有助于更好地理解个体样本的分布情况,并可以通过缩放和平移来调查个体样本和/或组。
- 在此处查看全球互动景观。
- 在此处查看欧洲的交互式景观。
# 导入库from scatterd import scatterd, jitter_funcfrom d3blocks import D3Blocks, normalizeimport numpy as np# 初始化3 = D3Blocks()tooltip = []for i in range(0, df.shape[0]): tip = '<br>'.join(list(map(lambda x: x[0].replace('_', ' ').title()+': '+x[1], np.array(list(zip(df.columns, df.iloc[i,:].values)))))) tip = tip + '<br>' + '薪水:$' + str(y[i]) tooltip.append(tip)# 设置所有属性d3.scatter(jitter_func(X[:,0], jitter=1), # tSNE x坐标 jitter_func(X[:,1], jitter=1), # tSNE y坐标 x1=jitter_func(model.results['PC']['PC1'].values, jitter=0.05), # PC1 x坐标 y1=jitter_func(model.results['PC']['PC2'].values, jitter=0.05), # PC2 y坐标 color=df['job_title'].values, # 颜色基于工作标题 tooltip=tooltip, # 工具提示 size=normalize(y.values, minscale=1, maxscale=25), # 根据薪水设置节点大小。 opacity='opaque', # 通过仅突出显示密集区域来创建整洁的散点图 stroke='#000000', # 边缘颜色为黑色 cmap='tab20', # 色图 scale=True, # 缩放数据点 label_radio=['tSNE', 'PCA'], figsize=[1024, 768], filepath='Data_science_landscape.html', )
最后的话。
我们开始了这个任务,使用数据科学薪资数据集来研究数据科学领域的趋势。 我们可以得出结论,高薪工资主要在欧洲以外,具有资深的经验水平,并在大公司工作。远程工作似乎没有影响。这个趋势在2020年至2023年间似乎没有改变。 我们创建了一个交互式数据科学景观,并使用富集分析分析了聚类。交互式散点图是使用D3blocks库创建的。我希望您喜欢并从阅读这篇博客中学到了东西。只需记住,数据集中没有包含两个重要特征:幸福感和与家人朋友在一起的时间。 — 没有什么是永恒的,享受当下。 —
保持安全。保持警觉。
干杯,E。
如果您觉得这篇文章有帮助,请关注我,因为我会写更多这样的主题。如果您正在考虑加入小猪AI会员,您可以通过使用我的推荐链接来支持我的工作。这与一杯咖啡的价格相同,但这样可以让您每个月阅读无限的文章!
软件
- Scatterd文档页面
- Clusteval文档页面
- D3Blocks文档页面
- PCA文档页面
让我们连接!
- 让我们在LinkedIn上连接
- 在Github上关注我
- 在小猪AI上关注我
参考文献
- https://ai-jobs.net/salaries/download/ ( CC0:公共领域 )
- 2020年至2023年全球人工智能、机器学习、数据的薪资趋势
- 使用Python和小猪AI创建美丽的独立交互式D3图表,2022年
- 从数据到聚类:你的聚类何时足够好?小猪AI,2023年
- 从聚类到洞察:下一步,小猪AI,2022年
- 使用D3js和Python使散点图更具交互性,最大化利用您的散点图,小猪AI,2022年11月
- t-SNE、UMAP、PCA和其他映射之间的定量比较,小猪AI,2022年