TowardsDataScience-博客中文翻译-2020-十六-

TowardsDataScience 博客中文翻译 2020(十六)

原文:TowardsDataScience Blog

协议:CC BY-NC-SA 4.0

分析员工离职调查

原文:https://towardsdatascience.com/analyzing-employee-exit-surveys-fbbe35ae151d?source=collection_archive---------31-----------------------

使用 python 分析统计严密性和潜在因素以确定员工情绪

图片由来自 Pixabay 的 mohamed Hassan 提供

当分析员工情绪数据时,在我们的例子中是员工离职调查,我们必须考虑四个主题。

  1. 调查的统计严密性
  2. 调查对象的人口构成
  3. 对已定义的潜在结构的总体看法
  4. 根据受访者的特征(即性别、地点、部门等。)

首先,坚持这种方法将使我们能够确定我们的调查在多大程度上衡量了它想要衡量的东西。其次,从受访者特征的角度了解谁回答了调查(即性别、部门等),我们可以为我们的分析和结果提供背景。第三,这种方法将帮助我们确定响应者的总体情绪。最后但同样重要的是,它将帮助我们不仅确定哪些组织计划可能有助于增加情绪,而且还确定这些计划应该在哪里实施。

资料组

我们将使用的数据集是一个虚构的员工离职调查,该调查向员工提出了一系列关于其组织人口统计数据的问题(即部门)和 5 分李克特(即。强烈不同意,不同意,中立,同意,强烈同意)情绪问题(即。该组织提供了大量的晋升机会。没有使用开放式问题。

数据处理

import pandas as pd
import numpy as np
import scipy.stats as stats
import matplotlib.pyplot as plt
import seaborn as snsimport warnings
warnings.filterwarnings('ignore')
pd.set_option('display.max_columns', None)
%matplotlib inlinewith open('exit_data_final.csv') as f:
    df = pd.read_csv(f)
f.close()df.info()

我们向员工询问了 33 个项目或问题。在我们开始分析之前,我们需要进行一些数据清理。

df.drop('Unnamed: 0', axis=1, inplace=True)

让我们放弃这个奇怪的“未命名”专栏,因为它没有任何意义。

for var in df.columns:
    print(var, df[var].unique())

通过检查每个项目的唯一值,我们可以看到一些问题。

  1. 有些项目的缺失值被正确地标记为 np.nan,但其他项目则为空。
  2. 基于 df.info(),我们需要转换 Likert 项目的项目类型,因为它们当前被格式化为“objects”。
  3. 最后,我们需要转换一些值,以提高可视化效果的可读性。
# Replacing nulls with np.nan
for var in df.columns:
    df[var].replace(to_replace=' ', value=np.nan, inplace=True)# Converting feature types
likert_items = df[['promotional_opportunities', 'performance_recognized',
       'feedback_offered', 'coaching_offered', 'mgmt_clear_mission',
       'mgmt_support_me', 'mgmt_support_team', 'mgmt_clear_comm',
       'direct_mgmt_satisfaction', 'job_stimulating', 'initiative_encouraged',
       'skill_variety', 'knowledge_variety', 'task_variety', 'fair_salary',
       'teamwork', 'team_support', 'team_comm', 'team_culture',
       'job_train_satisfaction', 'personal_train_satisfaction', 'org_culture',
       'grievances_resolution', 'co-worker_interaction',
       'workplace_conditions', 'job_stress', 'work/life_balance']]for col in likert_items:
    df[col] = pd.to_numeric(df[col], errors='coerce').astype('float64')# Discretization of tenure
bins = [0,4,9,14,19,24]
labels = ['0-4yrs', '5-9yrs', '10-14yrs', '15-19yrs', '20+yrs']
df['tenure'] = pd.cut(df['tenure'], bins = bins, labels=labels)

潜在结构的发展

在我之前的 文章 中,我们回顾了分析统计严密性的过程(即。效度、信度、因子分析)。请随意回顾,但让我们快速回顾一下什么是潜在调查结构以及它们是如何派生的。

为了开发保持良好的统计严谨性的调查项目或问题,我们必须从学术文献开始。我们想要找到一个理论模型来描述我们想要测量的现象。例如,性格调查经常会使用大五模型(即。开放性、尽责性、外向性、宜人性和神经质)来开发调查项目。调查开发人员将为模型的每个组成部分精心设计 2-10 个(取决于调查的长度)项目。旨在评估相同成分的项目被称为测量“潜在结构”。换句话说,我们没有明确地测量“外向性”,因为这是一个“观察到的结构”,而是通过个别调查项目间接测量。在达到一定的严格程度之前,该调查将使用多个受访者样本进行试点测试。同样,如果你对用于确定严密性的统计分析感兴趣,看看我以前的 文章

# Calculating latent variables
df['employee_valued'] = np.nanmean(df[['promotional_opportunities',
                        'performance_recognized',
                        'feedback_offered',
                        'coaching_offered']], axis=1)df['mgmt_sati'] = np.nanmean(df[['mgmt_clear_mission',
                  'mgmt_support_me', 'mgmt_support_team', 'mgmt_clear_comm', 'direct_mgmt_satisfaction']], axis=1)df['job_satisfaction'] = np.nanmean(df[['job_stimulating',
'initiative_encouraged','skill_variety','knowledge_variety',
                                     'task_variety']], axis=1)df['team_satisfaction'] = np.nanmean(df[['teamwork','team_support',
                          'team_comm','team_culture']], axis=1)df['training_satisfaction'] = np.nanmean(df[['job_train_satisfaction',
'personal_train_satisfaction']], axis=1)df['org_environment'] = np.nanmean(df[['org_culture','grievances_resolution',
'co-worker_interaction','workplace_conditions']], axis=1)df['work_life_balance'] = np.nanmean(df[['job_stress','work/life_balance']], axis=1)df['overall_sati'] = np.nanmean(df[['promotional_opportunities', 'performance_recognized','feedback_offered', 'coaching_offered', 'mgmt_clear_mission','mgmt_support_me', 'mgmt_support_team', 'mgmt_clear_comm','direct_mgmt_satisfaction', 'job_stimulating', 'initiative_encouraged','skill_variety', 'knowledge_variety', 'task_variety', 'fair_salary','teamwork', 'team_support', 'team_comm', 'team_culture', 'job_train_satisfaction', 'personal_train_satisfaction', 'org_culture', 'grievances_resolution', 'co-worker_interaction', 'workplace_conditions', 'job_stress', 'work/life_balance']], axis=1)

我们的离职调查也已经发展到评估某些潜在的结构。每个调查项目都根据它要测量的潜在因素进行平均。最后,我们计算了一个“总体满意度”特征,该特征计算了每位受访者所有项目/潜在因素的总体平均值。

下面是调查项目的列表以及它们要测量的潜在结构。请记住,为了便于可视化,每个项目的每个标签都被大大缩短了。你可以想象这些问题,比如“从 1 到 5 分,我觉得我的工作很刺激”。

mappings = {1:'1) Dissatisfied', 2:'1) Dissatisfied', 3:'2) Neutral', 4:'3) Satisfied', 5:'3) Satisfied'}
likert = ['promotional_opportunities', 'performance_recognized',
       'feedback_offered', 'coaching_offered', 'mgmt_clear_mission',
       'mgmt_support_me', 'mgmt_support_team', 'mgmt_clear_comm',
       'direct_mgmt_satisfaction', 'job_stimulating', 'initiative_encouraged',
       'skill_variety', 'knowledge_variety', 'task_variety', 'fair_salary',
       'teamwork', 'team_support', 'team_comm', 'team_culture',
       'job_train_satisfaction', 'personal_train_satisfaction', 'org_culture',
       'grievances_resolution', 'co-worker_interaction',
       'workplace_conditions', 'job_stress', 'work/life_balance']for col in likert:
    df[col+'_short'] = df[col].map(mappings)

df.head()

为了有助于可视化,我们将创建新的功能,将 1 和 2 的评分汇总为不满意,3 为中性,4 和 5 为满意。这将使我们能够创建具有 3 个独特部分的堆积条形图。

受访者的特征

了解调查受访者的人口统计数据有助于为我们的分析提供背景信息。我们还必须记住,大多数员工离职调查都是在自愿的基础上完成的。由于这个令人困惑的变量,我们需要将从数据中收集的任何见解作为组织事务的“证据”,而不是确定的“证据”。员工可能对组织非常满意或生气,他们的态度肯定会反映在他们的回答中。最后,该调查包括大约 600 名受访者,我们需要注意不要将这些视为过去 4 年中发生的所有终止的总数。600 例终止可能只占已发生的所有终止中的一小部分。

换句话说,我们的分析希望确定那些对调查做出回应的员工对几个组织因素的满意度。我们不应该将我们的结果推广到更广泛的组织或所有被解雇的员工。

def uni_plots(feature, text):
    tmp_count = df[feature].dropna().value_counts().values
    tmp_percent = ((df[feature].dropna().value_counts()/len(df))*100).values
    df1 = pd.DataFrame({feature: df[feature].value_counts().index,
                        'Number of Employees': tmp_count,
                        'Percent of Employees': tmp_percent})

    f, ax = plt.subplots(figsize=(20,10))
    plt.title(text, fontsize=25, pad=30)
    plt.tick_params(axis='both', labelsize=15, pad=10)
    plt.xlabel(feature, fontsize=20)
    plt.xticks(size=18)
    plt.yticks(size=18)

    sns.set_color_codes('pastel')
    count = sns.barplot(x=feature, y='Number of Employees', color='b', data=df1, label='Number of Employees')
    for p in count.patches:
        count.annotate(format(p.get_height(), '.1f'), 
                   (p.get_x() + p.get_width() / 2., p.get_height()), 
                   ha = 'center', va = 'center',
                   xytext = (0, 9), 
                   textcoords = 'offset points', size = 20)

    sns.set_color_codes('muted')
    percent = sns.barplot(x=feature, y='Percent of Employees', color='b', data=df1, label='Percent of Employees')
    for i in percent.patches:
        percent.annotate(format(i.get_height(), '.1f'), 
                   (i.get_x() + i.get_width() / 2., i.get_height()), 
                   ha = 'center', va = 'center', 
                   xytext = (0, 9), size = 20,
                   textcoords = 'offset points')

    ax.set_ylabel('')
    ax.legend(ncol=2, loc="upper right", fontsize=15, frameon=True)
    sns.despine(left=False, bottom=False)
    ax.set_xticklabels(ax.get_xticklabels(), rotation=45)
    plt.show() def bi_cat_plot(feature1, feature2):
    ax = pd.crosstab(df[feature1], df[feature2], normalize='index')*100
    ax1 = ax.plot(kind='barh', stacked=True, figsize=(25,15), fontsize=25)
    for i in ax1.patches:
        width, height = i.get_width(), i.get_height()
        x, y = i.get_xy() 
        ax1.text(x+width/2,
                 y+height/2,
                 '{:.0f} %'.format(width),
                 horizontalalignment='center',
                 verticalalignment='center',
                 size=25)

    plt.title('Percentage of Termination Reasons by {}'.format(feature1), fontsize=30, pad=25)
    plt.ylabel(' ')
    plt.legend(prop={'size':20})

在 4 年(2017 年至 2020 年)的数据收集中,生产、客户服务和销售部门占调查受访者的 65%以上。在四年的调查中,我们的受访者有很大的差异。2018 年有 342 例终止,而 2020 年只有 37 例。

近 50%的受访者是自愿终止妊娠,这是一个重要的事实。同样,在没有确凿的 HRIS 数据的情况下,很难将我们的结果推广到更广泛的组织中,但终止原因的差异大小向我们表明,该公司可能在自愿终止方面存在问题。我们没有任何员工绩效数据来确定建设性的或令人遗憾的自愿离职,因为这将使我们能够专门集中分析令人遗憾的自愿离职。

受访者中最大的年龄组是 56 岁及以上,几乎占受访者的 20%。按离职原因划分年龄,我们可以看到这个年龄组占退休人数的 87%,这是有道理的。如果我们看看自愿离职和年龄,我们可以看到所有年龄组的平均分布。

类别数量较少的变量(如性别)通常不会为我们提供很多见解,除非我们看到数据存在重大偏差。与男性相比,女性对调查的回应比例似乎接近 2:1。从终止原因的性别来看,我们看到的百分比反映了我们看到的整体性别比例。

根据工作类型,我们的受访者分布相当均匀。我们确实看到管理和行政工作类型占了很小的一部分,但这些职位在整个组织中所占的比例更小。更有趣的是,我们看到高管的非自愿离职激增(38%)。与其他终止原因相比,我们通常会看到非自愿终止的情绪较低,因此,我们可以预计高管的整体情绪得分相当低。

总的来说,我们看到自愿离职的受访者占大多数,他们主要来自生产、客户服务和销售部门,并在职业生涯的后期(41 岁及以上)离职。

总体受访者情绪

通过首先取所有单个情感项目的平均值(即李克特项目)。最后,根据数据的切片方式计算出一个总平均值。

def overall_plot(feature):
    ax = round(df.groupby(feature)['overall_sati'].mean(),2).sort_values().plot(kind='barh', stacked=True,
                                                              figsize=(25,15), fontsize=25)
    for i in ax.patches:
        width, height = i.get_width(), i.get_height()
        x, y = i.get_xy() 
        ax.text(x+width/2,
                y+height/2,
                '{:.2f}'.format(width),
                horizontalalignment='center',
                verticalalignment='center',
                size=25)
    plt.title('Overall Employee Sentiment by {}'.format(feature), fontsize=30, pad=25)
    plt.ylabel(' ')

总的来说,人力资源的总体情绪最低,但在受访者中所占比例也最小(5.8%)。这是有问题的,因为平均水平会很快被少数对李克特项目评价很低的受访者左右。也就是说,我们不能忽视这种整体平均情绪的低迷。另一方面,生产和销售是受访者中最大的两个部分,他们得分第二和第三低。我们必须了解这三个部门,以确定哪些因素得分特别低。

非自愿离职在总体情绪中得分最低,这一事实并不令人惊讶。被公司解雇通常会产生负面情绪,这会影响你对调查的反应。更有趣的是,自愿终止妊娠是回复率的第一大原因,在整体情绪中排名第二。了解自愿离职的潜在原因可以为留住高绩效员工带来巨大的好处。

我们可以看到 46-50 岁年龄组得分最低。

不出所料,高管在总体情绪上得分特别低,因为他们中有 38%的人是被非自愿解雇的,但我们也必须记住,有 33%的人是被自愿解雇的。我们肯定要调查高管在哪些具体因素上得分特别低。最后,machine_ops 在总体情绪上得分同样较低(3.39),该群体的自愿终止回应率也为 52%。

最后,我们看不出终身职位群体的总体情绪有任何特别的差异。

总之,人力资源、生产和销售的总体情绪最低,人力资源得分(3.39)。由于生产和销售部门的受访者人数最多,我们需要检查这些部门在哪些具体因素上得分最低。非自愿离职的情绪最低(3.42),这并不奇怪,但自愿离职的平均情绪第二低,同时拥有最多的调查受访者。最后,我们看到高管和机器操作工作类型的情绪最低(3.39)。然而,还需要额外的分析来确定这种关系的性质。

平均整体潜在因素情绪

emp_value_avg = round(np.mean(df['employee_valued']),2)
mgmt_sati_avg = round(np.mean(df['mgmt_sati']),2)
job_sati_avg = round(np.mean(df['job_satisfaction']),2)
team_sati_avg = round(np.mean(df['team_satisfaction']),2)
training_sati_avg = round(np.mean(df['training_satisfaction']),2)
org_env_avg = round(np.mean(df['org_environment']),2)
work_life_avg = round(np.mean(df['work_life_balance']),2)
overall_sati = round(np.mean([emp_value_avg, mgmt_sati_avg, job_sati_avg, team_sati_avg,
                            training_sati_avg, org_env_avg, work_life_avg]), 2)
temp_dict = {'emp_value_avg': emp_value_avg, 'mgmt_sati_avg': mgmt_sati_avg,
                      'job_sati_avg': job_sati_avg, 'team_sati_avg': team_sati_avg, 
                      'training_sati_avg': training_sati_avg, 'org_env_avg': org_env_avg,
                      'work_life_avg': work_life_avg, 'overall_sati': overall_sati}
tmp_df = pd.DataFrame.from_dict(temp_dict, orient='index', columns=['average']).sort_values(by='average')plt.figure(figsize=(25,15))
plt.title('Overall Latent Factor Averages', fontsize=28)
plt.ylabel('Average Employee Rating', fontsize=25)
ax = tmp_df['average'].plot(kind='barh', fontsize=25)
for i in ax.patches:
    width, height = i.get_width(), i.get_height()
    x, y = i.get_xy() 
    ax.text(x+width/2,
            y+height/2,
            '{:.2f}'.format(width),
            horizontalalignment='center',
            verticalalignment='center',
            size=25)plt.grid(False)
plt.show()

如果我们忽略任何受访者的人口统计数据,并查看我们的潜在情绪因素,我们会看到 fair_salary、emp_value 和 org_env 得分最低。将我们的分析集中在这些因素上是很重要的,以便理解这些因素为什么低,以及它们在组织中的最低位置(即部门、工作类型等。).我们的结果也被确认为自愿终止妊娠。

likert = ['promotional_opportunities', 'performance_recognized',
       'feedback_offered', 'coaching_offered', 'mgmt_clear_mission',
       'mgmt_support_me', 'mgmt_support_team', 'mgmt_clear_comm',
       'direct_mgmt_satisfaction', 'job_stimulating', 'initiative_encouraged',
       'skill_variety', 'knowledge_variety', 'task_variety', 'fair_salary',
       'teamwork', 'team_support', 'team_comm', 'team_culture',
       'job_train_satisfaction', 'personal_train_satisfaction', 'org_culture',
       'grievances_resolution', 'co-worker_interaction',
       'workplace_conditions', 'job_stress', 'work/life_balance'] likert_avgs = []
for col in likert:
    likert_avgs.append(round(np.nanmean(df[col]),2)) likert_avgs_df = pd.DataFrame(list(zip(likert, likert_avgs)), columns=['likert_item', 'avg_sentiment'])
likert_avgs_df.set_index('likert_item', inplace=True) plt.figure()
ax = likert_avgs_df.plot(kind='bar', figsize=(25,15), fontsize=20)
for i in ax.patches:
        width, height = i.get_width(), i.get_height()
        x, y = i.get_xy() 
        ax.text(x+width-0.25,
                 y+height+.1,
                 '{:.2f}'.format(height),
                 horizontalalignment='center',
                 verticalalignment='center',
                 size=20)
plt.title('Average Overall Likert Item Sentiment', fontsize=30, pad=25)
plt.legend().remove()
plt.xlabel(' ')

通过检查构成每个潜在因素的 Likert 项目,我们可以看到晋升机会和绩效认可对员工价值的低情绪贡献最大。虽然我们希望看到不止一个 Likert 项目来评估工资满意度,但我们可以更详细地检查 fair_salary,以确定这种情绪在哪里特别低。最后,组织文化和不满解决似乎是导致组织环境情绪低落的最主要原因。

被调查者特征潜在构念情感

当分析调查数据时,很容易陷入众所周知的图表和绘图的兔子洞,却看不到你的目标。换句话说,我们需要缩小关注范围。分析情绪调查的最终目标是识别薄弱环节,在这些环节中可以实施组织计划来改进这些已识别的环节。我们主要关注的领域是自愿终止妊娠。首先,他们几乎占受访者的 50%。第二,这是我们可以对使用组织计划产生最大影响的员工群体。最后,我们希望限制自愿离职的数量,以限制组织的知识流失,并最大限度地减少与雇用替代员工相关的招聘和培训成本。

# plotting average likert sentiment by respondent characteristics for voluntary terminations
def bi_volterm_plot(feature1, feature2):
    tmp_df = df.loc[(df['reason_of_term']=='vol_term')]
    ax = round(tmp_df.groupby(feature1)[feature2].mean(),2).sort_values().plot(kind='barh', stacked=True,
                                                              figsize=(25,15), fontsize=25)
    for i in ax.patches:
        width, height = i.get_width(), i.get_height()
        x, y = i.get_xy() 
        ax.text(x+width/2,
                 y+height/2,
                 '{:.2f}'.format(width),
                 horizontalalignment='center',
                 verticalalignment='center',
                 size=25)
    plt.title('Average {} Sentiment of Voluntary Terminations by {}'.format(feature2, feature1),fontsize=30, pad=25)
    plt.ylabel(' ')
    plt.legend(prop={'size':20})

员工价值因素

毫不奇怪,人力资源部门的自愿离职在 emp_value 上得分最低,因为人力资源部门的整体情绪最低。此外,采购、客户服务和生产得分也相对较低。这一点很重要,因为生产和客户服务部门拥有最多的调查对象。

因为我们知道晋升机会和公认的绩效是低员工价值情绪背后的主要驱动因素,所以我们根据部门、年龄和工作类型绘制了这些李克特项目。

仅过滤自愿离职的数据,我们可以看到大多数部门在晋升机会方面得分较低,但客户服务、人力资源和采购得分特别低。年龄得分特别低,因为 21-50 岁之间自愿离职的员工在晋升机会情绪方面得分非常低。46-50 岁年龄组在这个李克特项目上得分很低(2.62)。主动离职的高管、管理人员、机器操作人员和服务人员工作类型在晋升机会情绪方面得分较低,高管得分非常低(2.17)。最后,除了 1-14 岁,所有终身职位组在晋升机会上得分较低,但 5-9 岁得分尤其低(2.80)。

我们再次看到采购和人力资源在公认绩效方面得分最低,但 IT 和生产部门的满意度也相当低。从员工年龄的角度来看,26-50 岁的得分最低,而且这些年龄组在晋升机会方面得分也很低。我们再次看到行政、管理和机器操作工作类型得分最低。最后,就像我们看到的晋升机会一样,5-9 岁的任期组在公认绩效方面得分最低。

公平工资因素

Fair_salary 在受访者的总体情绪中得分第二低。现在我们已经过滤了自愿离职的数据,让我们看看哪里的薪资情绪最低。不出所料,人力资源和采购部门得分最低。然而,倾向于在许多李克特项目上得分较低的产品实际上得分相对较高。也就是说,我们确实看到 R&D 和 IT 在这个李克特项目上得分较低。46-50 岁的人再次得分较低,他们的平均情绪得分为(2.76)。主管、管理和行政工作类型在 fair_salary 中得分最低。最后,5-9 岁的任期组得分最低。

组织环境因素

我们知道,就整体情绪而言,组织环境因素排名倒数第三。当我们过滤自愿离职时,我们看到人力资源部和采购部在这一因素上得分最低。让我们把这个因素分解成各个李克特项目。

我们从上面的分析中看到,组织文化和不满解决是李克特的两个项目,这两个项目似乎对组织环境的低整体情绪贡献最大。

看起来组织文化在所有部门的得分都很低,有时非常低。从年龄的角度来看,46-50 岁的人的 org_culture 最低,但 26-50 岁的人的 org _ culture 普遍较低。高管在李克特项目上得分极低(1.80),其次是机器操作和管理。也就是说,除了销售,所有工作类型的平均得分都低于 3.00。最后,从任期的角度来看,所有任期组的得分都低于 3.00,5-9 岁组的得分特别低。

采购、人力资源、客户服务和生产部门再次在申诉解决方面得分最低。在年龄组中,我们再次看到 46-50 岁的受访者情绪最低落。高管和机器操作人员对解决不满的情绪最低。最后,5-9 年任期组在李克特项目上得分最低,非常像组织文化。

概括起来

受访者的特征

自愿终止妊娠构成了最大的调查对象群体,接近 50%。这是个好消息,因为它让我们有足够的数据来洞察一个特别重要的群体的组织情绪。了解自愿离职发生的潜在原因可以帮助公司减少人员流动,从而最大限度地减少知识流失和招聘成本。

从部门角度来看,生产(32%)、客户服务(19%)和销售(16%)构成了大部分调查反馈。尽管其余部门的回复率明显较低,但他们的回复数量并没有低到足以证明他们的见解毫无意义。

56 岁及以上的受访者构成了最大的群体(23%),其余群体相对均衡,20 岁及以下的受访者仅占 2.3%。通过按终止原因对这些数据进行切片,我们可以看到 56 岁及以上的应答者中有 54%正在退休。

女性回答调查的比例几乎是男性的 2:1。

工作类型在调查中表现得相当均衡,服务人员(19%)、专业人员(17%)和销售人员(14%)位居前三。高管回答调查的比例最低(3%),只有 24 人回答。24 是一个相对较低的响应数,可能会产生不准确的结果。此外,行政工作类型的非自愿离职比例最大(38%),我们知道非自愿离职倾向于产生更负面的整体情绪。任何关于高管群体的见解都必须经过更大样本量的仔细审查和验证。

最后,组织任期产生平均 20%每个任期组响应的平均表示。

整体员工情绪

正如预期的那样,非自愿终止的平均情绪最低,但紧随其后的是自愿终止。似乎人力资源、生产和销售部门的整体情绪最低。此外,年龄在 46-50 岁之间以及行政、机器操作和管理工作类型的受访者整体情绪最低。

总体情绪最低的三个潜在因素是薪酬满意度(3.14)、员工价值(3.31)和组织环境(3.46)。如果我们检查这三个潜在因素的 Likert 项目/问题,我们可以看到,晋升机会和绩效认可对员工价值的影响最小。公平薪酬对薪酬满意度的影响最低。最后,组织文化和不满解决对组织环境的总体情绪最低。

自愿离职情绪

当具体观察自愿终止妊娠的人群时,我们看到了与上述类似的结果。薪资满意度、员工价值和组织环境的整体情绪最低。晋升机会和绩效认可再次成为员工价值情绪低落的驱动因素。公平工资是低工资满意度的主要驱动力。最后,组织文化和不满解决在组织环境潜在因素上得分最低。

晋升机会(员工价值)

大多数部门都需要改善晋升机会,但采购、人力资源、客户服务和生产部门最需要阻止自愿离职。此外,在大多数年龄组,尤其是 46-50 岁的人群中,晋升机会情绪似乎很低。行政人员、管理人员、机器操作人员和服务人员工作类型最缺乏晋升机会。最后,5-9 岁的终身雇员对晋升机会的情绪最低。

绩效认可(员工价值)

采购和人力资源部门受到了低绩效认可的困扰。46-50 岁的人、行政人员、管理人员和机器操作人员的工作类型对绩效认可的情绪也很低。最后,我们再次看到 5-9 年的任期似乎对这一项目的情绪很低。

公平工资(工资满意度)

人力资源、采购、R&D 和 IT 部门对他们的薪水的看法最低。年龄较大的群体,尤其是 46-50 岁的人,情绪低落。高管、管理人员和行政人员的薪酬满意度得分最低。

组织文化(组织环境)

在整个调查中,似乎组织文化的整体情绪最低。所有部门,尤其是采购、客户服务和生产部门,似乎都受到情绪低落的困扰。年龄和工作类型也是如此,46-50 岁的人和行政人员、机器操作人员和管理人员似乎得分最低。

申诉解决(组织环境)

该主题的情绪通常高于组织文化,但它仍然是导致组织环境情绪低落的主要因素。同样的部门,采购、人力资源、客户服务和生产得分最低。随着 46-50 岁的人再次得分最低,这一趋势还在继续。管理人员、机器操作人员和服务人员得分也最低。

行动纲要

我们将注意力集中在分析一项员工离职调查上,该调查是在 4 年前从大约 600 名员工中收集的。绝大多数调查应答者(50%)是自愿终止妊娠的。生产、客户服务和销售部门构成了大多数受访者。整体受访者的年龄略有倾斜,因为最大的群体是 56 岁及以上。最后,工作类型按比例表示。

非自愿终止妊娠的总体情绪最低,紧随其后的是自愿终止妊娠。总体而言,薪酬满意度、员工价值和组织环境的情绪最低。这些结果也在自愿终止的样本中得到证实。

然后分析特别集中在自愿终止的样品上。似乎为了提高员工的整体情绪并潜在地减少自愿离职,组织需要将其计划集中在改善晋升机会、绩效认可、薪酬、组织文化和申诉解决上。

任何旨在改善上述领域以潜在阻止自愿离职的举措最好针对采购、人力资源、客户服务和生产部门(根据需要)。此外,年龄在 46-50 岁、26-30 岁和 36-40 岁之间的员工也将从任何和所有计划中受益。从工作类型的角度来看,管理人员、机器操作人员、管理人员和服务人员也将从这些计划中受益(按需求排序)。值得注意的是,高管工作类型的样本量很小(24),其中只有三分之一是自愿离职。总体而言,该调查的样本量相对较小(600 人),因此,建议通过更大的调查样本来验证这些结果。也就是说,这些结果确实提供了对可能存在于内部的组织问题的一点了解。

NBA 交易截止后的球迷情绪分析

原文:https://towardsdatascience.com/analyzing-fan-sentiments-after-nba-trade-deadline-1b73f72d0ca2?source=collection_archive---------33-----------------------

Unsplash点特乡绅的照片

通过推特上关于截止日期前交易的信息来了解球队的整体球迷情绪。

介绍

今年我们看到了一些最大和最令人惊讶的季中交易。随着一些明显的赢家和其他人对其选择的怀疑,这显然是一个复杂情绪的管弦乐队。然而,在我解释我写这篇文章的原因之前,我想给非 NBA 球迷一些一周前发生的事情的背景。

照片由拉米罗·皮亚诺罗萨Unsplash 拍摄

随着 2020 年 2 月 6 日 NBA 19-20 赛季交易截止日期的临近,球迷们急切地等待着“Woj 炸弹”的降临,而谣言则通过各种人员在不同的平台上传播开来。关于这些谣言有太多要说的了,因为至少其中一些以某种形式成为了事实。

这真的在 twitter 上引起了爆发,我很享受看着推文滚动,试图抓住每个粉丝的情绪。

球员和媒体的反应很容易推断,但当你有 30 支不同球队的这么多球迷时,你如何确切地分辨谁感到不安,谁没有?一些球队因为失去了一名优秀的球员却没有得到太多的回报而明显感到沮丧,而另一些球队可能会以较小的数量表达他们的不满。

https://commons . wikimedia . org/wiki/File:2014 _ Toronto _ 猛龙 _fans_Air_Canada_Centre.jpg

所以,我做了一点挖掘,并恢复了过去 10 天中受交易截止日期影响最大的球队的推文。我的目标是了解这些球队的整体球迷情绪,并看看他们如何相互竞争。我将分析的 7 个团队是:

  • 亚特兰大老鹰队
  • 克利夫兰骑士队
  • 洛杉矶快船队
  • 迈阿密热火队
  • 休斯顿火箭队
  • 明尼苏达森林狼队
  • 金州勇士队

所以,事不宜迟,让我们开始吧。

谢尔盖·库图佐夫在 Unsplash 上拍摄的照片

该过程

我在 Python (Jupyter)上做了完整的分析,如果你想复制这个过程,我将为你添加我的代码片段。

需要以下库:NLTK、Textblob、Pandas(只需 pip 安装)。

您希望从导入以下内容开始:

import pandas as pd
import nltk
nltk.download('stopwords')
import en_core_web_sm
from textblob import TextBlob
import seaborn as sb
from bs4 import BeautifulSoup

我将每个团队的所有推文保存为单独的 CSV 文件,所以我上传了以下内容:

HawksTweets = pd.read_csv("HawksTweets.csv")
CavaliersTweets = pd.read_csv("CavaliersTweets.csv")
ClippersTweets = pd.)read_csv("ClippersTweets.csv")
HeatTweets = pd.read_csv("HeatTweets.csv")
TimberwolvesTweets = pd.read_csv("TimberwolvesTweets.csv")
RocketsTweets = pd.read_csv("RocketsTweets.csv")
WarriorsTweets = pd.read_csv("WarriorsTweets.csv")

然后,我会将它们合并到一个数据集,如下所示:

teams = [HawksTweets, CavaliersTweets, ClippersTweets, HeatTweets, TimberwolvesTweets, RocketsTweets, WarriorsTweets]
TeamTweets =  pd.concat(teams)

因为我们只想分析英语的推文:

TeamTweets = TeamTweets[TeamTweets['Language'] == 'English']

现在数据集已经准备好使用 NLTK 了。

照片由托尔加·阿赫梅特勒Unsplash 上拍摄

文本预处理

首先,我们希望将文本标记成单独的单词,这样我们就可以删除停用词、非英语单词和无意义的标签。我们用下面一行来标记:

TeamTweets['tokenized_sent'] = TeamTweets.apply(lambda row: nltk.word_tokenize(row['Text']), axis=1)

现在我们用漂亮的汤清洗课文:

soup = BeautifulSoup(TeamTweets['tokenized_sent'])
text = soup.get_text(strip=True)
TeamTweets['tokenized_sent'] = text

接下来,我将定义一个函数来分解文本,并进行一些额外的清理,这可能有助于我们完成这个过程。我还使用来自 NLTK 的英语单词语料库去除了非英语单词。

def untokenize(words):
    text = ' '.join(words)
    step1 = text.replace("`` ", '"').replace(" ''", '"').replace('. . .',  '...')
    step2 = step1.replace(" ( ", " (").replace(" ) ", ") ")
    step3 = re.sub(r' ([.,:;?!%]+)([ \'"`])', r"\1\2", step2)
    step4 = re.sub(r' ([.,:;?!%]+)$', r"\1", step3)
    step5 = step4.replace(" '", "'").replace(" n't", "n't").replace(
         "can not", "cannot")
    step6 = step5.replace(" ` ", " '")
    return step6.strip()englang = words = set(nltk.corpus.words.words())
TeamTweets['Sentences'] = TeamTweets['tokenized_sent'].apply(lambda x: [item for item in x if item in englang])
TeamTweets['Sentences'] = TeamTweets['Sentences'].apply(lambda x: untokenize(x))

现在,我有了一个包含已处理文本的数据集,它已经准备好进行 TextBlob 的情感分析。

https://commons . wikimedia . org/wiki/File:2019 _ NBA _ 总决赛 _ 游戏 _2.jpg

情感分析

首先,让我们为分数插入一列:

TeamTweets.insert(2, "Score", "", True)

接下来,我将定义一个函数,它将为我们的数据帧中的每一行文本提供情感输出。我将对我们的行应用这个函数,并获得每条推文的情感分数。

def senti(x):
    return TextBlob(x).sentimentTeamTweets['Score'] = TeamTweets['Sentences'].apply(senti)

我们以主观性和极性的形式得到分数。您可以使用下面的方法用 seaborn 来绘制它们:

ax1 = sb.catplot(x="Subjectivity", y="Team", data=df)

我的 Jupyter 笔记本截图

ax2 = sb.catplot(x="Polarity", y="Team", data=df)

我的 Jupyter 笔记本截图

现在,让我们通过对项目求和来计算每个团队的总得分。这将给出我们的最终得分,这将是球迷情绪的一个指标。

首先,我创建一个新的数据框,每个团队作为一行。

key = {'Team': ['Atlanta Hawks', 'Cleveland Cavaliers', 'LA Clippers', 'Miami Heat', 'Minnesota Timberwolves', 'Houston Rockets', 'Golden State Warriors']}
keydata = pd.DataFrame(key)

将团队列为一个列表:

teamlist = keydata.Team.tolist()
list2 = keydata.indexteams = []
teams.extend([list(a) for a in zip(teamlist, list2)])

最后,我编写了一个函数,根据球队名称计算得分总和:

def add_team(team_tuple):

    score = df.loc[(df['Team'] == team_tuple[0]),'Polarity'].sum()
    keydata.loc[keydata.index[team_tuple[1]]] = score
    returnfor team in teams:
    add_team(team)

最终的数据框汇总了每个队的得分。根据他们的推文,得分越高,这支球队的球迷情绪就越积极。

我的 Jupyter 笔记本截图

用图表展示我们的最终结果:

ax3 = sb.barplot(x="Score", y="Team", data=keydata)

我的 Jupyter 笔记本截图

结论

我们可以看到骑士队在得到安德烈·德拉蒙德后情绪不佳(考虑到他的合同还有 3 个月到期),所以他们只拥有他很短一段时间。另一方面,迈阿密热火队在交易了安德烈·伊戈达拉后,似乎得到了球迷们最好的反应。

埃德加·恰帕罗在 Unsplash 上拍摄的照片

感谢你的阅读!希望你学到了新东西。

更多类似内容关注我!

用特征重要性分析用户行为和 UX 性能

原文:https://towardsdatascience.com/analyzing-feature-importance-user-behaviour-and-ux-performance-cbf32d55eff8?source=collection_archive---------21-----------------------

如何应用商业策略概念进行特性重要性分析。有例子!

图片来自 pexels.com 的 Kaboompics

当我们在数据科学中考虑特征重要性时,我们可能也会考虑特征选择,然后我们的想法可能会最终出现在 scikit-learn 中可以找到的典型分类器算法中,如决策树、回归、随机森林……你能想到的。

这些算法对于捕获数据集中最重要的特征非常有用,然后可能会根据这些特征的重要性创建一个预测模型。

数据科学目前充斥着来自不同背景的人。在我的案例中,作为一名工业工程师,我想到了一种用工业工程方法进行分析的方法。除了学习大量的数学和微积分,我们还学习了策略和流程优化。

在工业工程中学到的一个经典概念是 BCG 矩阵(也称为增长份额矩阵)。这是波士顿咨询集团在 70 年代开发的一个概念,用于公司识别并将其产品聚集到不同的类别中,然后使它们在市场中发展,使公司的收入最大化。

如果您不了解 BCG 矩阵,不要担心,请继续阅读本文,您将会了解其中的概念。我还会在文末留下说明链接!

商业案例

假设您是一家电子商务公司的数据科学家,您的经理要求您进行分析,目的是确定他们可以在公司网站中改进的关键功能。通过这种方式,公司将获得更多的用户进行转换(在这种情况下转换可以是购买产品,或购买机票等)。所有公司最终想要的是客户转化更多,这样他们就有更多的收入。

他们可能会问你,是什么让人们更容易皈依

对于这个例子,我从一个虚构的在线销售产品的电子商务公司构建了一个玩具数据集。这家公司的网站具有你可以从这样的网页中期待的典型特征:搜索引擎、搜索后的过滤选项、将产品添加到购物车等。

你得到的数据

你能得到的简化的数据库看起来像这样:

包含用户数据事件的熊猫数据框架

我们还可以将该表视为数据透视表,使用以下代码:

df['one'] = 1df_pivot = (pd.pivot_table(df
            ,values=['one']
            ,columns=['event']
            ,index=['user_id','converted']
            ,aggfunc = {'one':'sum'}))df_pivot.columns = df_pivot.columns.to_series().str.join('_')df_pivot = df_pivot.reset_index()for col in df_pivot.columns[2:]: df_pivot = df_pivot.rename(columns = {col: col.replace('one_','')})

我们得到了这个数据透视表:

前一个数据帧的透视版本

有很多人做的动作,比如做一个搜索,然而,没有多少人改变。正因为如此,很难建立一个预测模型,因为只有少数人采取行动,最终导致转变。

我们可以构建一个决策树,看看哪些特性是最重要的,看看哪些是阈值,我们已经可以看到明显的转化趋势。主要的问题是:是什么使人皈依?

决策树—使用 Graphviz 包

因此,如果用户应用过滤器超过 5 次,并向购物车中添加超过 3 件商品,那么它有很高的转换机会…但它看起来有点太具体,在这个业务案例中没有用,不是吗?

让我们看看如果构建波士顿矩阵会发生什么

我为此构建了一个包(要安装它,在您的终端中运行这个命令: pip install bcganalysis )。这是您必须使用的代码,使用类似前面的数据透视表作为输入(访问 GitHub repo 了解更多细节!)

!pip install bcganalysis# we import the package first
from bcg_analysis import Generate_BCG# and matplotlib
import matplotlib.pyplot as plt# then we instantiate the object for the analysis 
# df is the table with the user and events
features = Generate_BCG(df)

# and we get the pivot table
features.get_pivot(index=['user_id','converted'],columns='event')

# then we generate the chart with penetration and conversion
features.generate_chart(threshold=1)# and then we plot it
features.plot_bcg() 

(*)关于如何应用代码的更详细的方法,请看我的 Github 中的自述文件:https://github.com/Mateil04/bcg_analysis。在这里,您可以使用回购中可用的玩具数据集进行示例。

我们得到的情节

在 x 轴上,它显示了有多少用户已经使用了每个功能至少一次,我们称之为渗透。也可以认为是该功能的流行度

在 y 轴上,我们可以看到转换,这是在所有使用过该功能的用户中,有多少人最终进行了转换。

例如,几乎所有的用户(大约 80%)都在进行搜索操作,但是在所有执行这个操作的用户中,大约 20%的人会转换。这是因为这是一个非常常见的动作,几乎每个用户都会做。它让网站持续滚动,但不能保证转化。

另一方面,如果我们看一下 add_to_cart 动作,我们会看到它是由少数用户(渗透率20%)完成的,但这些用户的转化率非常高(75%)。

这就是 BCG 矩阵的用武之地(它不是严格意义上的矩阵,但它就是这么叫的!)

BCG 矩阵—图片由 Matias Eiletz 提供

波士顿矩阵最初被认为是用来评估一家公司的产品在市场上的成功和市场份额。

例如,你可以考虑麦当劳公司,把巨无霸作为摇钱树,因为它是非常受欢迎的经典产品,总能带来一些收入。纯素汉堡可以在问号象限,因为现在纯素食品非常流行,而且肯定有很大的潜力,但它必须在市场上取得成功,才能成为明星产品并带来最大的收入。一个鱼汉堡可以在狗象限。这可以是这样的情况,一个产品曾经是一个问号,但没有通过测试,最终没有流行起来。

产品的所需运动用虚线箭头表示。

卡介苗矩阵的 x 轴是市场份额,y 轴是增长率。在我们的业务案例中,我们为了渗透(流行)和转换而改变了它们,但是本质是一样的。

卡介苗矩阵-改编-马蒂亚斯·埃列茨拍摄的图像

回到生成的图,我们可以看到:

问号象限中的功能(add_to_cart,apply_filter)是具有很大潜力但尚未被开发的功能。我们希望它们变得更受欢迎,并像那样,成为我们未来的之星特色(高渗透率,高转化率)。为了实现这一点,您可能会建议 UX 团队改变这些功能在网站上的显示方式,例如颜色和大小,以突出显示它们。然后,他们会得到更多的点击,将会有更多的整体转换(这一功能被转换成了星形象限)。

犬类象限中的特征,它们可以被省略并替换为其他特征。这个例子中的“看稀有”按钮似乎没有吸引很多人,也不会触发转换,所以最好考虑另一个不同的功能来代替它。

摇钱树是那些没有太多转换的功能,但是它们非常受欢迎,并且让网站的用户流量很大。在我们的案例中,搜索并点击一篇又一篇的文章,就是明显的例子。

最终,如图表所示,星星将在某个时候传给奶牛,我们将需要找到更多的特征作为问号,比如在主页上一个全新的部分,然后成为我们的新星星。

总结

在本文中,我展示了如何执行与 UX 设计相关的功能重要性分析,评估用户行为。它也可以被认为是一种聚类方法。

如果您想查看包的代码,可以在我的 GitHub 中查看。

如果您想了解更多关于卡介苗基质的信息,您可以查看这篇维基文章或这篇其他帖子

分析 Fitbit 数据,揭开疫情封锁期间身体模式变化的神秘面纱

原文:https://towardsdatascience.com/analyzing-fitbit-data-to-demystify-bodily-pattern-changes-amid-pandemic-lockdown-5b0188fec0f0?source=collection_archive---------21-----------------------

健康分析:了解锻炼、睡眠、卡路里燃烧模式,并朝着正确的方向优化

疫情禁闭后在家锻炼

在过去的四年里,我一直定期锻炼。由于疫情的局势,这是我第一次这么长时间不能去健身房或使用任何设备。希望我们大多数人都面临着同样的问题。我们都知道呆在家里拉平疫情曲线有多重要。

然而,我们仍然可以通过呆在家里来尽力保持健康。我一直在我的阳台/露台上锻炼,用楼梯作为引体向上的杠,一根长杆作为我的重量(幸运的是我找到了它),做几个俯卧撑和一个阻力管,这是我过去在旅行中买的。

为了量化家庭锻炼的效果,我下载了我的 Fitbit 数据,做了一些预处理,并将其标记为锁定前后的数据。主要目的是了解:

什么是不同的 Fitbit 度量?他们之间有什么关系吗?

我在禁闭前后消耗的卡路里是怎样的?

我能在家燃烧/达到同样的强度吗?

这两个时期我的睡眠模式有什么变化?

我可以根据这一分析创建智能推荐吗?比如我的身体需要睡几个小时才能恢复?

作为分析的结果,我可以进一步优化什么?

通过这篇博文,我打算对数据进行分析,找到上述问题的答案。这篇文章也是一个很好的例子,告诉我们如何利用分析来变得健康和健美。我们如何从数据中获得洞察力,并创建关于睡眠、锻炼提醒、锻炼赞赏徽章等的智能建议。我已经在这里提供了数据和代码。让我们开始有趣的东西。

在家锻炼时面临的挑战

在跳到代码和找到见解之前,我想指出我在家庭锻炼中面临的挑战,这些挑战使我深入挖掘并进行这一分析。

我一直在尽最大努力用我能找到的任何东西来保持我的健康。我面临的主要挑战是-

1.重量和设备的缺失

在健身房,我做了 110 公斤(242 磅)的深蹲,120 公斤(265 磅)的 T2,硬拉等等。在家里,我不能设法得到这样的重量级人物。所以,我用我得到的更轻的竿增加了重复次数。

我没有合适的引体向上或引体向上杆,所以我只能在楼梯上做。我在大部分练习中使用阻力管。重量/张力变小了,所以我再次增加了重复次数。但是,缺少重量和设备仍然是一个挑战。

我发现了一篇有趣的文章,文章称举重会导致肌肉增长,从而燃烧更多的卡路里。所以,举重是关键。

[## 为什么举重是减肥的关键

如果你曾经举过重物,你可能不止一次想知道你应该举多少重量。你是…

www.verywellfit.com](https://www.verywellfit.com/are-you-lifting-enough-weight-1231071)

2.空间不足

我不能出去跑步或散步,因为现在很危险。为此,我在瑜伽垫上做有氧运动。

这里是我在家做的一些锻炼来保持我的健康。下面你可以观察到的一件事是我一直戴着一个 Fitbit。

在家锻炼:俯卧撑

在家锻炼:引体向上和阻力管

在家锻炼:二头肌弯曲和深蹲

需要注意的重要事项

一个人的卡路里摄入量取决于他们的性别、年龄、身高和体重。有在线计算器可以用来计算。人们也可以使用性别、年龄、身高和体重信息来计算身体质量指数(身体质量指数)。

同样,燃烧的卡路里数量取决于性别、年龄、身高和体重因素。在线计算器可用于计算。

[## 身体质量指数和卡路里计算器

这个计算器估计你在运动和日常生活中燃烧的卡路里数量。报告生成了…

nutritiondata.self.com](https://nutritiondata.self.com/tools/calories-burned)

活动数据分析和有趣的见解

Fitbit 提供的两个重要文件是活动数据和睡眠数据。让我们看一看它

活动数据

我将数据分别标记为锁定前(2020 年 3 月 15 日之前)和锁定后(2020 年 3 月 15 日当天和之后)。我还删除了周末的数据,因为我通常不会在周六和周日锻炼,给我的身体休息和恢复时间。

度量分析

锁定前后指标的变化

我们来看看散点图 ( 柱状图)和箱线图。有时,平均值可能是一个有偏差的参数,因此应该查看百分位数分布。

metric = 'Calories Burned'
#Equally dividing Pre-Post Data into 21 Percentile buckets
bins = 21
percentile_bins = np.linspace(0,100,bins)
pre_bins = np.percentile(df_exercise_preLockdown[metric], percentile_bins)
post_bins = np.percentile(df_exercise_postLockdown[metric], percentile_bins)
sns.distplot(pre_bins,kde_kws={ "lw": 2, "label": "Pre Lockdown"},axlabel=metric)
sns.distplot(post_bins,kde_kws={ "lw": 2, "label": "Post Lockdown"})
plt.show()

锁定前和锁定后消耗的卡路里散点图(直方图和 Kde 图的组合)

锁定前和锁定后消耗的卡路里箱线图

在封锁期后,燃烧的平均卡路里从 2475 千卡减少到 2290 千卡。大约下降了 7.5%。

5-Days with Minimum Calories Burned during Pre-Lockdown Period
**2128, 2154, 2203, 2229, 2330**
5-Days with Maximum Calories Burned during Pre-Lockdown Period
**2597, 2755, 2755, 2896, 2969**5-Days with Minimum Calories Burned during Post-Lockdown Period
**1864, 1921, 1935, 1959, 1962**
5-Days with Maximum Calories Burned during Post-Lockdown Period
**2688, 2729, 2777, 2827, 3224****All units in Kcalorie*

另一件有趣的事情是一致性也降低了。与封锁前相比,封锁后燃烧的卡路里有显著的差异。从图和计算的标准偏差可以清楚地看出这一点。我有点知道原因了。在办公室,我经常从办公桌走到会议室,午饭后也会和同事一起散步。在封锁前,我的健身时间是一致的,但现在在家里,我偏离了固定的日程,有时开始得晚,结束得快。 宾果!!我发现了改进的余地。一致性是关键。

度量之间的关系

我还想找出所走的步数、走过的距离和燃烧的卡路里之间的关系。无论从直觉上还是从数据上看,它们似乎都是正相关的。让我们来验证相关性的数量或强度。

度量之间的相关性

所有指标都与相关系数为 1 的完全相关。这基本上意味着,如果我们知道 Fitbit 使用的正确公式,我们就可以使用消耗的卡路里精确地推导出步数和距离。也表示关系/公式为线性

睡眠数据分析和有趣的见解

我们来看看睡眠数据。我已经标记了封锁前后的数据。

睡眠数据

度量分析

锁定前后睡眠指标的变化

我们来看看距离图 ( 直方图)和箱线图。有时,平均值可能是一个有偏差的参数,因此我们可以查看百分位数分布。

metric = 'MinutesAsleep'
#Equally dividing Pre-Post Data into 21 Percentile buckets
bins = 21
percentile_bins = np.linspace(0,100,bins)
pre_bins = np.percentile(df_sleep_preLockdown[metric], percentile_bins)
post_bins = np.percentile(df_sleep_postLockdown[metric], percentile_bins)
sns.distplot(pre_bins,kde_kws={ "lw": 2, "label": "Pre Lockdown"},axlabel=metric)
sns.distplot(post_bins,kde_kws={ "lw": 2, "label": "Post Lockdown"})
plt.show()

锁定前和锁定后的睡眠分钟数分布图(直方图和 Kde 图的组合)

锁定前和锁定后期间的睡眠分钟数(以分钟为单位)箱线图

这些年来,我的身体一直在训练自己减少睡眠,同时保持活跃。有一些文章围绕着睡眠需求因人而异的主题。这取决于基因和身体多年来的训练方式等因素。这里有一篇有趣的文章。

[## 大脑基础:理解睡眠

睡眠解剖学睡眠阶段睡眠机制你需要多少睡眠?梦想基因的作用和…

www.ninds.nih.gov](https://www.ninds.nih.gov/Disorders/Patient-Caregiver-Education/Understanding-Sleep)

在封锁期后,我燃烧的平均卡路里下降了 7.5%。平均睡眠时间也减少了 14 分钟。但是,好的一面是,在锁定后,差异非常小。我的睡眠时间更稳定。 太棒了!!这是我感到高兴的事情,甚至在封锁结束后我也想这么做。

智能推荐

接下来,我想进行智能推荐,根据我燃烧的卡路里数量,根据我的身体需求推荐适量的睡眠。

有些日子我会因为睡眠不足或高强度锻炼而感到懒惰,但大多数时候我会因为充足的睡眠而感到精力充沛、精神焕发。

其次,我晚上锻炼,晚上睡觉大多在 12 点以后。所以,我对第一天燃烧的卡路里数和第二天的睡眠时间感兴趣。我将使用这些信息创建一个数据集,其中我将记录第一天燃烧的卡路里和第二天的睡眠时间。

根据您的锻炼和睡眠模式,您可以创建不同的数据集。

#Find Day2
df_exercise['incremented_date']= df_exercise.Date + datetime.timedelta(days=1)#Joining Activity Dataset with Sleep Dataset
query = """
            select 
                a.*,
                b.MinutesAsleep
            from
                df_exercise a
            join 
                df_sleep_agg b
            on
                a.incremented_date = b.Date
            order by
                a.Date

        """
df_join = sqldf(query,globals())

这是最终连接数据集的外观

活动和睡眠数据集的连接

接下来,我会试着找到燃烧的卡路里和一分钟睡眠之间的理想关系。我将使用回归,因为它给出系数值,并且结果是直观的。此外,由于我只是在消耗卡路里方面退步,保持模型简单是明智的。

cols = ['CaloriesBurned']
clf = LinearRegression()
clf.fit(df_join[cols],df_join['MinutesAsleep'])

回归模型的截距和系数为

Intercept = 253.77
Coefficient = 0.0441

让我们看看我的推荐引擎会根据燃烧的卡路里量给出什么建议

Calories Burnt: **2100 Kcalorie**
Amount of Ideal sleep needed for your body-type: **5 hours 47 minutes**Calories Burnt: **2200 Kcalorie**
Amount of Ideal sleep needed for your body-type: **5 hours 51 minutes**Calories Burnt: **2500 Kcalorie**
Amount of Ideal sleep needed for your body-type: **6 hours 5 minutes**Calories Burnt: **2600 Kcalorie**
Amount of Ideal sleep needed for your body-type: **6 hours 9 minutes**Calories Burnt: **2700 Kcalorie**
Amount of Ideal sleep needed for your body-type: **6 hours 13 minutes**Calories Burnt: **2969 Kcalorie**
Amount of Ideal sleep needed for your body-type: **6 hours 25 minutes**Calories Burnt: **3000 Kcalorie**
Amount of Ideal sleep needed for your body-type: **6 hours 27 minutes**

注意事项:

  1. 该关系仅在消耗的卡路里数在 3000 千卡限制内时有效,因为在训练集中,很少有我消耗超过 3000 千卡的情况。
  2. 在 3000 千卡之后,这种关系很可能是非线性的。也就是说,如果我燃烧 3000+千卡热量,我做了一些我的身体不习惯的事情,燃烧这么多热量需要我的身体采取适量的饮食和休息来恢复。所以,一旦我们得到更多的数据,我们就能更好地发现这种关系。
  3. 3000 千卡范围内的关系也可能是非线性的。可以应用非线性算法来进一步微调推荐。我使用回归只是因为它的简单和直观。

结论

通过这篇博文,我分享了我如何使用 Fitbit 数据来了解我在疫情情况下的身体模式变化。主要见解可以总结为:

由于缺乏适当的设备、运动较少以及无法遵循严格的作息时间,在锁定后的时间里,燃烧的卡路里减少了 7.5%。但是,通过家庭锻炼,我已经能够燃烧大量的卡路里。

在锁定后期间,平均睡眠时间减少了 14 分钟,但睡眠时间的一致性增加了,变化很小,对此我很高兴,并希望继续下去。

我们还发现了关于 Fitbit 指标及其相关性的重要见解。

我们还创建了一个智能推荐引擎,根据燃烧的卡路里数和个人的睡眠要求,推荐所需的睡眠时间。

我已经在这里 做好了数据和代码

如果你有任何疑问,请联系我。我也很想知道你是否对健康分析有兴趣。

我的 Youtube 频道获取更多内容:

[## 阿布舍克·蒙戈利

嗨,伙计们,欢迎来到频道。该频道旨在涵盖各种主题,从机器学习,数据科学…

www.youtube.com](https://www.youtube.com/channel/UCg0PxC9ThQrbD9nM_FU1vWA)

关于作者-:

Abhishek Mungoli 是一位经验丰富的数据科学家,拥有 ML 领域的经验和计算机科学背景,跨越多个领域并具有解决问题的思维方式。擅长各种机器学习和零售业特有的优化问题。热衷于大规模实现机器学习模型,并通过博客、讲座、聚会和论文等方式分享知识。

我的动机总是把最困难的事情简化成最简单的版本。我喜欢解决问题、数据科学、产品开发和扩展解决方案。我喜欢在闲暇时间探索新的地方和健身。在 Linkedininsta gram关注我,查看我以前的帖子。我欢迎反馈和建设性的批评。我的一些博客-********

分析巴西的全球识字率数据

原文:https://towardsdatascience.com/analyzing-global-literacy-rates-data-in-r-88062f813ad2?source=collection_archive---------29-----------------------

数据

我在 data.world 上偶然发现了一些非常有趣的数据集,其中引起我注意的是这个关于 2011 年至 2018 年全球识字率的数据集,按年龄组和性别进行了细分。如果你读过我以前的博客,那么所有的分析都将在 r 中完成。这次我不会解释我的代码,所以如果你想理解代码,最好有一些 r 的工作知识。

设置

**library**(tidyverse)
**library**(lubridate)
**library**(httr)
**library**(readxl)
**library**(extrafont)
**library**(hrbrthemes)
**library**(gghighlight)
**library**(ggtext)
**library**(ggmap)
**library**(scales)
**loadfonts**(device = "win")

**GET**("https://query.data.world/s/ethf3e5hs6lv52vzvegp3g3qeeeloj", **write_disk**(tf <- **tempfile**(fileext = ".xlsx")))literacy_data <- **read_excel**(tf)

分析

2018 年女性平均识字率最高的国家

literacy_data **%>%** 
  **filter**(Gender**==**"female") **%>%**
  **group_by**(Country) **%>%**
  **filter**(**max**(Year)**==**2018**&n_distinct**(Year)**>=**5,
         **mean**(`Literacy rate`[Year**==**2018],na.rm = T)**>mean**(`Literacy rate`[Year**!=**2018],na.rm = T)) **%>%** 
  **group_by**(Country,Year) **%>%** 
  **summarise**(Average_literacy_rate = **mean**(`Literacy rate`,na.rm = T)) **%>%**
  **ggplot**(**aes**(**reorder**(Country,Average_literacy_rate,mean),Average_literacy_rate,color=**factor**(Year)))**+**
  **geom_point**(size=9,alpha=0.4)**+**
  **coord_flip**()**+**
  **gghighlight**(Year**==**2018)**+**
  **scale_y_percent**()**+**
  **scale_color_manual**(values = **c**("2018"="firebrick"))**+**
  **theme_minimal**()**+**
  **labs**(x=NULL,y=NULL,title = "Countries that had peak average literacy rate in <span style='color:firebrick'>**2018**</span>",
       color=NULL)**+**
  **theme_ipsum_ps**()**+**
  **theme**(plot.title = **element_markdown**(size=20,margin = **margin**(b = 10)))

该图显示了与以往所有年份的平均水平相比,2018 年女性平均识字率最高的国家。南美洲的一些国家和阿根廷保持在 90%左右。其他拉美国家如巴西、墨西哥和哥伦比亚也显示出良好的增长势头。孟加拉国的女性识字率显著提高。这可能是由于政府将教育支出增加了一倍,从 2008 年的 20 亿美元增加到 2016 年的 43 亿美元

各地区识字率的分布

world_avg <- literacy_data **%>%**
  **summarise**(avg=**mean**(`Literacy rate`,na.rm = T)) **%>%** 
  **pull**(avg)

literacy_data **%>%**
  **group_by**(Region) **%>%** 
  **mutate**(region_avg= **mean**(`Literacy rate`,na.rm = T)) **%>%** 
  **ungroup**() **%>%** 
  **ggplot**(**aes**(Region,`Literacy rate`,color=Age))**+**
  **geom_jitter**(alpha=0.7,size=3,height  = 0.2)**+**
  **geom_hline**(**aes**(yintercept=world_avg),color="grey40",size=0.9)**+**
  **geom_segment**(**aes**(x=Region,xend=Region,y=world_avg,yend=region_avg),color="black")**+**
  **coord_flip**()**+**
  ggsci**::scale_color_jama**()**+**
  **stat_summary**(fun.y = mean, geom = "point", size = 8,color="firebrick")**+**
  **geom_text**(**aes**(x=Region,y=region_avg,label=scales**::percent**(region_avg)),color="white",hjust=0.5,nudge_y = 0.01)**+**
  **theme_classic**()**+**
  **theme**(text = **element_text**(family = "Roboto Condensed"),axis.title = **element_text**(size = 12),
        axis.text.y = **element_text**(family = "Roboto Condensed", size = 12),
        panel.grid = **element_blank**(),
        plot.title = **element_text**(size = 25,hjust = 0.5,family = "Roboto Condensed"))**+**
  **annotate**(geom = "text",x=7,y=1,label=**paste0**("Worldwide literacy average of\n",scales**::percent**(world_avg)),color="white")**+**
  **scale_y_percent**()**+**
  **labs**(x=NULL,title = "Distribution of literacy rate across regions")

灰色垂直线代表全球平均识字率,红色大圆圈突出显示每个地区的平均识字率。与全球平均水平和其他地区相比,撒哈拉以南非洲、北非和西非、中亚和南亚等地区的平均识字率较低。毫不奇怪,年龄较大的人识字率普遍较低。

各年龄组的识字率趋势

avg_year <- literacy_data **%>%** 
  **group_by**(Year,Age) **%>%**
  **summarise**(avg=**mean**(`Literacy rate`,na.rm = T)) **%>%**
  **ungroup**()

avg_year **%>%** 
  **ggplot**(**aes**(Year,avg,color=Age))**+**
  **geom_line**(size=1.5)**+**
  **geom_point**(size = 2.6, **aes**(color = Age), shape = 15) **+**
  **geom_text**(data=avg_year **%>%** **group_by**(Age) **%>%** **filter**(Year**==max**(Year)),**aes**(label=Age),hjust=**-**0.5)**+**
  **scale_color_manual**(values = **c**("15-24"="#d20962","15+"="#005670","25-64"="#ce181e","65+"="#8a7967"))**+**
  **scale_y_percent**()**+**
  **labs**(y=NULL,x=NULL,title = "Literacy rate trend across age groups")**+**
   **theme**(
    text = **element_text**(family = "Roboto Condensed"),
    plot.margin = **unit**(**rep**(1.2, 4), "cm"),
    plot.title = **element_text**(size = 20, 
                              color = "#22292F",
                              face = "bold",
                              margin = **margin**(b = 5)),
    plot.subtitle = **element_text**(size = 15, 
                                 margin = **margin**(b = 35)),
    plot.caption = **element_text**(size = 10,
                                margin = **margin**(t = 25),
                                color = "#606F7B"),
    panel.background = **element_blank**(),
    axis.text = **element_text**(size = 12, color = "#22292F"),
    axis.text.x = **element_text**(margin = **margin**(t = 5)),
    axis.text.y = **element_text**(margin = **margin**(r = 5)),
    axis.line = **element_line**(color = "#3D4852"),
    axis.title = **element_text**(size = 14),
    axis.title.y = **element_text**(margin = **margin**(r = 15),
                                hjust = 0.5),
    axis.title.x = **element_text**(margin = **margin**(t = 15),
                                hjust = 0.5),
    panel.grid.major = **element_line**(color = "#DAE1E7"),
    panel.grid.major.x = **element_blank**(),
    legend.position = "none"
  )

除了 2012 年所有年龄组的识字率急剧下降之外,这里没有什么有趣的东西可看

女性平均识字率高于男性平均识字率的国家

countries_female <- literacy_data **%>%** 
  **group_by**(Country) **%>%** 
  **filter**(**mean**(`Literacy rate`[Gender**==**"female"],na.rm = T)**>mean**(`Literacy rate`[Gender**==**"male"]))

literacy_data **%>%** 
  **semi_join**(countries_female) **%>%** 
  **group_by**(Country) **%>%** 
  **summarise**(avg_ltrcy_male = **mean**(`Literacy rate`[Gender**==**'male'],na.rm = T),
            avg_ltrcy_female=**mean**(`Literacy rate`[Gender**==**'female'],na.rm = T)) **%>%**
  **ungroup**() **%>%** 
  **ggplot**(**aes**(y=**reorder**(Country,avg_ltrcy_female),x=avg_ltrcy_male,xend=avg_ltrcy_female))**+**
  ggalt**::geom_dumbbell**( size=5, colour="grey",colour_x = "#005670",colour_xend = "#d20962")**+**
  ggrepel**::geom_text_repel**(**aes**(x=avg_ltrcy_female,label=**percent**(avg_ltrcy_female,accuracy = 1)))**+**
  ggrepel**::geom_text_repel**(**aes**(x=avg_ltrcy_male,label=**percent**(avg_ltrcy_male,accuracy = 1)))**+**
  **labs**(x=NULL,y=NULL,title = "Countries where female literacy rate is higher than male literacy rate")**+**
  **scale_x_percent**()**+**
  **theme_classic**()**+**
  **theme**(
    text = **element_text**(family = "Roboto Condensed"),
    plot.margin = **unit**(**rep**(1.2, 4), "cm"),
    plot.title = **element_text**(size = 20, 
                              color = "#22292F",
                              face = "bold",
                              margin = **margin**(b = 5)),
    plot.subtitle = **element_text**(size = 15, 
                                 margin = **margin**(b = 35)),
    plot.caption = **element_text**(size = 10,
                                margin = **margin**(t = 25),
                                color = "#606F7B"),
    panel.background = **element_blank**(),
    axis.text = **element_text**(size = 12, color = "#22292F"),
    axis.text.x = **element_text**(margin = **margin**(t = 5)),
    axis.text.y = **element_text**(margin = **margin**(r = 5)),
    axis.line = **element_line**(color = "#3D4852"),
    axis.title = **element_text**(size = 14),
    axis.title.y = **element_text**(margin = **margin**(r = 15),
                                hjust = 0.5),
    axis.title.x = **element_text**(margin = **margin**(t = 15),
                                hjust = 0.5),
    panel.grid.major = **element_line**(color = "#DAE1E7"),
    panel.grid.major.x = **element_blank**(),
    legend.position = "none")

我很好奇哪些国家的女性平均识字率高于或等于男性平均识字率。这张图表上的大多数国家都是南美和非洲地区的小国,这确实令人惊讶。

男女识字率差距最大的国家

countries_male <- literacy_data **%>%** 
  **group_by**(Country) **%>%** 
  **filter**(**mean**(`Literacy rate`[Gender**==**"female"],na.rm = T)**<mean**(`Literacy rate`[Gender**==**"male"]))

literacy_data **%>%** 
  **semi_join**(countries_male) **%>%** 
  **group_by**(Country) **%>%** 
  **summarise**(avg_ltrcy_male = **mean**(`Literacy rate`[Gender**==**'male'],na.rm = T),
            avg_ltrcy_female=**mean**(`Literacy rate`[Gender**==**'female'],na.rm = T),
            diff= avg_ltrcy_male**-**avg_ltrcy_female) **%>%** 
  **top_n**(20,diff)**%>%** 
  **ggplot**(**aes**(y=**reorder**(Country,avg_ltrcy_female),x=avg_ltrcy_male,xend=avg_ltrcy_female))**+**
  ggalt**::geom_dumbbell**( size=5, colour="grey",colour_x = "#005670",colour_xend = "#d20962")**+**
  **geom_text**(**aes**(x=avg_ltrcy_female,label=**percent**(avg_ltrcy_female,accuracy = 1)),vjust=**-**1)**+**
  **geom_text**(**aes**(x=avg_ltrcy_male,label=**percent**(avg_ltrcy_male,accuracy = 1)),vjust=**-**1)**+**
  **geom_rect**(**aes**(xmin=1,xmax=1.2,ymin=**-**Inf,ymax=Inf),fill="grey")**+**
  **geom_text**(**aes**(label=**percent**(diff,accuracy = 1), y=Country, x=1.1), fontface="bold", size=4)**+**
  **geom_text**(**aes**(x=1.1,y=20.5,label="Difference"))**+**
  **labs**(x=NULL,y=NULL,title = "Top 20 countries with highest discrepency between <span style='color:#005670'>male</span> and <span style='color:#d20962'>female</span> literacy rates")**+**
  **scale_y_discrete**()**+**
  **scale_x_percent**(breaks = **c**(0.3,0.6,0.9),labels = **c**("30%","60%","90%"))**+**
  **theme_classic**()**+**
  **theme**(
    text = **element_text**(family = "Roboto Condensed"),
    plot.margin = **unit**(**rep**(1.2, 4), "cm"),
    plot.title = **element_markdown**(size = 20,margin = **margin**(b = 5)),
    plot.subtitle = **element_text**(size = 15, 
                                 margin = **margin**(b = 35)),
    plot.caption = **element_text**(size = 10,
                                margin = **margin**(t = 25),
                                color = "#606F7B"),
    panel.background = **element_blank**(),
    axis.text = **element_text**(size = 12, color = "#22292F"),
    axis.text.x = **element_text**(margin = **margin**(t = 5)),
    axis.text.y = **element_text**(margin = **margin**(r = 5)),
    axis.line = **element_line**(color = "#3D4852"),
    axis.title = **element_text**(size = 14),
    axis.title.y = **element_text**(margin = **margin**(r = 15),
                                hjust = 0.5),
    axis.title.x = **element_text**(margin = **margin**(t = 15),
                                hjust = 0.5),
    panel.grid.major = **element_line**(color = "#DAE1E7"),
    panel.grid.major.x = **element_blank**(),
    legend.position = "none")

接下来,我想看看男女识字率差异最大的国家。对于审美,我只是根据两性差异的数值,可视化了前 20 名。同样,我们也可以在这里看到许多非洲国家和一些亚洲国家,比如巴基斯坦——我来自那里。非常有趣的是,在非洲有些国家,女性的平均识字率高于男性,但同时,在非洲有些国家,男性的识字率远远高于女性。

在地图上将其可视化

world <- **map_data**(map = "world") **%>%**
  **filter**(region**!=**"Antartica")

long_lat <- literacy_data **%>%**
  **group_by**(Country) **%>%** 
  **summarise**(`Literacy rate`=**mean**(`Literacy rate`,na.rm = T)) **%>%** 
  **ungroup**() **%>%** 
  **left_join**(world,by = **c**("Country"="region")) **%>%** 
  **filter**(**!is.na**(lat))

p <- **ggplot**() **+** 
  **geom_map**(data = world, map = world,
           **aes**(long, lat, group = group, map_id = region),
           fill = "#282828", color = "#282828") **+**
  **geom_map**(data = long_lat, map = world,
           **aes**(fill = `Literacy rate`, map_id = Country),
           color = "#282828", size = 0.5, alpha = .8) **+**
  **scale_fill_gradient2**(low = "#be0027", high = "#0a8ea0",mid = "#b4a996",midpoint = 0.6) **+**
  **scale_y_continuous**(breaks=**c**()) **+**
  **scale_x_continuous**(breaks=**c**()) **+**
  **labs**(x = "", y = "") **+**
  **guides**(
    fill = **guide_legend**(title = "Literacy Rate")
  ) **+**
  **coord_map**("gilbert", xlim = **c**(**-**300, 300)) **+**
  **labs**(
    title = "Global Literacy Rates"
  ) **+**
  **theme**(
    text = **element_text**(family = "Roboto Condensed"),
    plot.title = **element_text**(color = "#ffffff",
                              margin = **margin**(t = 30, b = 10),
                              size = 20),
    plot.subtitle = **element_text**(color = "#ababab",
                              margin = **margin**(b = 10),
                              size = 15,
                              hjust = 0.7),
    plot.background  = **element_rect**(fill  = "#323232"),
    panel.background = **element_rect**(fill  = "#323232", 
                                    color = "#323232"),
    legend.position = "right",
    legend.title = **element_text**(color = "#6d6d6d",
                                size = 10),
    legend.background = **element_rect**(fill = "#323232"),
    legend.text = **element_text**(color = "#6d6d6d",
                               size = 10)
  )

cowplot**::ggdraw**(p)**+**
  **theme**(plot.background = **element_rect**(fill = "#323232"))

我想尝试可视化地图上的数据,因为我从来没有这样做过。在深入研究细节之前,这有助于从全局层面了解数据

近距离观察巴基斯坦

south_asia <-literacy_data **%>%** 
  **filter**(Region**==**"Central and Southern Asia") **%>%** 
  **group_by**(Year,Country) **%>%** 
  **summarise**(avg_ltrcy=**mean**(`Literacy rate`)) **%>%** 
  **ungroup**() **%>%** 
  **group_by**(Country) **%>%** 
  **filter**(**n**()**>**1)

south_asia **%>%**
  **ggplot**(**aes**(Year,avg_ltrcy))**+**
  **geom_line**(size=1.5,**aes**(color=Country))**+**
  **gghighlight**(Country**==**"Pakistan",use_direct_label = F,use_group_by = F)**+**
  **scale_color_manual**(values = **c**("Pakistan"="#11862f"))**+**
  **geom_text**(data = south_asia **%>%** **group_by**(Country) **%>%**
              **filter**(Year**==max**(Year)) **%>%** **ungroup**(),**aes**(label=**paste0**(Country,"-",**percent**(avg_ltrcy,accuracy = 1))),size=4,hjust=0,fontface="bold")**+**
  **scale_x_continuous**(breaks = **seq**(2010,2019,by = 1),limits = **c**(2010,2021))**+**
  **annotate**(geom = "text",x=2020,y=0.8,label="Between 75% and 100%",color="black",fontface="bold")**+**
  **geom_rect**(**aes**(xmin=2019,xmax=2021,ymin=0.75,ymax=1),fill="#3be8b0",alpha=0.05)**+**
  **geom_rect**(**aes**(xmin=2019,xmax=2021,ymin=0.5,ymax=0.75),fill="#56a0d3",alpha=0.05)**+**
  **annotate**(geom = "text",x=2020,y=0.6,label="Between 50% and 75%",color="black",fontface="bold")**+**
  **geom_rect**(**aes**(xmin=2019,xmax=2021,ymin=0,ymax=0.5),fill="#c90f23",alpha=0.05)**+**
  **annotate**(geom = "text",x=2020,y=0.4,label="Less than 50%",color="black",fontface="bold")**+**
  **scale_y_percent**()**+**
  **labs**(color=NULL,title = "Pakistan vs the rest of the region",x=NULL,y=NULL)**+**
  **theme**(legend.position = "none")**+**
  bbplot**::bbc_style**()**+**
  **theme**(text = **element_text**(family = "Roboto Condensed"))

因为我来自巴基斯坦,我想看看我的国家与同一地区的其他国家相比如何。虽然总体趋势略有上升,但令人失望的是,巴基斯坦仅好于阿富汗,落后于其邻国,如印度、孟加拉国和伊朗。

结论

在这篇文章中,我试图让我的可视化更有创造性,而不是依赖于典型的条形图。我很惊讶地发现,有很多“较穷”的国家拥有很高的识字率。我希望有更多的数据可以帮助我们了解哪些因素影响一个国家的识字率,我们可以根据这些数据建立一个基于某种回归分析的模型。

分析健康保险市场数据

原文:https://towardsdatascience.com/analyzing-health-insurance-market-data-71b1cf00e97d?source=collection_archive---------21-----------------------

健康保险数据的探索性数据分析

照片由像素上的 Pixabay 拍摄

Healthcare.gov 向公众提供合格健康计划(QHP)景观数据。这些数据集包括联邦政府运营市场的各州的个人和家庭健康计划。对医疗保险计划以及其他一般医疗保健服务的可用数据进行定量分析,可能有助于在定价和基于价值的护理方面使我们的医疗保健系统更加透明。

在本帖中,我们将对 QHP 景观数据进行探索性数据分析。数据可在这里获得。它也可以作为一个。我的 GitHub 上的‘CSV’文件。

我们开始吧!

首先,让我们将数据存储在 pandas 数据框中,并打印列的列表:

import pandas as pd
df = pd.read_csv("QHP_landscape_2020.csv")
print(df.columns)

我们还可以打印完整的列列表:

print(list(df.columns))

如你所见,有 128 列。我们将把我们的分析限制在以下几列:

  1. 发行人名称
  2. 县名
  3. 计划营销名称
  4. 医疗最高自付费用-个人-标准
  5. 医疗免赔额—个人—标准
  6. 初级保健医生—标准
  7. 急诊室—标准
  8. 专家—标准
df = df[['Issuer Name', 'County Name', 'Plan Marketing Name',   
         'Medical Maximum Out Of Pocket - Individual - Standard',
         'Medical Deductible - Individual - Standard', 'Primary Care Physician - Standard', 'Specialist - Standard', 'Emergency Room - Standard']]

print(df.head())

我们可以看到有几个分类列。让我们定义一个将数据框、列名和限制作为输入的函数。当被调用时,它打印分类值的字典以及它们出现的频率:

def return_counter(data_frame, column_name, limit):
   from collections import Counter    print(dict(Counter(data_frame[column_name].values).most_common(limit)))

让我们将函数应用于“发行人名称”列,并将结果限制为五个最常见的值:

return_counter(df, 'Issuer Name', 5)

我们可以看到发行者名称“Medica”的记录最多。

让我们将函数应用于“County Name”列:

return_counter(df, 'County Name', 5)

正如您所看到的,这是一个有用的快速测试,可以查看数据中是否有任何明显的不平衡,这通常是建模时需要处理的一个关键问题。

接下来,从数字列(如“医疗最高自付费用-个人-标准”)中生成汇总统计数据会很有用。首先,我们需要将字符串值的美元金额转换成浮点类型。

OOP_INDV = []
for i in list(df['Medical Maximum Out Of Pocket - Individual - Standard'].values):
    try:
        OOP_INDV.append(float((str(i)[1] + str(i)[3:])))
    except(ValueError):
        OOP_INDV.append(np.nan)

df['OOP_INDV'] = OOP_INDV

让我们确保转换成功:

print(df[['Medical Maximum Out Of Pocket - Individual - Standard', 'OOP_INDV']].head())

看起来不错!

现在,让我们定义一个采用数据框、分类列和数值列的函数。每个类别的数字列的平均值和标准偏差存储在数据框中,并且数据框根据平均值以降序排序。如果您想要快速查看特定类别对于特定数字列是否具有更高或更低的平均值和/或标准偏差值,这将非常有用。

def return_statistics(data_frame, categorical_column, numerical_column):
    mean = []
    std = []
    field = []
    for i in set(list(data_frame[categorical_column].values)):
        new_data = data_frame[data_frame[categorical_column] == i]
        field.append(i)
        mean.append(new_data[numerical_column].mean())
        std.append(new_data[numerical_column].std())
    df = pd.DataFrame({'{}'.format(categorical_column): field, 'mean {}'.format(numerical_column): mean, 'std in {}'.format(numerical_column): std})
    df.sort_values('mean {}'.format(numerical_column), inplace = True, ascending = False)
    df.dropna(inplace = True)
    return df

我们可以查看“开证人姓名”和“医疗最高自付费用-个人”的汇总统计数据:

stats = return_statistics(df, 'Issuer Name', 'OOP_INDV')
print(stats.head(15))

“德州蓝十字和蓝盾”的个人最高自付费用和跨计划类型的零标准差。

接下来,我们将使用箱线图来显示基于最小值、最大值、中值、第一个四分位数和第三个四分位数的数值分布。如果您对它们不熟悉,可以看看文章了解 Boxplots

与汇总统计函数类似,此函数采用数据框、分类列和数值列,并根据限制显示最常见类别的箱线图:

def get_boxplot_of_categories(data_frame, categorical_column, numerical_column, limit):
    import seaborn as sns
    import matplotlib.pyplot as plt
    keys = []
    for i in dict(Counter(df[categorical_column].values).most_common(limit)):
        keys.append(i)
    print(keys)

    df_new = df[df[categorical_column].isin(keys)]
    sns.boxplot(x = df_new[categorical_column], y = df_new[numerical_column])

让我们在 5 个最常见的发行者名称中为 OOP_INDV 生成箱线图:

get_boxplot_of_categories(df, 'Issuer Name', 'OOP_INDV', 5)

我们可以对“医疗免赔额—个人—标准”进行类似的分析。让我们将字符串值的美元金额转换为浮点类型:

DEDUCT_INDV = []
for i in list(df['Medical Deductible — Individual — Standard'].values):
    try:
        DEDUCT_INDV.append(float((str(i)[1] + str(i)[3:])))
    except(ValueError):
        DEDUCT_INDV.append(np.nan)

df['DEDUCT_INDV'] = DEDUCT_IND

让我们确保转换成功:

print(df[['Medical Deductible - Individual - Standard', 'DEDUCT_INDV']].head())

我们可以查看“发行人名称”和“医疗免赔额—个人—标准”的汇总统计数据:

stats = return_statistics(df, 'Issuer Name', 'DEDUCT_INDV')
print(stats.head(15))

“Wellmark Value Health Plan,Inc .”和“Wellmark Health Plan of Iowa,Inc .”的平均“医疗免赔额—个人—标准”最高。

让我们为 5 个最常出现的发行人名称中的 DEDUCT_INDV 生成箱线图:

get_boxplot_of_categories(df, 'Issuer Name', 'DEDUCT_INDV', 5)

最后,让我们定义一个函数,它将一个数据框、分类列、分类值和两个数值列作为输入,并显示一个散点图:

def get_scatter_plot_category(data_frame, categorical_column, categorical_value, numerical_column_one, numerical_column_two):
    import matplotlib.pyplot as plt
    import seaborn as sns
    df_new = data_frame[data_frame[categorical_column] == categorical_value]
    sns.set()
    plt.scatter(x= df_new[numerical_column_one], y = df_new[numerical_column_two])
    plt.xlabel(numerical_column_one)
    plt.ylabel(numerical_column_two)

让我们为 Medica 生成一个 OOP_INDV 与 DEDUCT_INDV 的散点图:

get_scatter_plot_category(df, ‘Issuer Name’, ‘Medica’, ‘DEDUCT_INDV’, ‘OOP_INDV’)

我就讲到这里,但是请随意处理数据并自己编码。我鼓励你对急诊室、专科医生和初级保健列进行类似的分析。例如,了解初级保健的共同保险百分比在不同的发行人名称之间的差异是很有趣的。

概括地说,我回顾了几种分析健康保险市场数据的方法。这包括用箱线图和散点图显示数据。我们还定义了用于生成汇总统计数据的函数,比如平均值、标准差和分类值的计数。我希望这篇文章有趣。这篇文章的代码可以在 GitHub 上找到。感谢您的阅读!

分析 StyleGAN 如何工作:高质量图像生成中的样式合并

原文:https://towardsdatascience.com/analyzing-how-stylegan-works-style-incorporation-in-high-quality-image-generation-80a29227075b?source=collection_archive---------15-----------------------

入门

在之前的帖子中,我们讨论了 2K 图像到图像的翻译、视频到视频的合成以及大规模的类条件图像生成。即 pix2pixHD、vid-to-vid 和 BigGAN。

但是我们离生成基于真实风格的图像还有多远呢?快速浏览一下真实照片的时尚程度:

照片由 anabelle cariteUnsplash 拍摄

为此,在这一部分中,我们将关注通过自适应实例规范化风格合并。为此,我们将重温层内规范化的概念,这将被证明对我们理解 GANs 非常有用。

没有风格,就没有派对!

StyleGAN (基于风格的生成对抗网络生成器架构 2018)

基于我们对甘的理解,我们现在将能够控制他们的风格,而不仅仅是生成图像!多酷啊。但是,等一下。我们已经看到, InfoGAN 可以控制依赖于去纠缠表示的图像生成。

这项工作严重依赖于渐进式 GANs、自适应实例规范化( AdaIN )和风格转移。我们已经在前面的部分中介绍了渐进式 gan,所以在我们专注于理解这项工作之前,让我们深入了解它们的其余部分。

1.了解特征空间规范化和样式转换

人类视觉系统对图像统计非常敏感。众所周知,空间不变的统计,如通道方式的均值和方差可靠地编码图像的风格。同时,空间变化特征编码具体实例

批量标准化

图片由作者提供,最初用 Latex 编写

在所描绘的等式中, N 是图像批次的数量 H 高度和 W 宽度。希腊字母 μ ()表示平均值,希腊字母 σ ()表示标准差。类似地, γβ 对应于导致线性/仿射变换的可训练参数,对于所有通道来说是不同的。具体来说 γβ 是具有通道维度的向量。批次特征是形状为[N,C,H,W]的 x ,其中索引 c 表示每个通道的平均值。值得注意的是,空间维度以及图像批次被平均。这样,我们将我们的特征集中在一个紧凑的空间,这通常是有益的。

但是,就风格和全局特征而言,所有个体通道都共享羞耻学习特征,即γ、β。因此,BN 可以直观地理解为将一批图像归一化为以单一样式为中心。尽管如此,卷积层能够了解一些批内风格的差异。因此,每个样品可能仍然有不同的风格。例如,如果您想将所有图像转换为相同的共享风格(即梵高风格),这是不可取的。

但是如果我们不混合特性批次特性呢?

实例规范化

与 BN 层不同,实例归一化(IN)仅在特征空间维度上计算,但对于每个通道(和每个样本)再次独立计算。从字面上看,我们只是在前面的等式中去掉了对 N 的求和。令人惊讶的是,实验验证了 IN 中的仿射参数可以完全改变输出图像的风格。与 BN 相反,IN 中的可以将每个单独样本的样式标准化为目标样式(由γ和β建模)。由于这个原因,训练一个模型转换到一个特定的风格是比较容易的。因为网络的其余部分可以将其学习能力集中在内容操作和局部细节上,同时丢弃原始的全局细节(即风格信息)。

以这种方式,通过引入由多个γ组成的集合,可以设计一个网络来模拟过多的有限样式,这正是条件实例规范化的情况。

自适应实例规范化(AdaIN)

另一个图像的风格转移的想法开始变得自然。如果γ,β是从另一幅图像的特征统计中注入的呢?通过这种方式,我们将能够通过给定我们期望的特征图像均值为β,方差为γ来对任意风格建模。 AdaIN 正是这样做的:它接收输入 x(内容)和样式输入 y,并简单地调整 x 的通道均值和方差以匹配 y 的均值和方差。数学上:

图片由作者提供,最初用 Latex 编写

仅此而已!那么,我们可以做些什么呢?只需一个小改动的单层?让我们看看!

来源:https://arxiv.org/abs/1703.06868

在上半部分,您可以看到一个简单的编码器-解码器网络架构,其中有一个额外的 AdaIN 层用于样式对齐。在下半部分,你会看到这个惊人想法的一些结果!总之,AdaIN 通过对齐一阶统计量(μ和σ)来执行风格转移(在特征空间中),在复杂性方面没有额外的成本。如果你想玩这个想法,代码可以在这里找到(官方)(非官方)

2.基于样式的生成器

让我们回到我们最初理解风格的目标——甘。基本上,NVIDIA 在这项工作中完全抓住了我们对 GANs 中大多数发电机的理解和设计。让我们看看怎么做。

在一个普通的 GAN 发生器中,采样的输入潜在空间向量 z 被投影和整形,因此它将通过转置卷积上采样进行进一步处理,有或没有卷积。在这里,潜在向量由一系列完全连接的层进行变换,即所谓的映射网络 f !这导致了另一个学习向量 w ,叫做中间潜在空间 w。但是为什么有人会这样做呢?

图片由 StyleGAN paper 提供,来源:https://arxiv.org/abs/1812.04948

映射网络 f

这种选择的主要原因是中间潜在空间 W 不必支持根据任何固定分布的采样。通过连续映射,导出其采样密度。这种映射 f“打开”了 W 的空间,因此它隐含地实施了一种解开的表示。这意味着变异因素变得更加线性。作者认为与纠缠的图像相比,基于非纠缠的图像更容易生成真实的图像。有了这个完全无人监管的把戏,我们至少期望 WZ 空间纠缠度小。让我们看一下图中所示的 A。

街区:风格特征

b 区:噪音

此外,作者为 G 提供了明确的噪声输入,作为建模随机细节的直接方法。 B 块是由不相关的高斯噪声组成的单通道图像。它们作为附加噪声图像被馈送到合成网络的每一层。单一噪声图像被广播到所有特征地图。

合成网络 g

除了初始块之外,每一层都从上采样层开始,以使空间分辨率加倍。然后,添加卷积块。在每次卷积之后,2D 每像素噪声被添加到模型随机性中。最后用魔法 AdaIN 层注入与风格对应的已学中间潜空间。

风格混合和截断技巧

不是像 BigGAN 那样截断潜在向量 z,而是在中间潜在空间 W 中使用它。这实现为:w ' = E(w)—ψ*(w—E(w)),其中 E(w)= E(f(z))。ε表示预期值。控制样品质量的重要参数是ψ。当它接近 0 时,我们粗略地得到收敛到数据集的平均图像的采样面。如下图所示,在 W 空间中截断似乎效果很好:

图片由 StyleGAN paper 提供,来源:https://arxiv.org/abs/1812.04948

正如原论文所完美描述的:

“有趣的是,各种高级属性经常在对立面之间翻转,包括观点、眼镜、年龄、颜色、头发长度,通常还有性别。 " ~泰罗·卡拉斯等人

引入的另一个技巧是风格混合。从潜在空间 Z 采样 2 个样本,它们生成对应于两种风格的两个中间潜在向量 w1、w2。w1 应用于交叉点之前,w2 应用于交叉点之后。这可能是在块内部执行的。这种正则化技巧防止网络假设相邻的样式是相关的。一定比例的生成图像(通常为 90%)使用了这种技巧,并且每次都随机应用于网络中的不同位置。

3.浏览基于样式的生成器的设计选择

每个注入的风格和噪声(块 A 和 B) 被定位在网络中。这意味着当修改样式/噪声的特定子集时,预计只会影响图像的某些方面。

风格:我们来看看这种本土化的原因,从风格说起。我们广泛地看到,AdaIN 操作首先将每个通道归一化为零均值和单位方差。然后,它应用基于风格的尺度和偏差。以这种方式,用于后续卷积运算的特征统计被改变。更确切地说,先前的统计/样式在下一个 AdaIN 层中被丢弃。因此,在被下一个 AdaIN 操作覆盖之前,每个样式只控制一个卷积。

噪声:在传统的发生器中,潜在向量 z 被馈入网络的输入端。这被认为是次优的,因为它消耗了发电机的学习能力。这是合理的,因为网络需要发明一种方法来从早期激活生成空间变化的数字。

通过在每次卷积后添加每像素噪声,实验验证了噪声的影响出现在网络的局部。与 BigGAN 类似,每个层都有新的噪声,因此没有动力从先前的激活中产生随机效果。

以上都可以举例说明如下:

图片由 StyleGAN paper 提供,来源:【https://arxiv.org/abs/1812.04948

在左边,我们有生成的图像。在中间,4 种不同的噪声应用于所选的子区域。右边可以观察到一大组不同噪声的样本的标准差。

4.量化空间的解开

令人惊叹的是,他们第一次能够量化空间的解缠。因为如果你数不过来,它就不存在!为此,他们引入了两种新的方法来量化空间的混乱

感知路径长度

如果你对纠缠和不纠缠的表示感到不舒服,你可以重新访问 InfoGAN 。用非常简单的术语来说,纠缠是混合的,解纠缠与编码相关,但在某种程度上是可分离的。我喜欢把解开的表征称为一种低维度数据的解码信息。

假设我们有两个潜在的空间向量,我们想在它们之间进行插值?我们怎么可能确定“潜在空间的行走”对应于一个纠缠或不纠缠的表象?直观上,不太清晰的潜在空间应该导致在图像中观察到的感觉上平滑的过渡。

潜在空间向量的插值可以告诉我们很多。例如,图像中可能出现非线性、不平滑和急剧的变化。你怎么称呼这样的代表?例如,在任一端点都不存在的要素可能会出现在线性插值路径的中间。这是一个混乱世界的标志,即纠缠表象。

量化是根据潜在空间中的小步长ε来进行的。如果我们把一个潜在的空间插值路径细分成小段,我们就可以测量距离。后者在两个步骤之间测量,具体为 t,其中 t 在[0,1]中,t+ε。然而,基于生成的图像来测量距离是有意义的。因此,人们可以将所有步骤的距离相加,以穿过两个随机的潜在空间样本 z1z2 。注意,距离是在图像空间中测量的。实际上,在这项工作中,他们测量了两个 VGG 网络嵌入之间的成对距离。数学上这可以描述为(slerp 表示球面插值):

图片由作者提供,最初用 Latex 编写

有趣的是,他们发现通过添加噪声,路径长度(平均距离)大约减半,而混合风格略有增加(+10%)。此外,这一测量证明,8 层全连接架构显然产生了一个中间潜在空间 W,它比 z 更不纠缠。

线性可分性

让我们看看这是如何工作的。抓紧了!

  1. 我们首先用z∾P(z)生成 200K 幅图像,并使用标签为 Y 的辅助分类网络对它们进行分类。
  2. 我们保留 50%具有最高置信度得分的样本。这导致 100k 高分自动标记( Y )潜在空间向量 z 用于渐进 GAN,w 用于风格 GAN。
  3. 我们拟合一个线性的 SVM 来预测标签 X 仅基于潜在空间点( zw 用于样式-GAN)并且通过这个平面分类这些点。
  4. 我们计算条件熵 H( Y | X ),其中 X 表示由 SVM 预测的类别, Y 是由分类器确定的类别。
  5. 我们按照 exp(σ(H(Y | X))计算可分性得分,对数据集的所有给定属性求和。我们基本上为每个属性拟合一个模型。请注意,CelebA 数据集包含 40 个属性,如性别信息。

定量结果可在下表中观察到:

图片由 StyleGAN paper 提供,来源:https://arxiv.org/abs/1812.04948

本质上,给定 SVM 标签 X ,条件熵 H 告诉我们需要多少额外信息来确定样本的真实类别。理想的线性 SVM 将导致完全确定地知道 Y ,导致熵为 0。高熵值意味着高不确定性,因此基于线性 SVM 模型的标签根本不能提供信息。不夸张的说,熵 H 越低越好。

结论

GANs 上提出的工程解决方案一直让我惊叹不已。在本文中,我们看到了一个令人兴奋的设计,它通过自适应实例规范化来注入参考图像的样式。风格-甘绝对是该领域最具革命性的作品之一。最后,我们强调了所提出的线性可分性的度量,这使得我们在本系列中深入到越来越多的高级概念。

一如既往,我们专注于直觉,我们相信你不会气馁,开始尝试甘。如果你想开始用一堆模型进行实验以重现最先进的结果,你绝对应该检查 Tensorflow 中的这个开源或者 Pytorch 中的这个 one

下一部AI Summer 上有!

参考

[1]t .卡拉斯、s .莱恩和 t .艾拉(2019 年)。一种基于风格的生成对抗网络生成器体系结构。在IEEE 计算机视觉和模式识别会议论文集(第 4401–4410 页)中。

[2]伊奥费和塞格迪(2015 年)。批量标准化:通过减少内部协变量转移加速深度网络训练。 arXiv 预印本 arXiv:1502.03167

[3]乌里扬诺夫博士、韦达尔迪博士和莱姆皮茨基博士(2016 年)。实例规范化:快速风格化缺少的要素。 arXiv 预印本 arXiv:1607.08022

[4]t .宫户,t .片冈,Koyama,m .,& Yoshida,Y. (2018)。生成对抗网络的谱归一化。arXiv 预印本 arXiv:1802.05957 。

原载于 2020 年 5 月 9 日 https://theaisummer.com**的

使用机器学习分析韩国流行音乐|第 1 部分—数据收集和清理

原文:https://towardsdatascience.com/analyzing-k-pop-using-machine-learning-part-1-data-collection-cleaning-4b407baf7bce?source=collection_archive---------54-----------------------

K-Pop 机器学习教程系列

这是教程的第 1 部分,我在这里收集 K-Pop 数据并清理数据。

萨维里·博博夫Unsplash 上的照片

视频版附解说:https://youtu.be/lkhorCY5tFA

我的整个代码:https://github . com/import Data/kpop-analysis/blob/master/K _ pop _ Data _ cleaning . ipynb

介绍

作为一个在韩国出生和长大的人,我是听着 K-pop 长大的。这些年来,韩国流行音乐成为了一种全球现象,它的流行程度至今仍让我难以置信。

所以,我认为使用机器学习来分析 K-pop 以探索有趣的见解会很酷。谢谢查宁(又名。数据教授为理念!

在这篇文章中,我将向您展示数据科学周期中的数据收集和数据清理阶段。

数据收集

我不得不做一些谷歌搜索来找到数据集。经过一番搜索,我发现这个网站有一个 excel 文件。这是一项关于社交媒体和韩国流行音乐的调查,我觉得很有趣。我喜欢他们问的问题,也喜欢最近进行的调查。

该数据集包含来自世界各地的 240 名 K-pop 粉丝,共有 22 个调查问题。

数据集链接:Rraman,Saanjanaa (2020): KPOP 数据. xlsx. figshare。数据集。https://doi.org/10.6084/m9.figshare.12093648.v2

数据清理

数据清理是一个重要的步骤,因为您需要用于 EDA 和模型构建的最干净的数据。如果你把垃圾放进去,那么你会从模型中得到垃圾。

数据集可能有前导空格和尾随空格。所以,我决定用这个函数去掉那些空白。然后我删除了第一列“时间戳”,因为它没有用。

函数来删除数据帧中的前导和尾随空格

因为列名就是问题,而且它们太长,所以我决定给它们取代码名来简化列。

重命名列

接下来,检查数据集是否有空值。

检查空值

有三列包含空值。首先,让我们检查只有一个空值的列。

我发现 life_chg 和 money_src 中的空值都是“n/a”,于是干脆用字符串“none”代替。

对于“daily_MV_hr”列,我决定用平均值替换空值。有多种方法可以处理空值(删除行,分配唯一的类别,或者可以运行回归模型来预测丢失的值,等等),但是我认为用平均值替换它们是最好的选择。

我取 1 和 4 的平均值,即 2.5 小时,去掉了“小时”这个词。我注意到有些类别在范围内,所以为了简单起见,我取了这些范围的平均值。我创建了一个特殊的函数来处理这个问题。

函数在一些有范围而另一些没有范围时寻找平均值

清洁“每日 MV 小时”色谱柱之前和之后

我意识到这个数据集有点乱。所以我重复了类似的步骤来清洁每根柱子。

  • “yr_listened”栏

清理“yrs _ listened”列的过程

我将只向您展示每一列之前和之后的图片。

  • “每日新闻人力资源”栏目

《每日 _ 音乐 _hr》前后

  • “年度支出”栏

“yr _ merch _ spent”之前和之后

  • “年龄”栏

前后“年龄”

  • “收藏组”栏

原始列值

制作一个单独的列来查找每个人喜欢的组的数量

BTS 与其他的单独列

  • “nes_medium”列

原始列值

简化的列值

  • “追求”栏目

原始列值

简化的列值

  • “时间常数”栏

原始列值

简化的列值

  • “生活 _ 变化”栏

原始列值

简化的列值

  • “pos_eff”列

原始列值

简化的列值

  • “money_src”列

原始列值

简化的列值

  • “疯狂 _ev”专栏

原始列值

简化的列值

  • “国家”栏

原始列值

简化的列值

终于清理完数据了!

我将清理后的数据框保存到一个 CSV 文件中,以供教程的下一部分使用。

将清理后的数据帧保存到 CSV

在第 2 部分,我将讨论本教程的探索性数据分析部分。敬请期待!

使用机器学习分析韩国流行音乐|第 2 部分—探索性数据分析(EDA)

原文:https://towardsdatascience.com/analyzing-k-pop-using-machine-learning-part-2-exploratory-data-analysis-eda-61f0cbf95a2a?source=collection_archive---------62-----------------------

K-POP 机器学习教程系列

这是教程的第二部分,我从变量中发现了有趣的见解。

UnsplashSaveliy Bobov 拍摄的照片

带解释的视频版本

第一部分—本教程的数据收集和数据清理可以在 这里找到

现在让我们做一些探索性的数据分析这个项目的一部分!

我的完整代码可以在 这里 找到。

正在完成数据清理

在上一个教程中,我意识到我忘记了清理三个列(变量)-“性别 _ 偏好”,“原因”和“工作”,所以我很快先清理了它们。

  • 对于“gender_pref”,我把它们重新贴上“男性”、“女性”、“两者皆有”、“是关于音乐”的标签,以简化它们。
  • 对于“原因”,我按照“独特的音乐风格”、“独特的编舞”、“有魅力的偶像”、“很多原因”、“其他原因”来分类。
  • 对于“工作”,我把他们分为“学生”、“全职工人”和“失业者”

分析连续变量

首先,我检查连续变量的描述——“yr _ listened”、“daily_music_hr”、“daily_MV_hr”、“yr _ merch _ spent”、“age”、“num_gr_like”。

检查连续变量的描述

连续变量的描述

我们可以看到,粉丝平均年龄为 18 岁,他们听 k-pop 的时间大约为 3 年。他们每天花 4.3 小时听 k-pop,花 1.95 小时看 k-pop 音乐视频。他们平均在韩国流行商品上花费 85 美元。

检查连续变量的直方图

  • 您可以看到由于一些异常值,“yr_listened”变量的分布有点向右倾斜。

“yr_listened”直方图

  • “每日 _ 音乐 _hr”正态分布

“每日 _ 音乐 _ 小时”直方图

  • 《daily_MV_hr》有点右倾

“每日 MV 小时”直方图

  • “年销售额”接近正态分布

“年销售额”直方图

  • “年龄”呈正态分布

“年龄”直方图

  • “num_gr_like”是右偏的

“数量类”直方图

检查箱线图以检测异常值

绘制箱线图可以帮助您检测异常值。

  • 我们看到“yr_listened”和“yr _ merch _ spent”中有一些异常值。

“每日音乐小时”、“每日音乐小时”、“年听音乐小时”和“年消费”的方框图

  • “num_gr_like”有很多离群值

“数量 _ 类别”的箱线图

移除异常值并再次检查分布

让我们从“yr_listened”和“num_gr_like”中删除异常值,并检查直方图,看看分布中是否有任何变化。

“yr_listened”和“num_gr_like”的直方图

我们可以明确的看到“yr_listened”现在是正态分布,而“num_gr_like”不是。太多的人只喜欢 1 或 2 组,所以去除离群值不会对分布产生太大影响。

检查连续变量之间的相关性

检查相关性很重要,因为我们想知道哪些变量是相关的。在模型构建过程中,我们不希望出现多重共线性——当自变量高度线性相关时。这可能会在拟合回归模型时导致问题。

修复多重共线性的方法有哪些?欢迎在下方的评论区回答这个问题!

检查关联矩阵和关联热图

根据相关矩阵,我们看到连续变量之间不存在多重共线性——没有相关性接近于 0。

我们可以看到这些关系:

  • 他们听 k-pop 的年数与他们听 k-pop 的小时数、他们在商品上的花费和年龄正相关。
  • k-pop 粉丝看 k-pop youtube 音乐视频的小时数与听 k-pop 的小时数呈正相关。
  • 他们花在听 k-pop 上的时间越多,他们花在购买 k-pop 商品上的钱就越多。
  • 他们看的 k-pop youtube 视频越多,听的 k-pop 越多,他们喜欢的组合就越多。
  • 年龄越小,花在听 k-pop 和看 k-pop 视频上的时间越多。
  • 年龄与他们每年花多少钱购买 k-pop 商品无关。

分析分类变量

现在,让我们分析分类变量。

制作分类变量的数据框架

条形图

让我们绘制柱状图来展示分类变量。以下是一些发现。

  • BTS 显然是最著名的团体

收藏夹组的条形图

  • 韩国流行音乐在许多国家越来越受欢迎

k-pop 流行度条形图

  • 很多粉丝喜欢 K-pop 是多重原因,其次是“独特的音乐风格”。不是很多人听 K-pop 只是因为偶像的外表。

原因条形图

  • 很多粉丝既喜欢男团也喜欢女团,其次是重男轻女和“是关于音乐的”。没有多少人只喜欢女团。

性别偏好条形图

  • 240 人中大约有 90 人因为喜欢韩国流行音乐而被取笑。大约 70 人说他们的睡眠时间减少了。

生活变化条形图

  • 超过 120 人说他们通过听 k-pop 减轻了压力/焦虑/抑郁。这对我来说非常有趣,因为我认为鉴于很多人被取笑,不会有很多积极的影响。大约 80 人通过韩国流行音乐交了朋友。

正面效果条形图

  • 有很多美国 k-pop 粉丝,其次是英国、其他欧洲国家和加拿大。

国家条形图

使用数据透视表查找关系

让我们也用数据透视表找出变量之间的一些关系。

  • 听 k-pop 和看 k-pop YouTube 视频有助于粉丝减轻压力,并帮助他们结交更多朋友

听/看 kpop 与积极效果的关系

  • 大部分钱都花在了购买音乐会门票上

去听音乐会和他们花在韩国流行商品上的钱之间的关系

  • 年长的 K-pop 粉丝(24 岁左右)因为喜欢 K-pop 而被取笑。年轻的 K-pop 粉丝通过 K-pop 交朋友。

年龄与积极效果和生活变化的关系

就是这样!当然,在数据科学周期的探索性数据分析部分,您总是可以更深入地找到更多见解。一旦你很好地理解了你想在模型构建过程中做什么,那么你就可以在那里停下来。

我在我的代码中有更多的发现,所以如果你想检查一下,请到我的 GitHub 链接。

感谢您的阅读!

使用机器学习分析韩国流行音乐|第 3 部分—建模

原文:https://towardsdatascience.com/analyzing-k-pop-using-machine-learning-part-3-model-building-c19149964a22?source=collection_archive---------83-----------------------

K-POP 机器学习教程系列

这是本教程的第 3 部分,我构建了不同的预测模型并比较了结果。

萨维里·博博夫Unsplash 上的照片

可以在这里 找到之前的教程

注意:你可以在这篇文章的底部找到我的全部代码的链接。

现在让我们做一些模型建设!

对数据框进行子集划分,并将分类变量转换为虚拟变量

对于模型构建,我删除了“fav_grp”列,因为我们在探索性数据分析中看到有太多的组,而 BTS 是主导组。

df_model = df[['popl_by_co_yn', 'reason', 'yr_listened',     'gender_pref','daily_music_hr', 'watch_MV_yn', 'daily_MV_hr', 'obsessed_yn','news_medium', 'pursuit', 'time_cons_yn', 'life_chg', 'pos_eff','yr_merch_spent', 'money_src', 'concert_yn', 'crazy_ev', 'age','country', 'job', 'gender', 'num_gr_like', 'bts_vs_others']]

然后,我得到虚拟数据,将分类变量转换为回归模型的虚拟/指示变量。

获取虚拟数据以转换分类变量

训练和测试分割

主要目标是使用其他独立变量预测“每日音乐小时数”——K-pop 粉丝听 K-pop 的小时数。

设 X 为除“每日 _ 音乐 _hr”之外的所有其他变量,设 Y 为“每日 _ 音乐 _hr”。然后我们用 80%作为训练集,剩下的 20%作为测试集。

多元线性回归

由于我们有一个小数据集(只有 240 行),我们希望避免使用复杂的模型。所以我们从多元线性回归开始。

from sklearn.linear_model import LinearRegressionfrom sklearn.metrics import mean_absolute_error# initialize the linear regression modellm = LinearRegression()# train the modellm.fit(X_train, y_train)# perform predicion on the test datay_pred = lm.predict(X_test)# performance metricsprint('Coefficients:', lm.coef_)print('Intercept:', lm.intercept_)print('Mean absolute error (MAE): %.2f' % mean_absolute_error(y_test, y_pred))

对于指标,我们将使用 MAE(平均绝对误差)来检查模型的准确性。

多元线性回归的系数和平均误差

多元线性回归(MLR)模型的 MAE 为 2.17。这意味着平均来说,我们的预测有 2.17 小时的误差。由于 K-pop 粉丝听 K-pop 的小时数从 0 到 10 不等,这是相当合理的。但是让我们看看我们是否能做得更好。

在同一个多元线性回归模型上,我们将应用 10 重交叉验证来概括数据。10 折交叉验证的工作原理是这样的——它在数据中创建了 10 个组,留下 1 个组进行验证,并使用剩余的 9 个组进行训练。最终,它创造了 10 种不同的 Mae。

然后我们取它们的平均值,得到一个单一的 MAE-1.98。

我们可以看到比上面的稍微好一点。

使用 10 重交叉验证的 MLR 的 MAE

套索回归

构建模型时处理小数据的另一种方法是使用正则化模型。Lasso ( LAb soluteShrinkage 和SelectionOoperator)使用收缩(alpha)。收缩是指数据值向中心点收缩,如平均值。

它应用 L1 正则化,这增加了等于系数幅度绝对值的惩罚。

套索的 MAE 是 1.58。

拉索回归的 MAE

我们也可以尝试寻找最优的 alpha 来找到最佳的套索模型。

我们看到最佳 alpha 值是 0.09,MAE 现在稍微好一点,为 1.57。

寻找套索回归的最佳α

里脊回归

与 Lasso 类似,岭回归也增加了惩罚。它使用 L2 正则化。与 Lasso 回归的唯一区别是,它使用系数的平方大小,而不是绝对值。

岭回归的 MAE 为 1.85,与 lasso 相比并不算大。

岭回归的 MAE

我们也可以尝试找到最佳收缩参数,但根据图,看起来我们已经有了最佳收缩参数。

看我们是否能找到最佳收缩率

随机森林回归量

基于树的模型可以是好的模型,只要它们不太深。

我们可以看到 RF 的 MAE 为 1.61。

随机森林回归的 MAE

我们还可以尝试调整随机森林模型的超参数。使用 GridsearchCV 是调优参数的好方法。

下面是调整随机回归参数的一种方法。

from sklearn.model_selection import GridSearchCVparams = {'n_estimators':range(10,100,10), 
          'criterion':('mse','mae'), 
          'max_features':('auto','sqrt','log2')}gs_rf = GridSearchCV(rf, params,
                     scoring = 'neg_mean_absolute_error', cv = 10)gs_rf.fit(X_train, y_train)

使用最佳估计量,最佳 MAE 为 1.51。

调谐随机森林

XGBoost

另一个基于树的模型是 XGBoost。

XGBoost 的 MAE 为 1.54。

XGBoost 的 MAE

我们还可以尝试调整超参数,就像我们对随机森林模型所做的那样。

params = {'min_child_weight': [3, 5, ], 
          'gamma': [0.5, 1], 
          'subsample': [0.8, 1.0],
          'colsample_bytree': [0.6, 0.8], 
          'max_depth': [1,2]}gs_xgb = GridSearchCV(xgb, params,
                      scoring = 'neg_mean_absolute_error', cv = 10)gs_xgb.fit(X_train, y_train)

调优后的 XGBoost 的 MAE 为 1.33。

调优的 XGBoost

比较所有型号的性能

作为本教程的总结,我们将比较我们构建的所有模型的性能。

lm_pred = lm.predict(X_test)lm_las_pred = lm_las.predict(X_test)lm_rid_pred = lm_rid.predict(X_test)rf_pred = gs_rf.best_estimator_.predict(X_test)xgb_pred = gs_xgb.best_estimator_.predict(X_test)

比较模型性能

我们看到 XGBoost 是最好的模型!平均而言,预测误差为 1.23 小时。

当然,你可以花几天时间去寻找“最好”的模型,但同时,我们也希望有效率。

感谢您的阅读!接下来的教程,我要讲的是模型制作!

我的全码就是这里的

使用机器学习分析 K-Pop |第 4 部分—生产模型(模型部署)

原文:https://towardsdatascience.com/analyzing-k-pop-using-machine-learning-part-4-productionizing-the-model-model-deployment-a9fc2e703d95?source=collection_archive---------58-----------------------

K-POP 机器学习教程系列

这是教程的第 4 部分,我使用 FLASK 将预测模型投入生产。

萨维里·博博夫Unsplash 上的照片

可以在这里 找到之前的教程

视频版本

注意:你可以在这篇文章的底部找到整个 GitHub 库的链接。

在本教程中,我将向您展示如何将一个模型投入生产(又名。模型部署)。

什么是模型部署?模型部署是将机器学习模型集成到现有的生产环境中,以根据数据做出实际的业务决策。

我们将使用 Python web API 和 FLASK 来部署模型。因此,我们的最终目标是创建一个网站,一旦用户输入了输入值,它就会向您提供预测结果。

从 GitHub 下载我的文件

首先,去我的 GitHub 页面上的 K-Pop 库下载模型部署文件夹

我们将使用名为 GitZip 的网站,该网站允许您下载回购中的特定文件夹。您所需要做的就是将链接复制并粘贴到我的模型部署文件夹中。

在这里复制并粘贴我的文件夹链接

您可以随意命名文件夹。我将把我的命名为“K-Pop 模型部署”

使用 Spyder IDE

在本教程中,我们将使用 Spyder IDE。

如果你还没有安装,你可以从这里下载(你需要从 Anaconda 网站下载)。请务必下载 3.7 版本,因为这是最新版本。

安装 Anaconda (Python 3.7)

安装后,打开 Spyder IDE,导航到“文件资源管理器”并选择您刚刚下载的文件夹。

打开模板文件夹下的 app.py、k_pop_model_building.py 和 index.html。

Spyder 的文件浏览器

仅选择连续变量

在上一篇教程中,我们使用. pd.get_dummies(df_model)将分类变量转换为虚拟/指示变量。我意识到这产生了太多额外的变量,我认为这不是很用户友好(我们不想让用户输入 73 个答案)。因此,我们将只选择连续变量—这样用户只需输入五个变量(“yr_listened”、“daily_MV_hr”、“yr _ merch _ spent”、“age”、“num_gr_like”)来预测“daily _ music _ HR”—他们每天听 K-pop 的小时数。

df_real = df[[“yr_listened”, “daily_music_hr”, “daily_MV_hr”, 
              “yr_merch_spent”, “age”, “num_gr_like”]]

然后,运行列车并再次测试分离。

from sklearn.model_selection import train_test_splitX = df_real.drop('daily_music_hr', axis = 1)
y = df_real.daily_music_hr.valuesX_train, X_test, y_train, y_test = train_test_split(X, y, 
                                   test_size = 0.2, 
                                   random_state = 1)

运行 XGBoost 模型

从上一个教程中,我们看到 XGBoost 模型是最好的一个。因此,我们将部署这种模式。

import xgboost as xgb# initialize the linear regression model
xgb_clf = xgb.sklearn.XGBClassifier(nthread = -1, seed = 1)# train the model
xgb_clf.fit(X_train, y_train)# Tune XGBoost using GridSearchCVfrom sklearn.model_selection import GridSearchCVparams = {'min_child_weight': [5], 'gamma': [1], 
          'subsample': [0.8, 1.0],
          'colsample_bytree': [0.6, 0.8], 
          'max_depth': [1,2]}gs_xgb = GridSearchCV(xgb_clf, params ,
                      scoring = 'neg_mean_absolute_error', 
                      cv = 10)gs_xgb.fit(X_train, y_train)gs_xgb.best_score_xgb_best = gs_xgb.best_estimator_
xgb_bestxgb_best.fit(X_train, y_train)

保存已训练的模型

我们可以使用 pickle 将训练好的模型保存到磁盘上。它可以在以后重新加载,就像我们训练过的一样使用。

# save the model to disk
with open('model.pkl', 'wb') as file:
    pickle.dump(xgb_best, file)

使用 FLASK 创建 Web 应用程序

首先,我们需要这两样东西来创建一个 web 应用程序。

  1. Python 脚本将加载训练好的模型,要求用户将输入值放在网站上,执行预测,并返回结果。
  2. HTML 模板是网站的格式。这将允许用户输入他们的数据,并将呈现结果。

其结构如下所示:

web app/
index.html├──model/
│└──model . pkl—训练好的模型
├──模板/
│└──—网站格式
└── app.py —托管模型

创建 app.py 来托管模型

app.py 将成为网络应用的主干。它会发送网页,从用户那里获取数据来进行预测。

# use flask to host the modelimport flask
import pickle
import pandas as pd# Use pickle to load in the pre-trained model
with open(f'model.pkl', 'rb') as f:
    model = pickle.load(f)# initialize the flask app
app = flask.Flask(__name__, template_folder='templates')# set up the main route
[@app](http://twitter.com/app).route('/', methods=['GET', 'POST'])
def main():
    if flask.request.method == 'GET':
        # rendering the initial form, to get input
        return(flask.render_template('index.html'))

    if flask.request.method == 'POST':
        # extracting the input values
        yr_listened = flask.request.form['yr_listened']
        daily_MV_hr = flask.request.form['daily_MV_hr']
        yr_merch_spent = flask.request.form['yr_merch_spent']
        age = flask.request.form['age']
        num_gr_like = flask.request.form['num_gr_like']

        # making dataframe for model
        input_variables = pd.DataFrame([[yr_listened, daily_MV_hr, yr_merch_spent, age, num_gr_like]],
                                       columns=['yr_listened', 'daily_MV_hr', 'yr_merch_spent', 'age', 'num_gr_like'],
                                       dtype=float,
                                       index=['input'])

        # get the model's prediction
        prediction = model.predict(input_variables)[0]
        output = float(round(prediction, 2))

        # render the form again, but add in the prediction and remind user of the values they input before
        return flask.render_template('index.html',
                                     original_input={'yr_listened':yr_listened,
                                                     'daily_MV_hr':daily_MV_hr,
                                                     'yr_merch_spent':yr_merch_spent,
                                                     'age':age,
                                                     'num_gr_like':num_gr_like},
                                     result=float(output)
                                     )

if __name__ == "__main__":
    app.run(debug=True)

创建 index.html 来格式化我们的网站

这是这个项目的前端部分。它将要求用户输入值,执行预测,并给我们输出。这是很基础的风格。我试着玩 CSS,但无法真正让它工作。如果你了解 CSS 或者想尝试一下样式,请随意!

<!doctype html>
<html>
<style>
form {
    margin: auto;
    width: 35%;
}.result {
    margin: auto;
    width: 35%;
    border: 1px solid #ccc;
}
</style><head>
    <title>Predicting Daily K-Pop Listening Hours</title>
</head>
<form action="{{ url_for('main') }}" method="POST">
    <fieldset>
        <legend>Input values:</legend>
        Number of years you listened to K-Pop:
        <input name="yr_listened" type="number" step=".01" required>
        <br>
        <br> Number of hours you watch K-Pop MV per day:
        <input name="daily_MV_hr" type="number" step=".01" required>
        <br>
        <br> How much money you spend on K-Pop merchandise a year:
        <input name="yr_merch_spent" type="number" step=".01" required>
        <br>
        <br> Your age:
        <input name="age" type="number" step=".01" required>
        <br>
        <br> Number of groups you like:
        <input name="num_gr_like" type="number" step=".01" required>
        <button type="submit" class="btn btn-primary btn-block btn-large">Predict!</button>
    </fieldset>
</form>
<br>
<div class="result" align="center">
    {% if result %}
     {% for variable, value in original_input.items() %}
      <b>{{ variable }}</b> : {{ value }}
     {% endfor %}
  <br>
     <br> Predicted Daily K-Pop Listening Hours:
     <p style="font-size:50px" step=".01">{{ result }}</p>
    {% endif %}
</div></html>

运行 Web 应用程序

现在我们终于可以测试看看是否一切都按照我们想要的方式工作了!

  1. 转到 Anaconda 提示符
  2. 将目录更改为您的工作文件夹(即 cd 桌面-> cd K-Pop 模型部署)
  3. 运行 app.py(即 python app.py)
  4. 将你得到的链接复制并粘贴到浏览器上
  5. 输入值,检查它是否给出了预测

Anaconda 提示符命令示例

cd Desktop
cd K-Pop Model Deployment
python app.py

工作 Web 应用程序

我们完了。希望你觉得这个教程有用!

在下一个教程中,我将向你展示如何通过创建一个作品集网站来记录这个项目!敬请关注。

我的 GitHub 库就是这里的

使用机器学习分析 K-Pop |第 5 部分— GitHub 文档和作品集网站

原文:https://towardsdatascience.com/analyzing-k-pop-using-machine-learning-part-5-github-documentation-portfolio-website-a88afd8ff2c?source=collection_archive---------55-----------------------

K-POP 机器学习教程系列

这是本教程的最后一部分,我将展示如何在 GitHub 上记录您的工作,以及如何使用 GitHub 托管一个简单的作品集网站。

萨维里·博博夫Unsplash 上的照片

可以在这里 找到之前的教程

视频版本

注意:你可以在这篇文章的底部找到整个 GitHub 库的链接。

我想通过在 GitHub 上记录它并使用 GitHub 创建一个简单的作品集网站来结束这篇教程。特别感谢 Ken Jee 提供的关于如何创建简单投资组合网站的视频

在 K-Pop 回购中增加概述部分

Overview
- Created a web application that returns the predicted number of hours one listens to K-Pop on a daily basis (MAE ~ 1.2 hours).
- Engineered features from the text of each column.
- Explored the data to analyze the relationships among the features.
- Built five different regression models — linear, lasso, ridge, random forest, and XGBoost.
- Optimized the random forest and the XGBoost models using GridSearchCV to find the optimal parameters.

为投资组合网站创建回购

创建新的存储库

存储库->新建->用 README.md 初始化

我把我的命名为“ds-简单-投资组合”。

在 README.md 文件中添加内容

我将为 K-Pop 分析项目添加如下描述。

我还将在这里添加一些图像——一个用于 web 应用程序的图像和一些用于探索性数据分析的图像。

在 README.md 文件中添加完内容后,请转到“设置”。

向下滚动到“GitHub Pages”,打开“Source”下的“Master Branch”。

一旦你点击它,它会给你一个网址,这是你的工作组合网站!

你也可以选择你喜欢的主题。

最终作品集网站

感谢您在本教程中调谐!向所有晋级到最后一轮的人致敬。我个人很享受这个旅程,因为我学到了以前没有用过的技术。我学会了如何使用 GridSearchCV 寻找最佳参数,还学会了如何部署模型和创建 web 应用程序。

完成了简单的作品集网站

我的 GitHub 库就是这里的

分析 MacBook GPU 性能;使用 MLflow 和弹性 Kibana 进行可视化

原文:https://towardsdatascience.com/analyzing-macbook-gpu-performance-visualizing-with-mlflow-and-elastic-kibana-40e7bc494260?source=collection_archive---------43-----------------------

使用 AMD GPU、英特尔 UHD 和英特尔 CPU 加速深度学习的各种 MacBook PlaidML 配置的比较;用 Databricks MLflow 和 Elastic Kibana 可视化。

[## plaidml/plaidml

首先,我们要感谢您选择 PlaidML。无论您是新用户还是多年的老用户,我们都非常…

github.com](https://github.com/plaidml/plaidml)

一名观众对我的 YouTube 视频发表了评论,该视频展示了如何在 MacBook 上为 Keras 启用 AMD GPU 加速。

非常正确

评论指出我选择的 OpenCL 库不如 Metal 库性能好。这激起了我的好奇心。金属永远比 OpenCL 快吗?快了多少?AMD GPU 比英特尔 UHD 和英特尔 CPU 快多少?我 MacBook 中的三个计算设备是:

  1. 镭龙 Pro 560X 4 GB
  2. 英特尔 UHD 显卡 630 1536 MB
  3. 2.3 GHz 英特尔酷睿 i9,16 个线程,32 GB 主内存

TL;速度三角形定位法(dead reckoning)

DavidVilla147BVB 懂他的东西!在 MacBook 上加速深度学习,Metal 库绝对比 OpenCL 库快。在下图中,Y 轴是网络架构,从下到上依次为:IMDB_LSTM、MobileNet、NasNet Mobile、VGG16、VGG19。X 轴从左到右是以下计算配置:英特尔 CPU、OpenCL-UHD、金属 UHD、OpenCL-GPU、金属 GPU。

Y =网络架构;X =设备/库

一位同事最近提出了 MFflow,这个小实验似乎是检验它的完美借口。在这个实验中,我发现 PlaidML 有自己的一套称为 Plaidbench 的性能测试。Plaidbench 将其结果写成 JSON。拥有一堆需要分析的 JSON 文档引发了我对 Elastic 和 Kibana 的思考。以下是对 MFLow 和 Elastic 的 Kibana 的分析和可视化。我的设置如下。

PlaidML 与 Python 虚拟环境(venv)

我的第一步是用 pip 安装 PlaidML。最近,我一直在为新项目创建 python 虚拟环境,并将它们全部放在 NFS 的一个位置,以便在任何计算机上使用。以前,我只是在我的 Conda 环境或默认环境中运行。如果我感觉像青蛙,我会用 Docker 容器…我想这是我应该一直做的。

PlaidML venv & pip(与 asciinema.org 一起录制)

PlaidML 安装脚本

PlaidML 有一个简单的设置脚本,可以创建一个 JSON 文件,~/.plaidml。

要尝试所有可能的配置,而不仅仅是 AMD 的 Metal,您必须复制~/。plaidml 文件到另一个位置。否则,您只会覆盖之前的~/。plaidml 文件。我选择将该文件复制到一个更具描述性的文件名中,以指示其内容,然后为我想要测试的每个配置重新运行‘plaidml-setup’脚本。这看起来是这样的:

然后,对于每个试验,我将所需的测试配置文件复制到~/.plaidml 中。例如,要使用 OpenCL 测试英特尔 UHD,我会将“opencl_uhd.json”复制到“~/”。因为这是 Plaidbench 期望这个配置文件所在的位置。我可以修改代码,但我发现交换文件很容易。

数据块 MLflow

[## MLflow —机器学习生命周期的平台

机器学习生命周期的开源平台 MLflow 是一个管理机器学习生命周期的开源平台

mlflow.org](https://mlflow.org/)

mlflow

我一直在寻找一个借口来测试 MLflow,这似乎是一个完美的用例。我运行了许多 ML 试验,这个开源软件很容易安装、使用和探索它的 web UI。

MLflow 安装和 UI 启动

UI 运行后,可以通过本地主机上的浏览器在端口 5000 上访问它:http://locah host:5000

运行我的试验

现在,我已经准备好开始开发和运行我的基准代码了。由于 PlaidML 库附带了一个基准测试系统,所以只需要用正确的计算机硬件和库调用基准测试系统。使用的五种硬件和软件加速器配置是:

  1. 带 AMD 的金属
  2. 带 AMD 的 OpenCL
  3. 金属带英特尔 UHD
  4. OpenCL 与英特尔 UHD 合作
  5. 英特尔 CPU

我选择了五种网络架构进行测试,以便在合理的时间内完成测试。这些网络是:

  1. MobileNet
  2. 纳斯特移动
  3. IMDB
  4. VGG16
  5. VGG19

由于每个基准测试都调用 PlaidML 中的' exit()',所以我创建了一个 bash 脚本来重复调用我的 Python 脚本。Python 脚本验证试验尚未运行,然后运行试验。对于这样一个简单的项目,在 MLflow 中调用这三个基本函数。

  1. mlflow.start_run() #表示单次试验的开始
  2. ml flow . log _ param(' param _ name ',value) #零到多个您希望跟踪的参数,~个独立值
  3. ml flow . log _ metric(' metric _ name ',value) #要分析的结果,~相关值

trial.py(已创建 w/ carbon.now.sh )

trial_driver.sh

MLflow 中的结果

我检查了 MLflow 中的原始数据,然后深入研究了网络架构。我发现用简单的查询语法深入数据很容易,但是我发现绘图系统有点初级。为了深入研究 IMDB LSTM 网络的结果,我简单地用params . network = ' IMDB _ lstm '进行了查询,然后进行了基本的绘图。这里是一个快速查看一对夫妇的图表明,AMD 的金属库 GPU 确实是最快的设置。

情节 1

情节 2

这里有一个动画 GIF 演示如何创建前两个情节。

mlflow 演示

MLflow 摘要

MLflow 是一个不错的工具。我喜欢它入门的简单性,简单的 API,以及漂亮而简单的 web UI。我认为 Databricks 最终会把它建成更有价值的东西。最重要的是,我喜欢它是开源的,而不是像 Spark 一样绑定(或锁定)在一个单一的技术上。我也喜欢它有一个 Python API。

基巴纳的结果

[## 基巴纳

刚到基巴纳?这是你开始工作所需要的一切。观看视频,了解使用 Kibana 进行数据分析的核心概念…

www.elastic.co](https://www.elastic.co/kibana)

这里是我的 Kibana 仪表板的一个演示,展示了 plaidbench 设置的各种总执行时间。

基巴纳设置

要让 Kibana 快速运行,您可以运行一个 ElasticSearch Docker 映像,然后运行一个 Kibana Docker 映像。一旦您的容器运行,几行 Python 代码将索引所有基准 JSON 文件。

在 Docker 中创建 ElasticSearch + Kibana“集群”

用 Python 索引原始数据

要将数据输入 ElasticSearch,你需要一个“索引”然后这个索引需要一个“映射”我认为映射是索引数据的模式。Elastic 的一个很好的特性是你可以简单地开始索引数据,Elastic 会为你创建一个默认的索引和映射。这就是你搜索所需要的一切。除了搜索,还需要做更多的工作。在我的例子中,我想用 Kibana 进行可视化分析,所以我为我的数据创建了一个自定义映射。

Elastic 在所有主要语言中都实现了一个干净的 API,或者你可以使用他们各种*Stash 应用程序中的一个来代替编码。我用 Python 做了我的自定义映射。我的代码根据值是否可以转换为数字来自动创建我的映射(即模式)。如果没有,我将字段类型设置为“keyword”而不是“text ”,这样我就可以在 Kibana 中执行聚合。

我的自动映射 Python 代码

Python 中的自动弹性映射

我的弹性索引代码

Python 弹性索引

基巴纳可视化

在我索引我的代码之后,我构建了我的 Kibana 仪表板。

这一部分总是令人愉快的。我还没有从 Kibana 的“镜头”功能中得到任何东西,但公平地说,它仍处于测试阶段。各个可视化效果是同步的,这样,如果您深入查看一个小部件,整个仪表板都会更新。您还可以添加一个客户小部件,让查看者能够执行深入分析。选择您的各种范围和过滤器并单击“应用更改”按钮将更新整个仪表板。

自定义深入小部件

自定义小部件的演示

摘要

MLflow 有潜力,但我认为需要以简单性为代价增加一点功能。在我的下一篇文章中,我将分享如何在 Kibana 中创建一个仪表板。不难,但和大多数事情一样,细节的微调也不简单。但大多数情况下,如果你试图在 Mac 或 MacBook 上加速你的深度学习,请始终使用金属库。谢谢 DavidVilla147BVB。

用 Python 分析医疗保险数据

原文:https://towardsdatascience.com/analyzing-medicare-data-in-python-9417eaa140a6?source=collection_archive---------41-----------------------

使用 BigQuery 在 Python 中提取和分析医疗保险数据

照片由像素上的 Pixabay 拍摄

医疗保险是为 65 岁及以上的美国人提供的单一付款人国家社会健康保险计划。它还包括一些有残疾状况的年轻人、ALS 患者和终末期肾病患者。医疗保险和医疗补助服务中心向公众提供医疗保险数据。可以通过 Google BigQuery 访问数据。

您可以使用数据集来回答以下问题:

  1. 每个州开出的药物总数是多少?
  2. 每个州最常开的药物是什么?
  3. 每个城市和州的住院和门诊治疗的平均费用是多少?
  4. 在美国,哪些是最常见的住院诊断疾病?
  5. 对于每种诊断条件,哪些城市的病例数最多?
  6. 这些城市在这些条件下的平均费用是多少,与全国平均水平相比如何?

在本帖中,我们将对医疗保险数据库中的数据进行一些简单的探索性数据分析。虽然我们不会回答上面所有的问题,但这篇文章将为感兴趣的读者提供一个良好的开端。

首先,要访问数据,你需要创建一个谷歌云账户。接下来,您需要安装 Google BigQuery 并生成认证凭证。你可以在这里找到操作说明

我们开始吧!

首先,让我们导入必要的 google 身份验证和 BigQuery 包:

from google.oauth2 import service_account
from google.cloud import bigquery

接下来,我们需要导入熊猫并将列设置定义为“None”。我们这样做是为了显示所有的列,因为 pandas 默认情况下会截断它们。

import pandas as pd
pd.set_option("display.max_columns", None)

接下来,我们定义密钥路径、客户端凭证和客户端对象:

key_path = "key_path/key.json"credentials = service_account.Credentials.from_service_account_file(
    key_path,
    scopes=["[https://www.googleapis.com/auth/cloud-platform](https://www.googleapis.com/auth/cloud-platform)"],
)client = bigquery.Client(
    credentials=credentials,
    project=credentials.project_id,
)

接下来,我们定义数据集引用,其中我们传入数据库的名称“medicare”和项目的名称“bigquery-public-data”:

dataset_ref = client.dataset("medicare", project="bigquery-public-data")

我们可以看看 12 个可用的表:

columns = [x.table_id for x in client.list_tables(dataset_ref)]
print(columns)

让我们来看看列表中的第一个元素,“住院 _ 费用 _2011”:

table_ref = dataset_ref.table('inpatient_charges_2011')
table = client.get_table(table_ref)

我们可以将结果存储在数据帧中,并打印列名:

df = client.list_rows(table).to_dataframe()
print(df.columns)

我们还可以查看数据帧的长度:

print(len(df))

我们还可以打印数据帧的前五行:

print(df.head())

我们可以从 python 中的 collections 模块导入 counter 方法来分析一些分类变量的分布。让我们看看“提供商名称”列:

from collections import Counter
print(dict(Counter(df['provider_name'].values).most_common(20)))

我们也可以将这些输出可视化。让我们定义一个函数,它将类别字符串作为输入,并显示四个最常见值的条形图:

import matplotlib.pyplot as plt
import seaborn as sns
def plot_most_common(category):
    sns.set()
    bar_plot = dict(Counter(df[category].values).most_common(4))
    plt.bar(*zip(*bar_plot.items()))
    plt.show()

现在让我们用“provider_name”调用函数:

plot_most_common('provider_name')

我们还可以看看“drg_description”的四个最常见的值:

plot_most_common('drg_description')

我们还可以定义一个函数来生成跨分类值的箱线图。我们使用箱线图来显示基于最小值、最大值、中值、第一个四分位数和第三个四分位数的数值分布。如果你对它们不熟悉,可以看看文章了解盒子情节

该函数采用数据框、分类列和数值列,并根据限制显示最常见类别的箱线图:

def get_boxplot_of_categories(data_frame, categorical_column, numerical_column, limit):
    keys = []
    for i in dict(Counter(df[categorical_column].values).most_common(limit)):
        keys.append(i)
    print(keys)

    df_new = df[df[categorical_column].isin(keys)]
    sns.boxplot(x = df_new[categorical_column], y = df_new[numerical_column])

让我们为 5 个最常出现的提供者名称中的平均 medicare 付款生成箱线图:

get_boxplot_of_categories(df, 'provider_name', 'average_medicare_payments', 5)

我们可以为“总排放量”生成一个类似的曲线图:

get_boxplot_of_categories(df, 'provider_name', 'total_discharges', 5)

我们还可以构建一个散点图函数。此函数将一个数据框、分类列、分类值和两个数字列作为输入,并显示一个散点图:

def get_scatter_plot_category(data_frame, categorical_column, categorical_value, numerical_column_one, numerical_column_two):
    import matplotlib.pyplot as plt
    import seaborn as snsdf_new = data_frame[data_frame[categorical_column] == categorical_value]
    sns.set()
    plt.scatter(x= df_new[numerical_column_one], y = df_new[numerical_column_two])
    plt.xlabel(numerical_column_one)
    plt.ylabel(numerical_column_two)

让我们为“好撒马利亚人医院”生成一个平均医疗保险付款与总出院人数的散点图:

get_scatter_plot_category(df, 'provider_name', 'GOOD SAMARITAN HOSPITAL', 'average_medicare_payments', 'total_discharges')

我们也可以看看“北岸医疗中心”的同一个图:

get_scatter_plot_category(df, 'provider_name', 'NORTH SHORE MEDICAL CENTER', 'average_medicare_payments', 'total_discharges')

概括地说,在这篇文章中,我对医疗保险数据集进行了简单的探索性数据分析。我分析了提供者名称的分类值的分布。我还生成了条形图,用于可视化数据集中提供者名称的出现频率。最后,我定义了用于生成跨类别值的箱线图和可视化数值散点图的函数。我希望这篇文章有趣。

这篇文章的代码可以在 GitHub 上找到。感谢您的阅读!

分析微生物组数据

原文:https://towardsdatascience.com/analyzing-microbiome-data-320728b56b8e?source=collection_archive---------52-----------------------

来源: DNA 壁纸洞穴

第 1 部分—基因组数据科学系列

本文是将机器学习应用于生物信息学数据的系列教程的一部分:

第 1 部分—数据采集和预处理

第二部分——可视化高维数据

为了跟进,你可以下载我们的 Jupyter 笔记本,或者继续阅读并输入下面的代码。

介绍

我们许多人大部分时间都在室内度过,但很少有人意识到我们接触的每一个表面都是寄居在这些空间的特殊有机体的家园——门把手、厨房柜台、桌子、椅子、浴室、冰箱等。当我们想到不同的生态系统,如沙漠、雨林、草原或珊瑚礁时,我们知道不同的生物群落专门生活在世界上的这些地区。同样,我们可以推断不同组成或不同类群的微生物和真菌可能专门生活在我们周围的不同位置或表面。我们称这些特殊的微生物群落为 微生物群

来源:2012 年隐形世界展览,地理

如果我们只知道这些生物群落之间的基因差异会怎么样?我们也许可以逆向工程这些生物体的来源?在接下来的系列教程中,我们将向您展示我们如何从简单表面拭子的基因序列数据出发,在事先不知道拭子来源位置的情况下,使用一些数据科学检测工作,找出如何区分这些拭子的来源位置。

为了研究微生物组之间的遗传差异,我们能够使用 Qiita 找到一个数据集。

来源:qi ta 文档

Qiita(发音为“ cheetah ”)是一个多组学软件包和数据库,包含微生物生态学数据集,并提供一个良好的界面来搜索不同类型的组学数据,从论文中找到参考数据,甚至提供关于文件类型和相关元数据的信息。它基于qime生物信息学管道,将原始测序数据转换和处理为有利于数据分析的通用格式。其他常见的生物信息学管道有方法数据 2使用原型等。不同管道的比较,见此处

获取我们的数据

要访问这些数据,您需要创建一个帐户,然后点击左侧的所有 QIIME 地图和 BIOMs 下载北亚利桑那大学病原体&微生物研究所 Caporaso 实验室收集的数据。

资料来源: Qiita,Caporaso 实验室项目

一旦完成,你需要解压文件,把它放在一个名为 data 的文件夹中,然后把数据转换成我们可以处理的格式。为此,我们首先需要安装一个名为 biom-format 的软件包,方法是在您的终端上键入:

pip install biom-format

完成后,我们可以在终端中运行以下命令:

biom convert -i data/BIOM/60899/all.biom -o data/BIOM/60899/feature_table.tsv — to-tsv

此命令将转换我们的。biom 文件到更熟悉的。tsv 格式,这样我们就可以进入我们的 Jupyter 笔记本,并通过以下方式读入 Pandas 数据帧:

BIOM 文件格式常见于-组学数据集中,用于表示 OTU 表格。OTUs 代表 可操作的分类单位 ,可认为是由基因组特定区域的遗传序列相似性分组的生物集群。本质上,当生物集群未知时,它们在不同的分类水平上充当“物种”的代理。该表中的观察值或行是 OTU,相应的矩阵记录了在每个样品中观察到每个 OTU 的次数。

由于最初收集这些数据的科学家最有可能在不同的表面和位置采集拭子,然后对拭子采集的微生物和真菌的 DNA 进行测序,我们不一定事先知道实际物种的组成。我们所拥有的是不同组的 DNA 串(由 A,T,C,Gs 组成),然后我们可以通过对其运行 BLAST 局部比对搜索算法来与参考数据库进行比较。

来源:爆炸词汇

这样的工具可以在这里找到。在我们的例子中,这一步已经为我们完成了。

数据预处理

幸运的是,我们的数据集已经带有相应的元数据文件,其中包含有关样本 id、测序执行位置、数据来自的实验类型、使用的测序机器、拭子收集的时间戳、地理位置等信息。我们可以通过以下操作在 pandas 中打开我们的元数据文件:

来源:作者图片

然后,我们可以创建一个助手函数,它从我们的。biom 文件,并将其转换为更容易处理的格式,我们称之为特征表:

然后,我们可以在之前的原始表上运行上面的 helper 函数,并重命名我们的索引:

我们可以看到,我们从一个有 1,497 行和 25,756 列的表开始。因为每一列代表一个特定的基因序列或 OTU(对应于我们的特征或维度),所以我们可以看到我们正在处理难以置信的高维数据。这在处理宏基因组数据集时很常见,并提出了传统数据科学技术可能无法应对的新挑战。

为了帮助我们处理这种高维度,我们使用我们的助手函数将每个最初标记有基因序列的列重命名为相应的整数,这样我们就没有真正长的基因序列字符串作为列名。这些列中的每一列包含每行的特定 OTU 的频率或计数。每一行代表一个样本 id,其可以识别例如特定的拭子。

从这里,我们可以经历一些用于清理生物信息学数据的常见实践。通过确保每个序列有 20,000 个以上的相关读数,并且在至少 3 个不同的样本中出现过(本质上,应用一系列阈值),我们可以对数据进行子集化,这样我们现在有 301 行和 1,894 列。

请注意,这一步还有很多可以改进的地方,许多科学论文都是关于这一点的。我们正从快速和肮脏的角度来处理这个问题。

来源:作者图片

因为我们应用了阈值来减少我们的要素表,所以我们也将从元数据表中删除那些对应的条目:

从这里,我们现在可以使用我们的元数据来仔细检查,看看我们有来自 3 个不同城市的样本:

来源:作者图片

后续步骤…

我们已经完成了解释微生物组宏基因组数据的性质的步骤,如何获得它,如何加载它,以及如何预处理它并将其转换成我们现在可以进行分析的格式。在我们的下一篇文章中,我们将向您展示如何应用各种机器学习算法和降维技术来帮助可视化和解释高维数据,以便我们可以从数据中推断出潜在的地理结构,而不必求助于通过元数据提供的标签。

参见:第二部分——可视化高维数据

本教程系列的两位合著者是尼古拉斯·帕克 & 蒙迪·雷默,他们都是旧金山大学数据科学硕士项目的毕业生。

如果您想联系我们,请联系我们:

蒙迪·雷默 领英
推特
个人博客

尼克·帕克 Linkedin
个人博客

Neo4j 图形数据科学库中多重图形的分析

原文:https://towardsdatascience.com/analyzing-multigraphs-in-neo4j-graph-data-science-library-35c9b6d20099?source=collection_archive---------19-----------------------

了解如何使用 Neo4j 分析多重图以及如何快速将多重图简化为单一图

当时局艰难时,我认为关注我们的关系是至关重要的。我们中的一些人更关注社会交往。其他人喜欢摆弄神经元,而有些人只想看看可爱的动物。不管你的网络偏好是什么,我都愿意帮助你反思那些关系,并找到更多关于它们的(积极的)见解。为此,我们将戴上数据科学的帽子,研究一个简单的网络,以了解 Neo4j 图形数据科学库如何处理多重图形以及如何分析它们。我必须警告你,这将是一篇更长的博文,并且更多地关注技术细节。

到底什么是多重图?让我们来看看维基百科的定义:

在数学中,更具体地说是在图论中,一个 多图 是一个允许有多条边(也称为平行边)的图,即具有相同端节点的边。因此,两个顶点可以由一条以上的边连接。

多重边缘有两种截然不同的概念:

  • 没有自己身份的边:一条边的身份只由它连接的两个节点定义。在这种情况下,术语“多条边”意味着同一条边可以在这两个节点之间出现多次。

-有自己身份的边:边是像节点一样的基本实体。当多条边连接两个节点时,它们是不同的边。

总结定义,多重图允许给定节点对之间的多重关系。换句话说,这意味着在一对节点之间可以有许多相同类型的连接,例如:

或者一对节点之间不同类型的连接,如:

例如,在知识图中,我们可能会遇到两者的组合。

用 Neo4j 浏览器创建的可视化

我们将使用上面的示例图来演示 GDS 图书馆如何处理投影多重图,寻找什么,以及期待什么。我已经为关系添加了权重,因为我们将需要它们来演示属性聚合,但稍后会有更多内容。

导入图表

CREATE (t:Entity{name:'Tomaz'}),
       (n:Entity{name:'Neo4j'})
CREATE (t)-[:LIKES{weight:1}]->(n),
       (t)-[:LOVES{weight:2}]->(n),
       (t)-[:PRESENTED_FOR{weight:0.5}]->(n),
       (t)-[:PRESENTED_FOR{weight:1.5}]->(n);

您可以期待深入研究 GDS 多图投影,而很少或根本没有关注实际的图形算法。我们将只使用度中心性来检查投影图。

没有自我认同的关系

在 GDS 图书馆的上下文中,没有自己身份的关系意味着我们在设计图表的过程中忽略了关系的类型。

自然投影

我们将从原生投影的例子开始。如果我们使用通配符操作符*来定义我们想要投射的关系,我们会忽略它们的类型并将它们捆绑在一起。这可以理解为失去了自己的身份(Neo4j 语境下的类型)。

默认聚合策略

在第一个例子中,我们将观察图形投影过程的默认行为。

CALL gds.graph.create('default_agg','*','*', 
    {relationshipProperties: ['weight']})

默认的聚合策略不执行任何聚合,并将所有的关系从存储的图投影到内存中,而不进行任何转换。如果我们检查relationshipCount,我们观察到有四个关系被投射。我们还可以看一看relationshipProjection:

{
  "*": {
    "orientation": "NATURAL",
    "aggregation": "DEFAULT",
    "type": "*",
    "properties": {
      "weight": {
        "property": "weight",
        "defaultValue": null,
        "aggregation": "DEFAULT"
      }
    }
  }
}

任何时候你看到一个type:'*',你可以确定所有的关系在投射过程中都失去了它们的类型。这也意味着我们在执行算法时不能进行额外的过滤。为了仔细检查投影图,我们可以使用度中心性。

CALL gds.alpha.degree.stream('default_agg')
YIELD nodeId, score
RETURN gds.util.asNode(nodeId).name AS name, 
       score AS degree
ORDER BY degree DESC

结果

╔═══════════╦════════════╗
║   name    ║   degree   ║
╠═══════════╬════════════║
║ Tomaz     ║   4.0      ║
║ Neo4j     ║   0.0      ║
╚═══════════╩════════════╝

正如我们所料,所有四种关系都已被预测。为了将来有个参考,我们也来计算一下加权度中心性。通过添加relationshipWeightProperty参数,我们表明我们想要使用算法的加权变体。

CALL gds.alpha.degree.stream('default_agg', 
    {relationshipWeightProperty:'weight'})
YIELD nodeId, score
RETURN gds.util.asNode(nodeId).name AS name, 
       score AS weighted_degree
ORDER BY weighted_degree DESC

结果

╔═══════════╦════════════════════╗
║   name    ║   weighted_degree  ║
╠═══════════╬════════════════════║
║ Tomaz     ║        4.0         ║
║ Neo4j     ║        0.0         ║
╚═══════════╩════════════════════╝

结果是所有考虑的关系的权重之和。我们不再使用这个投影图,所以请记住从内存中释放它。

CALL gds.graph.drop('default_agg');

单图策略

根据用例,在投影过程中,我们可能希望将多图简化为一个图。这可以通过aggregation参数轻松实现。我们必须为关系定义使用配置映射变量。

CALL gds.graph.create('single_rel_strategy','*',
   {TYPE:{type:'*', aggregation:'SINGLE'}})

通过查看relationshipCount,我们注意到只有一个关系被投影。如果我们愿意,我们可以用程度中心性来复查结果。

CALL gds.alpha.degree.stream('single_rel_strategy')
YIELD nodeId, score
RETURN gds.util.asNode(nodeId).name AS name,
       score AS degree
ORDER BY degree DESC

结果

╔═══════════╦════════════╗
║   name    ║   degree   ║
╠═══════════╬════════════║
║ Tomaz     ║   1.0      ║
║ Neo4j     ║   0.0      ║
╚═══════════╩════════════╝

完成后,别忘了放下投影图。

CALL gds.graph.drop('single_rel_strategy');

资产聚集策略

到目前为止,我们已经看了未加权的多重图。现在是时候看看当我们处理一个加权多重图时会发生什么,我们想把它简化成一个单一的图。对于资产聚合策略,我们可以选择三种不同的策略:

  • MIN:投影所有权重的最小值
  • 最大值:投影所有权重的最大值
  • 总和:投影所有权重的总和

在我们的下一个例子中,我们将使用MIN属性聚合策略将一个加权的多重图简化为一个单一的图。通过提供 property aggregation 参数,我们表明我们希望在投影过程中将存储的图形减少为单个图形。

CALL gds.graph.create('min_aggregation','*','*',
    {relationshipProperties: {weight: {property: 'weight',
                                       aggregation: 'MIN'}}})

我们可以观察到relationshipCount为 1,这意味着我们的多重图已经成功地简化为单个图。让我们来考察一下relationshipProjection

{
  "*": {
    "orientation": "NATURAL",
    "aggregation": "DEFAULT",
    "type": "*",
    "properties": {
      "weight": {
        "property": "weight",
        "defaultValue": null,
        "aggregation": "MIN"
      }
    }
  }
}

在这里,我们可以看到有两个聚合配置选项,一个在关系级别,另一个在属性级别。据我所知,您应该在处理未加权网络时使用关系级聚合,在处理加权网络时使用属性级聚合。我们将再次用程度中心性仔细检查结果。

CALL gds.alpha.degree.stream('min_aggregation')
YIELD nodeId, score
RETURN gds.util.asNode(nodeId).name AS name,
       score AS degree
ORDER BY degree DESC

结果

╔═══════════╦════════════╗
║   name    ║   degree   ║
╠═══════════╬════════════║
║ Tomaz     ║   1.0      ║
║ Neo4j     ║   0.0      ║
╚═══════════╩════════════╝

为了验证MIN属性聚合,让我们也计算加权度中心性。

CALL gds.alpha.degree.stream('min_aggregation',
    {relationshipWeightProperty:'weight'})
YIELD nodeId, score
RETURN gds.util.asNode(nodeId).name AS name,
       score AS weighted_degree
ORDER BY weighted_degree DESC

结果

╔═══════════╦════════════════════╗
║   name    ║   weighted_degree  ║
╠═══════════╬════════════════════║
║ Tomaz     ║        0.5         ║
║ Neo4j     ║        0.0         ║
╚═══════════╩════════════════════╝

正如我们对MIN属性聚合策略的预期,单个缩减权重具有所考虑权重的最小值。同样,在我们完成示例时,不要忘记删除投影图。

CALL gds.graph.drop('min_aggregation');

Cypher 投影

让我们用 cypher projection 重现上面的例子。为了丢失关系的身份并将它们捆绑在一起,我们避免在关系语句的返回中提供type列。

默认聚合策略

与本机投影类似,cypher projection 中的默认设置是在投影过程中投影所有关系,而不进行任何转换。

CALL gds.graph.create.cypher(
  'cypher_default_strategy',
  'MATCH (n:Entity) RETURN id(n) AS id',
  'MATCH (n:Entity)-[r]->(m:Entity)
   RETURN id(n) AS source, id(m) AS target'
)

通过查看relationshipCount,我们观察到所有四个关系都按照预期进行了设计。我们也来仔细看看relationshipProjection

{
  "*": {
    "orientation": "NATURAL",
    "aggregation": "DEFAULT",
    "type": "*",
    "properties": {

    }
  }
}

记住,之前我们说过type:"*"表示关系在投射过程中失去了身份(类型)。这同样适用于 cypher projection。为了验证投影图,我们运行度中心性。

CALL gds.alpha.degree.stream('cypher_default_strategy')
YIELD nodeId, score
RETURN gds.util.asNode(nodeId).name AS name, 
       score AS degree
ORDER BY degree DESC

结果

╔═══════════╦════════════╗
║   name    ║   degree   ║
╠═══════════╬════════════║
║ Tomaz     ║   4.0      ║
║ Neo4j     ║   0.0      ║
╚═══════════╩════════════╝

单一关系策略

使用 cypher projection,我们无法访问关系级别的聚合策略。这完全没有问题,因为只使用 cypher 查询语言将多重图简化为单一图是非常简单的。我们简单的在关系语句的返回中加上DISTINCT子句,应该就好走了。如果你需要更多关于 cypher 的帮助,我建议你看看 Neo4j Graph Academy

CALL gds.graph.create.cypher(
  'cypher_single_strategy',
  'MATCH (n:Entity) RETURN id(n) AS id',
  'MATCH (n:Entity)-[r]->(m:Entity) 
   RETURN DISTINCT id(n) AS source, id(m) AS target'
)

关系计数是 1,这意味着我们已经成功地简化了多重图。记得放下投影图。

CALL gds.graph.drop('cypher_single_strategy')

资产聚集策略

另一方面,有了 cypher projection,我们就可以访问资产级别的聚合策略。我们并不真的“需要”它们,因为我们只用 cypher 就可以完成所有的转换。为了向您展示我的意思,我们可以使用简单的密码应用最低资产策略聚合,如下所示:

CALL gds.graph.create.cypher(
  'cypher_min_strategy',
  'MATCH (n:Entity) RETURN id(n) AS id',
  'MATCH (n:Entity)-[r]->(m:Entity)
   RETURN id(n) AS source, id(m) AS target, min(r.weight) as weight'
)

但是,如果我们看一下官方文档:

这种方法的一个缺点是我们给 Cypher 执行引擎施加了更多的压力,并且查询结果会消耗额外的内存。另一种方法是使用 *relationshipProperties* 作为可选配置图的一部分。语法与本机投影中使用的属性映射相同。

因此,为了节省内存,我们可以在配置图中使用属性级聚合策略。

CALL gds.graph.create.cypher(
  'cypher_min_improved',
  'MATCH (n:Entity) RETURN id(n) AS id',
  'MATCH (n:Entity)-[r]->(m:Entity)
   RETURN id(n) AS source, id(m) AS target, r.weight as weight',
   {relationshipProperties: {minWeight: {property: 'weight', 
                                         aggregation: 'MIN'}}})

relationshipCount是 1,这证实了我们成功的多图归约。为了确保万无一失,我们可以运行加权中心性并验证结果。

CALL gds.alpha.degree.stream('cypher_min_improved',
    {relationshipWeightProperty:'minWeight'})
YIELD nodeId, score
RETURN gds.util.asNode(nodeId).name AS name, 
       score AS weighted_degree
ORDER BY weighted_degree DESC

结果

╔═══════════╦════════════════════╗
║   name    ║   weighted_degree  ║
╠═══════════╬════════════════════║
║ Tomaz     ║        0.5         ║
║ Neo4j     ║        0.0         ║
╚═══════════╩════════════════════╝

一切就绪后,我们可以从内存中释放两个投影图。

CALL gds.graph.drop('cypher_min_improved');
CALL gds.graph.drop('cypher_min_strategy);

与自己身份的关系

我们还可以选择在预测过程中保留关系类型。其中,这允许我们在执行图算法时执行额外的过滤。然而,我们必须小心,因为在多重图的上下文中,用保留类型来投影关系有点不同。

自然投影

使用原生投影,很容易声明我们想要保留关系的类型。我们所要做的就是指定我们要考虑的关系类型,GDS 引擎会自动将关系捆绑到特定的关系类型下。让我们来看一些例子,以便更好地理解。

默认聚合策略

从前面的例子中,我们已经知道默认的聚合策略不执行任何转换。通过定义关系类型,我们向 GDS 库表明,我们希望在投影过程之后保留它们的类型。

CALL gds.graph.create('type_default','*',
    ['PRESENTED_FOR','LIKES','LOVES'])

不出所料,relationshipsCount是 4。让我们仔细看看relationshipProjection

{
  "LIKES": {
    "orientation": "NATURAL",
    "aggregation": "DEFAULT",
    "type": "LIKES",
    "properties": {}
  },
  "LOVES": {
    "orientation": "NATURAL",
    "aggregation": "DEFAULT",
    "type": "LOVES",
    "properties": {}
  },
  "PRESENTED_FOR": {
    "orientation": "NATURAL",
    "aggregation": "DEFAULT",
    "type": "PRESENTED_FOR",
    "properties": {}
  }
}

我们可以看到我们有三个不同的关系束或组。每个包由一个关系类型组成,这个关系类型是用type参数定义的。查看relationshipProjection来验证我们投射了什么类型的图,以及关系类型是否被保留,这是很方便的。与这个内存中的图形没有太多关系。

CALL gds.graph.drop('type_default');

单一关系策略

像以前一样,我们可以用关系等级aggregation参数将未加权的多重图简化为单一图。我们必须分别为每种关系类型提供聚合参数。

CALL gds.graph.create('type_single','*',
   {LIKES:{type:'LIKES',aggregation:'SINGLE'},
    LOVES:{type:'LOVES',aggregation:'SINGLE'},
    PRESENTED_FOR:{type:'PRESENTED_FOR',aggregation:'SINGLE'}})

好的,所以我们简化为一个单一的图形,但是relationshipCount是 3。为什么会这样呢?多图归约过程在关系类型级别上工作,因为我们有三种不同的关系类型,所以每种类型都有一个单独的关系。我们来计算一下整个内存图的度中心性。

CALL gds.alpha.degree.stream('type_single')
YIELD nodeId, score 
RETURN gds.util.asNode(nodeId).name AS name, 
       score AS degree
ORDER BY degree DESC

结果

╔═══════════╦════════════╗
║   name    ║   degree   ║
╠═══════════╬════════════║
║ Tomaz     ║   3.0      ║
║ Neo4j     ║   0.0      ║
╚═══════════╩════════════╝

正如我们所解释的,即使我们已经分别减少了每种关系类型,我们仍然在整体上处理一个多重图。当运行图算法时,您必须密切关注您是否正在处理多重图,您是否投影了多个关系类型,以及您是否在投影期间执行了任何转换,因为所有这些都将影响算法结果。

我们现在可以放下这张图表了。

CALL gds.graph.drop('type_single');

资产聚集策略

财产聚集策略与我们以前处理没有身份的关系时非常相似。唯一的变化是现在聚合是按关系类型分组的。

CALL gds.graph.create('type_min','*',
    ['PRESENTED_FOR','LIKES','LOVES'],
    {relationshipProperties: {weight: {property: 'weight', 
                                       aggregation: 'MIN'}}})

我们预测了 3 个关系,因为我们知道聚合发生在关系类型级别。我们将用加权度对结果进行复查。

CALL gds.alpha.degree.stream('type_min',
    {relationshipWeightProperty:'weight'})
YIELD nodeId, score
RETURN gds.util.asNode(nodeId).name AS name, 
       score AS weighted_degree
ORDER BY weighted_degree DESC

结果

╔═══════════╦════════════════════╗
║   name    ║   weighted_degree  ║
╠═══════════╬════════════════════║
║ Tomaz     ║        3.5         ║
║ Neo4j     ║        0.0         ║
╚═══════════╩════════════════════╝

我现在感觉像一个破纪录,但不要忘记放弃图表:)

CALL gds.graph.drop('type-min');

结论

为了缩短这篇博文,我跳过了保留关系类型的 cypher projection 的例子。基本上,您所要做的就是在 relationship 语句中添加列type,它的行为应该与原生投影示例相同。我希望你在投影过程中更好地了解了 Neo4j 图形数据科学库下面发生的事情,这将有望帮助你找到更多更好的见解。总而言之,所有关系级别或属性级别的聚合都是按关系类型分组的。

感谢阅读,一如既往,代码可在 GitHub 上获得。

使用 Python 分析 Youtube 上的音乐视频趋势

原文:https://towardsdatascience.com/analyzing-music-video-trends-in-youtube-using-python-11e5750709bd?source=collection_archive---------27-----------------------

根据 Youtube 搜索趋势找出最受欢迎的音乐视频。

雅各布·欧文斯在 Unsplash 上的照片

音乐视频在音乐产业中有着巨大的作用。不仅仅是为了宣传的目的,音乐视频也是为了表现艺术家的形象和形象化的诠释歌曲而制作的。

在这个数字时代,Youtube 已经成为全球最主要的流媒体平台。Youtube 上有很多不同风格的音乐视频。不可否认,了解音乐行业受欢迎程度的方法之一是查看 Youtube 上的音乐视频趋势。

你有没有好奇过 Youtube 上搜索次数最多的音乐视频是什么?因为我一直。这就是为什么我试图通过 Pytrends 找出 Youtube 的搜索趋势。Pytrends 是 Google Trends 的 API,允许你在 Google 搜索引擎上检索趋势,包括 Youtube。在本教程中,我将向您展示如何深入了解 Python 中的 Youtube 搜索趋势。

项目设置

您需要做的第一件事是通过 pip 安装 Pytrends 和 leav 包。

pip install pytrends
pip install folium

安装 API 后,打开 Jupyter 笔记本,然后导入必要的库,包括:

  1. 熊猫来处理数据帧,
  2. Seaborn 和 Matplotlib 来制作图表,
  3. 叶子创建可视化的地图。
from pytrends.request import TrendReq
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
import folium

接下来的步骤是连接到 Google,并确定我们想从 Pytrends 获得什么样的数据。

pytrends = TrendReq(hl='en-US', tz=360)kw_list = ["music video", "mv"]
pytrends.build_payload(kw_list, cat=0, timeframe='today 5-y', geo='', gprop='youtube')

在这种情况下,我使用了【音乐视频】【MV】作为主要关键字。此外,我用它作为一个时间框架来检索过去 5 年的数据。要查看参数选项,可以在这里查看 Pytrends 的文档。

相关查询

要查看大多数人感兴趣的术语—与音乐视频相关,我们可以使用相关查询。在 Pytrends 中,有一个功能可以找到其他用户搜索过的关键字的相关术语。

df_queries = pytrends.related_queries()

有两种度量结果, TopRising

热门最受欢迎的搜索查询,按谷歌趋势分值(0-100)排序

上升— 具有最大的查询在一段时间内增加搜索频率。如果您想查看最近的命中查询,您可以查看此指标。

我结合了音乐视频或 MV 的最高结果,并用下面的代码选择了 5 个最高值。

top_music_video = df_queries.get("music video").get("top")
top_mv = df_queries.get("mv").get("top")
df_top = pd.concat([top_music_video, top_mv] )df_top.sort_values(['value'], ascending = False).head(5).reset_index(drop = True)

音乐视频热门查询

此外,我用下面的代码选择了上升结果的前 5 名。

rising_music_video = df_queries.get("music video").get("rising")
rising_mv = df_queries.get("mv").get("rising")
df_rising = pd.concat([rising_music_video, rising_mv] )df_rising.sort_values(['value'], ascending = False).head(5)

不断上升的对音乐视频的质疑

分析

从上面的结果,我们可以看到,在过去的 5 年里,音乐视频的趋势是由韩国流行音乐行业主导的。

乔尔·穆尼斯在 Unsplash 上的照片

Blackpink (一个 K-pop 女子乐队)和 BTS (一个 K-pop 男子乐队)是其音乐视频在 Youtube 上被搜索次数最多的艺术家。从上升的结果,我们发现有一个 BTS 的音乐视频叫做炸丨药成为一个巨大的打击和搜索了许多互联网用户。

还有 Gacha Life ,这是一个促进孩子创造力和讲故事的游戏。Gacha Life 的用户喜欢根据 Gacha 动画制作音乐视频,许多 Youtube 用户对此感兴趣。

长期利息

从相关查询的结果中,我们发现了 4 个与音乐视频或 MV 相关的主要术语,它们是 BTS、Blackpink、Gacha Life 和 Dynamite。

为了了解这些术语在 Youtube 上的受欢迎程度,我们可以使用Interest Over Time

kw_list = ["Gacha Life", "Dynamite", "Blackpink", "BTS"]pytrends.build_payload(kw_list, cat=0, timeframe='today 5-y', geo='', gprop='youtube')df_interest = pytrends.interest_over_time().drop(columns='isPartial') 

得到 DataFrame 形式的数据后,我们可以使用 Seaborn 和 Matplotlib 来可视化,如下图所示。

plt.figure(figsize=(16, 8))
plt.xticks(rotation=45)
sns.set(style="darkgrid", palette = 'rocket')
ax = sns.lineplot(data=df_interest)
ax.set_title('Music Video Trends Over Time', fontsize=20)

分析

在这四个词之间,“BTS”从 2016 年开始流行。人气一直持续到现在,排在第二位的是《Blackpink》。“炸丨药”本身在 2020 年中期开始蓬勃发展。与此同时,“嘎查生活”在 2018 年底开始流行。

按地区划分的利息

从上面的结果来看,我个人对 BTS 更感兴趣,因为他们的音乐视频在其他人中占主导地位。所以在这种情况下,我想知道全球范围内 BTS 观众在 Youtube 上的传播情况。

kw_list = ["BTS"]pytrends.build_payload(kw_list, cat=0, timeframe='today 5-y', geo='', gprop='youtube')df_interest_region = pytrends.interest_by_region(resolution='COUNTRY', inc_low_vol=False)df = df_interest_region.sort_values(by='BTS', ascending=False).head(10).reset_index()

我试图创建一个对 BTS 音乐视频最感兴趣的 10 个国家的图表。

fig = plt.figure(figsize=(15,5))
plt.bar( df['geoName'],
         df['BTS'],
         color = ['#fa9d96','#fa928a','#ff828a','#ff728a','#ff628a','#ff528a','#ff428a',
                  '#ff328a','#ff228a','#ff028a'])plt.xticks(rotation=45,ha='right')
plt.title('Countries Which Most Interested in BTS Music Video',y=1.1,fontsize=20)
plt.xlabel('Country')
plt.ylabel('Value')

条形图

如果您有兴趣查看地图可视化,可以尝试创建由 leav 提供的 Choropleth 地图。Choropleth 图以阴影颜色显示区间数据,其中较浅的阴影代表较低的数字,较深的阴影代表较高的数字。尝试下面的代码。

url = '[https://raw.githubusercontent.com/python-visualization/folium/master/examples/data'](https://raw.githubusercontent.com/python-visualization/folium/master/examples/data')
country_shapes = f'{url}/world-countries.json'the_map = folium.Map(tiles="cartodbpositron")
the_map.choropleth(
    geo_data=country_shapes,
    name='choropleth',
    data=df_interest_region,
    columns=['geoName', 'BTS'],
    key_on='properties.name',
    fill_color='Purples',
    nan_fill_color='white',
    fill_opacity=0.8,
    line_opacity=0.5,
)the_map

然后,我们来看结果。

等值区域图

分析

从上面的柱状图我们可以看出,BTS 最大的观众来自文莱,其次是另一个亚洲国家,如菲律宾、蒙古、马来西亚和缅甸。这一点得到了地图的支持,地图显示亚洲出现了较暗的阴影。美国和澳大利亚等其他大陆也成为 BTS 观众的一部分,尽管不像亚洲国家那样大胆。

结论

作为最受欢迎的视频搜索引擎平台之一,Youtube 可以让我们了解到世界各地的人们最好奇的是什么样的音乐视频。Pytrends 可以利用这些数据,甚至通过使用 Seaborn、Folium 和 Matplotlib 来创建这些数据的可视化效果。因为这些工具,现在我们知道在过去的 5 年里,K-pop 在世界音乐产业中占据了主导地位。这种趋势会持续下去吗?我们走着瞧。

分析 PyPI 包下载统计数据并构建数据应用程序

原文:https://towardsdatascience.com/analyzing-pypi-package-download-statistics-and-building-a-data-application-c30f4f708e38?source=collection_archive---------39-----------------------

使用数据堆栈构建数据应用程序

使用数据堆栈构建的最终数据应用程序

尽管 Jupyter 笔记本对于编写代码和与数据进行交互是必不可少的,但仍然需要为非技术受众构建应用程序。幸运的是,Python 生态系统提供了一个广泛而活跃的开源生态系统,不仅用于数据分析,还用于使用数据和可视化库构建数据应用程序。

在这篇文章中,我将尝试向您概述如何使用 dstack 构建交互式数据应用程序。我们还将学习如何使用 Python、pandas 和 plot.ly 分析和可视化数据,以及使用 Python 和pandas查询来自 Google 的 BigQuery 的数据。

出于教学目的,我们将分析 Python 包的下载统计数据,并使我们的应用程序可视化以下内容:

  • 下载次数最多的软件包
  • 过去一个月、六个月和十二个月的下载次数
  • 每个版本的包、操作系统和 Python 版本的下载次数

我们将使用 dstack 构建一个交互式应用程序,以仪表板的形式可视化这些统计数据。本教程中构建的应用程序可以在这里找到。

对于渴望在分析数据的背景下学习 Python 的初级数据科学家来说,本教程可能会很有意思。

注意,本笔记本中的分析仅用于教育目的。如果您发现错误或对代码或结果有疑问,请发送电子邮件至 team@dstack.ai

本教程最初灵感来自 pepy.tech

关于 Python 包下载统计数据集

为什么在本教程中使用 Python 包下载统计?首先,因为数据是公开的。其次,因为 PyPI 提供了方便地访问这些数据的方法。第三,制作一个应用程序来可视化这些数据可能是用 Python 构建数据应用程序的一个很好的例子。

该分析的数据来自哪里?Python 包的下载数据由 PyPI 存储在 Google 的 BigQuery 中。每天的下载数据存储在与模式the-psf:pypi.downloadsYYYYMMDD匹配的单独的表中。这些表有这个模式。

注意,BigQuery 中的这些数据只有在你有自己的 Google Cloud 账号的情况下才能访问。要访问这些数据,您必须按照 BigQuery 的定价(每 TB 5.00 美元)付费。考虑到数据量,查询可能非常昂贵。你查询的越多,扫描的数据就越多,你支付的费用也就越多。

传递谷歌云凭证

要连接到 Google 的 BigQuery,我们必须进行身份验证。BigQuery 是 Google Cloud 的一部分,用于身份验证,需要通过 OAuth 2.0 传递 Google 凭证。

可以通过多种方式从 Python 连接到 BigQuery。在本教程中,我们将使用 pandas,这是使用 BigQueryusing pandas_gbq 最简单的方法。

对于认证,pandas_gbq集成了 google-auth 库。

为了开始使用 pandas_gbq,您必须传递凭证以及与凭证相关联的项目名称。

在本教程中,我们将使用服务帐户凭据。这种类型的凭证必须通过 Google Cloud 控制台创建。一旦创建了凭证,就必须下载相应的 JSON 文件,以便在这个笔记本上使用。

注意,带有凭证的 JSON 文件必须只存储在安全的环境中。

我们将使用以下代码从 Python 加载凭证:

现在,我们必须用获得的凭证和项目名称初始化pandas_gbq的上下文:

在使用pandas_gbq之前,请确保您已经为此项目启用了 BigQuery API,并且与获取的凭证相关联的服务角色具有访问 BigQuery 和运行 BigQuery 作业的权限。

现在,我们已经准备好从 Python 查询 PyPI 包下载统计数据。

在我们将要构建的应用程序上

在这个例子中,我们将使用dstack来构建一个应用程序,它显示按项目版本、Python 版本和操作系统分组的各个项目的下载。为了使教程足够简单,我们将限制它只显示下载最多的前 100 个项目的数据。

为什么是dstack?首先,与其他框架相比,构建和部署dstack应用程序需要几天时间。其次,dstack不要求你做 web 编程,写 UI 脚本,或者其他开发和部署应用程序通常需要的事情。不用编写应用程序,您可以将数据推送到一个dstack服务器,并提供一个声明性的逻辑,说明它应该如何与用户输入交互。开始时,你会对这种方法感到惊讶,但后来你会发现它是多么简单而强大。

应用程序将如何工作?在我们的应用程序中,我们将使用pandas_gbq查询数据,使用“plotly”进行可视化,并推送与以下参数相关的结果可视化:

  • 项目的名称
  • 时间段(例如,过去 30 天、过去 6 个月、过去 12 个月)
  • 数据的维度(例如,按项目版本、Python 版本或操作系统分组)

一旦我们推送带有所有参数的可视化效果,dstack会自动将其编译到一个仪表板中。本教程中构建的应用程序可以在这里找到

本教程的大部分内容都是一个简单步骤的重复:

  1. 运行 SQL 查询,聚合X期间的下载,按项目名称、YZ分组,其中
  2. X / Y可能是30 days / day6 months / week12 months / month
  3. Z可以是project versionpython versionoperational system
  4. 制作一个情节
  5. 用相应的参数如project nameXZdstack将图推送到dstack

放弃

请注意,下面的一些查询可能会查询超过 6 个月或更长时间的 BigQuery 数据。避免在不必要的时候运行整个笔记本。阅读关于如何避免对 BigQuery 运行不必要的查询的章节Optimization

获得 100 个下载量最高的 PyPI 项目

在本教程中,我们将只关注下载次数最多的前 100 个项目。我们要做的第一件事是获取这 100 个项目的名称。为此,我们将按项目汇总过去 30 天的下载量,按降序排列,并取前 100 个。

下面是我们如何用pandas_gbq来做这件事:

如您所见,pandas_gbq.read_gbq函数的结果是一个pandas.DataFrame

查询中最重要的部分是表名the-psf.pypi.downloads*和使用_TABLE_SUFFIXWHERE子句。它是做什么的?它只扫描从the-psf.pypi.downloads开始到当前日期的最后 30 天内结束的表中的数据。这样,我们可以确保查询只扫描最近 30 天内的数据。为什么这很重要?这很重要,因为我们使用 BigQuery,我们按每 TB 扫描数据付费。为了优化查询成本,限制被扫描的数据量总是很重要的。在这篇文章的后面,我们将再次回到定价因素,并看看优化它的其他方法。

同时,让我们声明一个变量top_projects,并将其分配给下载量最高的前 100 个项目的名称列表:

获取过去 30 天每个项目版本的下载量

下面是第一个 SQL 查询:

现在我们已经有了一个包含我们需要的数据的数据框,我们将为项目的每个版本制作一个下载图。前五个版本将作为单独的图表。剩下的将是一个名为Other的图表。下面是项目pandas的一个例子:

过去 30 天每个项目版本的下载量

获取过去 30 天每个 Python 版本的下载量

下面是与上面几乎相同的查询,但是现在它按照主要 Python 版本(REGEXP_EXTRACT(details.python, r'^\d*'))对数据进行分组:

项目的样本地块pandas:

过去 30 天每个 Python 版本的下载量

获取过去 30 天每个操作系统的下载量

下面是另一个查询,现在按operational system分组:

项目的样本地块pandas:

过去 30 天每个操作系统的下载量

获取过去 6 个月每个项目版本的下载量

在下面的单元格中,我们将重复上述步骤,但现在是针对不同的XYZ

项目的样本地块pandas:

获取过去 6 个月每个项目版本的下载量

获取过去 6 个月每个 Python 版本的下载量

项目的样本地块pandas:

过去 6 个月每个 Python 版本的下载量

这段代码最有趣的部分是对fig.update_xaxes的调用。为了确保图表中显示的所有数据在给定的一周内都是完整的,我们将图的边界相应地向右和向左移动一周,以排除可能具有不完整数据的周。

获取过去 6 个月每个操作系统的下载量

项目的样本地块pandas:

过去 6 个月每个操作系统的下载量

获取过去 12 个月每个项目版本的下载量

项目的样本地块pandas:

过去 12 个月每个项目版本的下载量

获取过去 12 个月每个 Python 版本的下载量

过去 12 个月每个 Python 版本的下载量

获取过去 12 个月每个操作系统的下载量

过去 12 个月每个操作系统的下载量

设置数据堆栈

现在我们已经有了数据和可视化,我们准备设置一个dstack服务器并构建应用程序。

dstack是一个开源框架,可以通过 pip 安装

或者使用 conda:

dstack有两个组件:

  1. 托管数据并运行已发布应用程序的服务器
  2. 将数据推送到服务器的客户端 Python 包

服务器可以在本地启动,也可以在专用服务器上启动。为了在本地启动 dstack 服务器,必须运行以下命令:

一旦服务器启动,用户就可以使用 Python 客户端包来推送数据。

最佳化

现在我们已经设置了一个dstack服务器,这里有一个重要的说明,关于如何使用 BigQuery 或类似的按扫描数据量收费的服务。使用此类服务的最佳方式是避免重新运行不必要的查询,这可能会非常昂贵。这可以通过在盘上本地缓存数据或其他方式来容易地完成。如果您必须定期更新可视化效果,这一点尤其重要。

有趣的是,您也可以使用dstack来存储缓存数据。这可以通过以下方式实现:

这里发生的是日期被放入两个“堆栈”:"pypi/top-projects""pypi/raw-data"。这个“堆栈”的概念概括了dstack是如何建造的。每个栈都被版本化(意味着它在被更新时存储所有的修订),并且可以包含一个或多个与参数相关联的工件。这样的工件可以包括数据帧、ML 模型或可视化。在上面的代码中,我们使用堆栈来存储数据帧。一旦发布了工件,就可以通过dstack的 web 界面访问它,或者通过 Python 代码获取它。下面是实现这一点的代码:

因此,变量被赋予我们存储的实际数据帧。推送时,提取的数据框具有完全相同的方案。当按计划以自动化方式进行处理时,这种方法特别有用。

使用数据堆栈制作数据应用程序

既然我们已经介绍了如何设置一个dstack服务器以及如何在那里缓存数据,那么让我们把所有的东西放在一起,最终构建一个应用程序。您会惊讶地发现,从头开始构建一个应用程序所需要做的工作是如此之少。

下面是构建整个应用程序的代码:

我们在这段代码中做的是:

  1. 创建一个框架(功能dstack.create_frame
  2. 对于每个可视化,在框架内创建一个附件以及可视化图形和相关参数,如"Project""Period""Group by"(函数dstack.StackFrame.commit)
  3. 推动框架(功能dstack.StackFrame.push)

如果将构建可视化效果的代码移动到单独的函数中,可以显著简化该代码:

正如你所看到的,与应用程序本身相关的代码甚至比专用于绘图的代码还要少。

将这些数据推送到dstack服务器后会发生什么?服务器动态构建一个交互式应用程序,您可以在其中更改ProjectPeriodGroup by的值,并查看与所选值相对应的可视化效果。由于数据是预先计算的,因此可以从缓存中提供,从而在高负载下高效扩展(例如,如果应用程序由大型团队使用)。

使用数据堆栈构建的最终数据应用程序

计划常规作业

正如您在上面看到的,在dstack中的数据应用程序可以是一个静态报告,可以定期手动或自动更新。

自动化将要求您设置运行上述代码的常规作业。虽然 12 个月和 6 个月的数据可以按月计划,但每日作业可以每天或每周运行。这样,您可以优化成本,并确保数据始终是最新的。

Docker 版本的dstack以及 in-cloud 版本确实提供内置工作。使用这些作业,您可以按照需要的节奏安排更新堆栈。

暂时就这样了。希望这篇文章能帮助你学习如何用 Python 和 stack 构建数据应用。感谢您抽出时间阅读本文。

dstack是一个非常新的工具,目前提供非常基本的功能,他们计划在接下来的几个月内推出更多高级功能。欢迎您查看他们的公共路线图,向他们的问题跟踪者提交特性请求,当然,也可以在他们的不和谐聊天中分享您的反馈。

资源

以下是您可能会发现有用的资源列表:

用零镜头分类法分析 Twitter 上的 QAnon

原文:https://towardsdatascience.com/analyzing-qanon-on-twitter-with-zero-shot-classification-13ad73d324fc?source=collection_archive---------37-----------------------

使用革命性的、无监督的零镜头文本分类机器学习技术,将 QAnon 推文标记为主题类别的教程

乔恩·泰森在 Unsplash 上的照片

最近,脸书在拖延了三年之后,终于在其平台上禁止了极右的阴谋论 QAnon。在他们的带领下,Twitter 和 Youtube 也纷纷效仿。从白人至上的根源开始,曾经的理论已经演变成一场咄咄逼人的运动,寻求发动信息战,以此作为影响即将到来的美国总统大选的一种手段,支持唐纳德·特朗普。联邦调查局已经将 QAnon 认定为国内恐怖主义威胁,今年早些时候,Twitter 采取行动封禁了数千个 QAnon 附属账户。然而,许多亲卡农的账户继续发推文,更阴险的是,特朗普有一个通过不负责任地转发他们的恶意内容来放大这些声音的被证明的历史。他还拒绝公开谴责这一理论,回避关于恋童癖的评论,进一步传播有毒的不确定性。

我认为调查这些账户在推特上发布了什么会很有趣。具体来说,我很好奇极右翼讨论的最受欢迎的话题,以及我是否可以利用机器学习的最新发展来分析 Twitter 上当前活跃的已知 QAnon 帐户的推文。我决定尝试零距离学习将推文分类成我选择的主题类别:政治、公共卫生、气候变化、宗教、争议假新闻。在这个实验中,我使用了 Twitter API 来收集推文,并使用了humping Face 的开箱即用的文本分类零镜头学习实现(2020 年 8 月发布)。根据无监督学习方法,未标记的推文被分类到社会相关主题中,没有任何特定任务的训练或注释推文进行微调。为了使我的 zero-shot 分类器可以访问,我用 Python 框架 Streamlit 构建了一个 web 应用程序,用于一个交互式界面,在那里我可以拖放大量存储为 csv 或 json 文件的未处理的 tweets。

使用 Streamlit 对推文进行批量零镜头分类的机器学习 web 应用程序。图片作者。

为什么要零拍学习?

在机器学习领域,零镜头学习是一种无监督学习,其目标是让模型在不依赖标记数据的情况下完成尚未明确训练的任务。在过去的三年中,自然语言处理(NLP)的趋势是创建非常大的预训练语言模型,允许从无监督模型进行迁移学习,然后在下游监督任务中进行微调,从而需要标记数据。与监督学习所需的高质量注释数据相比,从社交媒体获取原始未标记推文的资源成本较低。因此,在需要更少的资源和没有额外的训练时间,零射击学习有明显的优势。此外,零触发分类的实施允许选择标签的多样性。由于标签的选择并不局限于分类器被微调的标签,因此可以根据特定的任务任意选择标签。

上个月,我发现 Python 库 Hugging Face transformers 发布了一个用于零镜头文本分类的管道模型。像往常一样,拥抱脸使这个分类器变得容易使用。它很容易修改,我只用了一个下午就可以用 Streamlit 围绕它构建一个简单的 web 应用程序。拥抱脸研究工程师乔·戴维森写的这篇出色的博文给了我灵感。他简要解释了零起点学习的历史,并详细讨论了两种常用的方法:潜在嵌入法和自然语言推理(NLI)。拥抱脸管道模型利用了后一种方法。

什么是自然语言推理?

NLI 提到了一个 NLP 任务,其中考虑了两个句子:一个前提和一个假设。任务是确定前提对假设的影响。结果是由前提确定的假设的标签,它可以是真(蕴涵),假(矛盾)或不相关(中性)。对于零镜头分类,抱脸使用尹等(2019) 在一篇论文中介绍的方法。在这种方法中,前提将是要被标记的序列(tweet 的文本),假设将代表候选标签(主题类别)。一个序列和所有可能的候选标签被嵌入到同一个潜在空间中,从而可以测量嵌入之间的距离。此外,这允许评估两种不同序列的相容性。为每条推文预测的主题标签代表具有最高蕴涵可能性的假设/候选标签。

拥抱脸的实现涉及一个预训练的 BART 模型,该模型在多体裁自然语言推理(MNLI)语料库上进行微调,以构建一个序列对分类器,然后用作零镜头分类器。一个 BART 模型是指一个 seq2seq 机器翻译架构,带有一个双向编码器(像 BERT )和一个左右解码器(像 GPT-3 )。预训练使用一种新颖的填充方案随机打乱原始句子的顺序,其中文本的跨度由单个掩码标记代替。令人欣慰的是,拥抱脸使得使用他们的零射击分类器只需几行代码成为可能。整个过程可以被简化和可视化描述。我下面的图表显示了这个过程的概要:

拥抱脸变形金刚管道推文零镜头文本分类。图片作者。

实验设置

出于实用性和我的硬件限制,我不得不选择一天来分析。因此,我选择查看 10 月 2 日以来的所有推文,这一天川普宣布他对新冠肺炎的检测呈阳性。我从哈佛大学 Shorenstein 中心精心挑选的 QAnon 关联 Twitter 账户样本中收集了大约 5300 条推文。我将推文保存为 csv 文件,然后可以拖放到我用 Streamlit 构建的机器学习 web 应用程序中。在后端,我编写了一个 Python 脚本来预处理并对一批推文应用零命中率分类器,将每条推文分类到我任意选择的六个类别中的一个。我对关于政治、公共卫生、气候变化、宗教、争议和假新闻的对话很感兴趣。在稍后的场合,我打算对分类后的推文进行进一步的情感分析,并跨类别进行比较,以便对话语有更细微的理解。

如上图所示,我需要三样东西来进行分类:分类器、主题标签和假设模板。如下面的代码片段所示,我使用来自 Hugging Face 的管道构建了一个只有一行的零镜头分类器,一个主题标签列表被分配给一个变量,并且我为一个假设模板添加了一行前导文本。

输出有三个组成部分:一个单个文本条目的序列、一组六个标签和一组六个介于 0 到 1 之间的相应分数,代表蕴涵可能性的概率分布。请参见下面的示例:

{'sequence': 'FYI President Trump says he has been taking Hydroxychloroquine to combat COVID Coronavirus ', 'labels': ['misinformation', 'public health', 'controversial opinion', 'politics', 'climate change', 'religion'], 'scores': [0.5967247545719147, 0.2244514465332031, 0.09179548919200897, 0.07029037922620773, 0.010313253849744797, 0.006424691993743181]}

在样本输出中,该推文的主题被标记为“误传”,第一个也是最高的分数 0.59 代表该推文属于误传类别的置信度。

接下来,我编写了一个函数来迭代熊猫推文的数据帧,对结果进行预处理、分类和存储。

为了可视化结果,我使用了我最喜欢的 Python 图形库 Plotly 来绘制一个交互式条形图,显示主题的分布。

我发现了什么?

显示零镜头分类 QAnon 推文主题分布的条形图。图片作者。

在 5300 条推文中,约有 5000 条对某个特定标签的评分超过 50%,因此被分类。其余人在所有标签上的分数都太低,很可能会被归类为“其他”,这表明我选择的标签并不完全包容。最受欢迎的话题是争议,其次是政治。最不受欢迎的话题是气候变化,其次是假新闻和公共卫生。鉴于关于当前疫情的错误信息应该是一个严重的问题,我对被归类为公共卫生的推特数量之少感到惊讶。我从争议类别中随机选择了一些推文,因为它是最大的,我担心分类的准确性。

***# Sample tweet 1:***
'text': '*Screenshot all of the tweets that are wishing death to POTUS amp FLOTUS There will be a reckoning I reckon*'
'label': ['controversy'] 
'score': [0.7567790150642395]***# Sample tweet 2:***'text': '*Joe Biden still has not denounced pedophilia*'
'label': ['controversy'] 
'score': [0.9125083088874817]***# Sample tweet 3:***
'text': '*RT I m not antivaxx I m anti some paid off quack injecting me poison*'
'label': ['controversy'] 
'score': [0.5249272654056549]***# Sample tweet 4:***
'text': '*This senile, drunkin snatchbag is admitting she's going to do whatever it takes to steal the oval office. They R evil & corrupt down to the bone! They ONLY care about themselves!Especially Nasty Nancy Pelosi*'
'label': ['controversy'] 
'score': [0.6447234153747559]

我认为争议是一个太广泛的话题标签,我应该添加具体的阴谋类别作为话题标签,如医疗,政府,媒体,或其他阴谋。第三个样本推文,在我看来,因为它反对疫苗接种,应该更好地归类为“公共卫生”,或者更准确地说是“医疗错误信息”。最后一条有争议的样本推文更适合“仇恨”这个主题,这是我的疏忽,因为我没有在最初的实验中把“仇恨”这个主题作为一个标签。

此外,快速浏览标签为“政治”的推文,会出现以下推文:

***# Sample tweet 5:***
'text': '*Everygreen was Hilary Clinton's secret service name Googling that name you ll see these containers*' 
'label': ['politics'] 
'score': [0.502316071033478]

这条推文的标签分数只有 50.2%,这意味着它勉强被归类为“政治”,我觉得它更适合“政府阴谋”的主题标签。因此,对于未来的测试,我将修改主题标签,以更具体地说明争议或阴谋的类型,并包括“仇恨”作为一个主题。

隐藏在众多有争议的推文中的,是颠覆性的组织尝试;含糊威胁的声明,以及混淆视听的笨拙尝试。

***# Sample tweet 6:***
{'text': '*Anons, if things go like we hope they’re going to go; we are going to be needed now more than ever. Be ready.*'
'label': ['controversy'] 
'score': [0.7214859127998352] ***# Sample tweet 7:***
'text': '*Be prepared TRUST the plan Conspiracy NO MORE We are in full control PAIN wakeUp thinkForYourself pub read 1425*'
'label': ['controversy']
'score': [0.6223780512809753]

考虑到这种类型的推文的流行,另一个可能明智的话题是“组织”或“煽动”。

对结果的初步观察让我相信,来自 QAnon 附属账户的大多数推文都是有争议的,所以“争议”作为一个话题模糊了结果。然而,我相信这些结果显示了阴谋趋同的证据,我在观看众议院情报常设特别委员会(HPSCI)关于错误信息和阴谋主题的虚拟公开听证会时熟悉了这个术语。在听证会上,妮娜是指,

"当一种理论的追随者接触到并被鼓励传播其他理论时,就是共谋收敛."

最初的 QAnon 阴谋已经和许多其他的边缘阴谋纠缠在一起,比如反 vaxxers,平地球论者,或者大屠杀否认者。社交媒体平台未能保护用户免受错误信息的影响,这意味着加入一个庆祝 Vinyasa 瑜伽的脸书团体,可能会让你接触到有害的医疗错误信息。

在这个信息时代的中心是一群有影响力的人,他们不成比例地运用权力对现实产生负面影响,带来危险和破坏性的后果。几个月前,世卫组织举行了他们在 T4 的第一次信息学术会议,这清楚地表明了将错误信息视为对公共健康的威胁的重要性。世卫组织网站上写道:

“就像流行病中的病原体一样,错误信息传播得更远更快,增加了卫生应急响应的复杂性。”

尽管存在错误信息的危险,但极有可能的是,QAnon 将继续利用社交媒体达到邪恶的目的,如破坏公民社会,传播有害的医疗谎言,并将所有这些与仇恨议程联系起来。物理学家尼尔·约翰逊在今年早些时候发表的一篇论文中谈到了反疫苗团体中种族主义观点的扩散,

“围绕新冠肺炎的恐惧和错误信息的增加,使得恶意事件和仇恨的推动者能够围绕共同感兴趣的话题与主流受众接触,并有可能将他们推向仇恨的观点”

我的零枪击分类实验突然出现了几条反疫苗接种的推文,足以让我相信我需要一个特定的医学错误信息主题标签,或许还需要一个疫情阴谋的额外标签。

总结想法

我尝试使用零镜头分类对推文进行主题标注,结果很有希望。就目前而言,尝试其他主题标签和策划其他调查数据集将会很有趣。由于更改主题标签非常简单,我的下一组测试将是尝试其他主题标签并调整类别的数量。为了在保持交互性的同时在生产规模上使用我的零射击分类器,我当前的 web 应用程序将需要进一步的工作,以通过缓存加快预测,并集成更多的探索性数据分析工具,使其更加有用。

在测试零枪击分类时,我遇到的最有趣的概念是用流行病学家用来跟踪致病传播的机器学习方法来跟踪错误信息的想法。一个有趣的探险想法!

希望我的实验能启发你尝试一种新的、无监督的、开箱即用的机器学习方法的零镜头分类。我鼓励你们思考潜在的社会意识,公平信息研究的应用。

有兴趣合作吗?在 Linkedin 上联系我

分析最近的失业数据

原文:https://towardsdatascience.com/analyzing-recent-unemployment-data-f4d1ff7da2b1?source=collection_archive---------41-----------------------

纽约公共图书馆在 Unsplash 拍摄的照片

使用一些图表和统计数据来更好地理解现在就业市场的情况

如果你最近一直在关注经济新闻,那么你可能已经看到了这张首次申领失业救济金的图表:

首次失业索赔(资料来源:圣路易斯美联储银行)

这是一个巨大的增长,使得 2008 年的大金融危机(当时金融系统几乎结束)看起来像一个小丘。看到这张图表,我可以想象出三种不同的反应:

  1. 哦,太棒了,创纪录地高了一英里!这一定是大萧条 2.0。
  2. 这都是暂时的;一旦疫情曲线变平,一切都会好起来。
  3. 什么是首次失业索赔?

先回答最后一个问题。首次申请失业救济人数(有时也称为新申请失业救济人数)是某一周首次申请失业救济的总人数。所以你可以想象,它与裁员高度相关(如果有大量裁员和/或解雇,那么许多最近失业的人将首次申请失业救济金)。

正确的答案可能介于反应 1 和反应 2 之间。是的,最初的失业索赔高得惊人。但是,请记住以下几点:

  • 众所周知,首次申领失业救济金是不稳定的(看一下 4 周的滚动平均值,而不仅仅是最近的数字,通常会有所帮助)。此外,看看持续申领失业救济金的人也很有帮助。
  • 首次申领失业救济金人数不是一个净数字,这意味着它代表了申请失业的新人数,但它没有扣除等式的另一个关键部分:找到工作和享受失业救济的人数。例如,如果上周首次申请失业救济人数增加了 80 万,本周增加了 20 万,这并不意味着美国新增了 100 万失业人口。本周增加的 20 万没有告诉我们上周申请失业救济的 80 万人发生了什么(有些人可能已经找到工作,离开了劳动力队伍,等等)。)
  • 当前的危机异常尖锐——许多大城市的经济在几天之内从正常运转到关闭。没有公司通常有的那种“在开始裁员之前,让我们等一等,看看我们是否处于衰退之中”的态度。相反,从关闭开始的那一天起,很明显,经济将受到重大打击。因此,从数据的角度来看,这次衰退也是独一无二的。通常系统会受到冲击(抵押贷款违约、油价飙升等)。),然后每个人都等着看这种冲击如何逐渐影响到就业市场。这一次一切都反过来了:疫情迫使大多数人停止工作,现在我们等着看所有减少工时、休假和裁员将如何继续损害全球经济增长。
  • CARES 法案(美国政府最近通过的刺激方案)为零工经济工人、自由职业者和其他个体经营者打开了获得失业救济的大门。以前,他们没有资格。因此,有资格享受福利的人的基数暂时增加了很多。
  • 根据《关怀法案》,工资大幅下降的休假工人也有资格领取失业救济金。与过去的衰退相比,这也增加了符合条件的人的基数。此外,这些人仍然是技术上的雇员,一旦封锁结束,他们可以无缝地恢复工作(并获得报酬)。

因此,尽管首次申领失业救济金的数据预示着失业率和经济的坏消息,但它可能并不像看起来那么糟糕。让我们使用一些统计数据来试图弄清楚最近的数据如何影响失业率(这是经济结果的一个非常重要的驱动因素)。

如果你想知道:“如果他们已经公布了就业率,为什么我们还需要建模?”问得好, 这是因为失业率数据是按月发布的,通常比失业救济数据的变化要慢。因此,我们想看看使用申领失业救济金的数据(每周在周四公布)是否能让我们提前了解最终公布时失业率可能达到的水平。

失业索赔与失业率

让我们从目测失业索赔和失业率之间的关系开始。有两种类型的索赔数据:初始数据和持续数据。前者就像我上面解释的那样,仅限于新的申请人,换句话说就是最近被解雇的人。后者继续计算超过第一周仍在领取失业救济金的人数。在这两者中,首次申领失业救济金更及时,而持续申领失业救济金的波动性更小,与失业率的联系也更紧密。你可以把它想象成首先发生首次申领,然后其中一部分流入持续申领,形成失业人口的基础(但不是全部)。

为什么我们如此关心失业率?

消费者支出约占国内生产总值的 70%。这是驱动美国经济其他部分的引擎。但是我们只有被雇佣了才能消费。没有就业,支出就会下降,导致企业利润(对商品和服务的需求减少)、税收(给政府预算带来压力)和资产价格(购买股票和房屋的人减少)下降。这也造成了一个负反馈循环,失业人数的增加削弱了经济,这反过来又导致更多的失业。

我在下面画出了失业率。不出所料,它在衰退期间会增加(灰色阴影)。在疫情之前,失业率接近历史低点,然后在 3 月份急剧上升。

失业率(资料来源:圣路易斯美联储银行)

一些图和数字

让我们来看看这两类索赔与失业率的关系。首先,让我们绘制一个首次申领失业救济金人数与失业率的散点图。我排除了最近的首次索赔高峰,以便我们可以看到这种关系通常是什么样子:

首次申领失业救济金人数与失业率(无峰值)

存在近似线性的关系。现在让我们添加最近的峰值。让我们看到的是一生一次的论点变得可信,增加过去 2 周的数据点(橙色点)打破了我们的散点图。

首次申领失业救济金人数与失业率(有峰值)

即使我们试图使用蓝点(以及初始申领人数和它们所暗示的失业率之间的最佳拟合线)来推断预测的失业率,结果也不会有意义。它将大于 100%,这是不可能的。

那么,我们如何使最近的数据与历史(峰值前)统计关系相一致呢?我的猜测是,“实时真实”失业率目前非常高,甚至可能高于 2008 年衰退最严重时期。但是,它并不像最初的申请数据让我们相信的那样高,因为前面提到的《关怀法案》(CARES Act)的影响(该法案将失业救济开放给更广泛的人群,包括只是暂时休假的人),最初的申请数据的内在波动性,以及就业反弹的可能性(如果下周封锁就这样结束,那么本周首次申请失业救济的很大一部分人将重返工作岗位)。

重要的是要记住,并非所有的失业都是平等的。暂时但急剧的需求减少导致的失业(就像现在)比公司或整个行业永久破产导致的失业(这些工作或多或少已经一去不复返)更糟糕。当我们试图衡量失业时,我们更想抓住后者(长期类型)而不是前者。我猜想,截至目前,很大一部分初始索赔数据是高度临时性的。当然,如果封锁持续下去,更多的企业因此倒闭,那么目前看似暂时的失业将越来越成为长期甚至永久的失业。

回到数据上来,我认为持续的失业申请比首次申请更真实地反映了实际失业情况。这是至少两周前申请失业救济的人数。这意味着数据有点滞后(相对于最初的索赔 1 周)——通常不太好,因为我们通常更喜欢及时的数据。但这对我们的目的来说很好,因为我们对首次申请失业救济人数的波动性以及他们实际上会有多少流入失业率持怀疑态度。让我们画出散点图。和之前一样,峰值前的数据是蓝色的,最近 2 周的数据是橙色的。

持续申领与失业率(有峰值)

橙色的点仍然是异常值,但比最初的声明要少得多。这是有道理的,因为持续索赔有点像初始索赔的滞后和平滑版本。我们可以利用这个。

有趣的是,这个图实际上暗示了数据中可能有不止一条趋势线(每条都有自己的斜率)。换句话说,一些其他变量(可能是通货膨胀或利率)定义了我们所处的制度,而制度定义了持续申请和失业率之间的β(斜率)。在我的下一篇博客中,我将建立一个更深入的失业率模型,我保证会进一步探讨这个问题。但今天,我们只是想利用持续申领数据来猜测实时真实失业率。

预测未来

我们可以使用简单的线性趋势外推法(单变量线性回归)来了解当前的失业率应该是多少。我们其实并不想拿当月的失业率和当月的申领数据做比较。相反,我们希望将未来的失业率(前移 1 个月)与当前的申领数据进行比较。请记住,持续的(初始的)申领失业救济金意味着未来的失业率——例如,2020 年 4 月的申领失业救济金数据(已经公布)背后的人是 5 月初几周后最终公布的失业率的一大驱动力。支持这一假设的事实是,持续失业申请人数实际上与下个月的失业率(0.82)的相关性略高于同月的失业率(0.80)。

因此,我们可以建立以下线性回归,使用连续索赔来预测失业率:

Y = B0 + B1*(本月第 m 个连续索赔)

其中 Y =下个月的失业率

让我们绘制未来(下个月)失业人数与本月持续申领失业救济金人数的散点图(蓝色部分)。我们还将使用之前的线性回归方程计算出的预测值(红色)添加到图中。我们的简单模型预测,2020 年 5 月的失业率将为 13.6%。

失业率预测

13.6%是一个相当高的数字。如果模型是正确的,这肯定是我们在过去 50 年中看到的最高失业率。但这仍远低于美国在大萧条最严重时期经历的 25%的失业率。因此,尽管情况很糟糕,但目前与大萧条的比较看起来有些夸张。

失业率和预测

结论

我真的希望被证明是错的。13.6%的失业率可不是闹着玩的,如果封锁持续的时间比预期的长,更多负债的企业将会倒闭,更多的人将会失业。这就是为什么政府和美联储如此努力地保持目前的经济现状。拯救一家企业(现在就给它钱)比让它倒闭并希望它被新企业取代的成本要低得多。即使考虑到在管理不善的企业上浪费的资金,这种情况也很有可能是真的,这些企业或许应该被允许违约。当然,这种类型的支出不会永远持续下去,所以我们必须在政策制定者耗尽资金之前将曲线变平。

下一次,我们将构建一个更好的回归模型,并深入研究代码。在那之前,干杯,祝大家平安!

分析拼车数据

原文:https://towardsdatascience.com/analyzing-rideshare-data-a7c83f95cd65?source=collection_archive---------22-----------------------

构建应用程序定量主干的过程

原 app 描述

在过去的十年里,优步、Lyft、Gett、Juno 和 Via 等叫车服务已经彻底改变了运输领域。然而,司机的盈利能力在所有这些平台上以复杂的方式变化。司机被迫根据一天中的时间、激增率、搭载乘客的等待时间等进行心算。并且基于这些因素的优化选择在任何点上为给定的服务乘车。由于这个困难的优化问题——考虑到潜在因素的数量以及许多未知因素——司机无法确定他们选择了最有利可图的服务。 Ride King 通过根据驾驶员的位置以及驾驶员打算驾驶的时间,汇集每项驾驶服务的历史和最新数据,消除了这种不确定性。骑王的最终目标是最终将这种优化服务扩展到骑手,这样他们就可以在任何给定的时间点选择最便宜的乘车公司。针对乘客和司机的各自优化的融合将有望从每个拼车服务中转移足够的注意力,以产生更好的透明度和/或对司机的更大回报。

骑王:智能骑

一看原始数据

这个应用想法的数据收集绝不简单。为了避免他们的信息被用于竞争目的,大多数拼车公司都会竭尽全力保护他们的数据。然而,经过一番深思熟虑,我们实际上可以发现一些访问这些数据的后门方法。纽约市的所有拼车应用都在出租车和豪华轿车委员会(TLC)的监管之下,法律要求它们提交每次乘车的数据。通过一些调查,敏锐的观察者会发现 TLC 是纽约市开放数据倡议的一部分,该倡议旨在增加州和城市事务的透明度。虽然这些拼车公司提供的数据非常肤浅(每次拼车的信息非常少),但我们仍然可以从中获得相当多的信息。然而,首先,我使用的是 2014 年 4 月的优步数据(由该公司慷慨提供, fivethirtyeight ),其格式与 TLC 数据集略有不同。

来自 2014 年 4 月纽约市优步区 fivethirtyeight 数据集的示例行

快速浏览一下,我们可以看到该集合中的每一行都提供了日期、时间、取货地点(在地理坐标中)和“基地”。我们将稍后讨论最后一篇专栏文章——现在,让我们只看一下原始数据。请注意,虽然我们可以只绘制原始地理坐标,但我实际上已经覆盖了 Zillow 慷慨公开的纽约市社区边界数据。这为我们的数据增加了另一个维度!

曼哈顿中/下城 2014 年 4 月的所有优步皮卡

好吧,酷。因此,我们现在可以看到 2014 年 4 月在曼哈顿的所有优步皮卡(为了可读性,进行了缩放),包括街区边界等!由于这个项目的目标是试图根据一天中的时间和位置来预测拼车服务的盈利能力,到目前为止我们只是触及了表面,但是我们已经可以看到拼车绝对是而不是均匀分布的!

最受欢迎的社区

深入

现在让我们看看这些 rideshare 服务上传到 TLC 的实际数据。

2018 年所有纽约市拼车服务的 TLC 开放数据集

让我们从回顾最后一列开始(第一个数据集中的“base”,现在是“Dispatching_base_number”)。这些数据的最后一列是与每个驾驶员报告的基数相对应的代码。这些基地中的每一个都属于一个特定的 rideshare 应用程序,但当前的格式使分析变得复杂,因为我们不确定哪个基地对应于哪个公司。在花了一些时间在政府文件中搜索每个基本代码的所有权后,我们能够创建一个字典,轻松地将混淆的基本代码与我们熟悉的名称(例如,优步、Lyft、Gett、Via、Juno、Ola、Didi 等)进行转换。).一旦我们将接送 id 映射到实际的社区,我们就可以开始做一些有趣的事情了…

平均值。诺曼曼哈顿 Lyft 和优步 2018 年一天的皮卡数量。达到各自的最大值

作为我对这个应用程序想法的分析的一部分,我实际上花了几个月时间询问拼车司机这个问题,绝大多数人告诉我,最大化他们利润的最佳方式是最小化每次乘坐的持续时间。我做了一些补充分析来进一步探索这个问题,根据每个拼车应用程序的定价模型,最小化乘车时间实际上似乎是最大化利润的最佳方式。让我们继续分析,再次记住我们现在的目标是通过基于位置和时间寻找最短持续时间的乘坐来预测盈利能力。

使用 TLC 数据集中的接送时间,我们可以很容易地计算出数据中每一行的乘坐时间。通过聚集邻近地区的数据,我们可以分析一天中每个小时和每个拼车服务的平均乘车时长。我们在这里希望看到一个服务的持续时间相对于其他服务有显著的下降。

纽约市 Meatpacking/W. Village W .街区的 Via、优步、Lyft 2018 年全年骑行时长比较(平均值,SD)

仅仅看一下轨迹,每个服务的乘车时长之间的间隔很小,而且有如此高的方差,我们会疯狂地认为我们可以从附近或一天中的时间可靠地预测持续时间。

让我们再看一些:

啊!还是同样的问题…这就是我的应用程序想法的问题所在:位置和一天中的时间不足以预测乘车时间。这完全有道理——你会放心用 100 美元赌你朋友在街上看到的某个随机优步的旅行时长吗?很遗憾数据显示了如此大的差异,因为这是一个非常令人兴奋的概念。我肯定有办法创造性地向数据中添加功能,以实现更好的预测,但就目前情况而言,我并不完全相信历史数据足以预测拼车服务的未来。

虽然我最终没有进一步追求这个应用程序(数据第一,总是!),这是一次非常有趣的经历,也是我第一次对数百万行数据进行大规模分析,在这种情况下,代码效率成为一个重要因素。

非常感谢你的阅读!

奖金调查结果

不足为奇的是,降雨会影响到优步游乐设施的数量。这里的每个点代表 2014 年的一天,我将 rideshare 数据与历史天气报告数据结合起来,以确定当天的最大降雨量(****,皮尔森的 R 临界值)

Lyft(粉色)对优步(黑色)的平均得分。随机选择的社区在 2018 年的骑行时长

使用 Python 和 SciPy 通过傅立叶变换分析季节性

原文:https://towardsdatascience.com/analyzing-seasonality-with-fourier-transforms-using-python-scipy-bb46945a23d3?source=collection_archive---------8-----------------------

通过寻找 911 电话数据中的季节性趋势,学会从噪音中分离信号

最后,你将能够用你自己的数据做到这一点

分析 911 电话呼叫的季节性

俗话说,历史重演。理解这些模式有助于我们做出更明智、更有准备的决策。挑战在于将图案从周围的噪音中分离出来。我们可以使用工程师常用的傅立叶变换来实现这一点——从噪声中分离出信号。

今天,我们来分析一下宾夕法尼亚州蒙哥马利县的 911 电话数据。我们希望回答在以下时间段内 911 报警电话的数量是高还是低:

  • 一天中的特定时间?
  • 一周中的某些日子?
  • 一年中的某些月份?

根据调查结果,我们可以决定如何为 911 呼叫中心配备人员。例如,如果我们发现周五晚上的呼叫量最高,我们可以在周五晚上提供更多的班次,这样我们的呼叫中心就可以处理更高的呼叫量。

傅立叶变换有什么作用?

傅立叶变换允许您将时间和信号的函数转换为频率和功率的函数。这告诉你什么频率组成你的信号,以及它们有多强。在我们的例子中,信号是电话的数量,我们可能期望某种每周或每天的频率。

在左侧,我们绘制了两个正弦波的和,一个周期为 5,频率为 1/5=0.2,另一个频率为 1/10=0.1。在傅立叶变换中,我们可以清楚地看到,我们有两个频率为 0.2 和 0.1 的波,通过查看峰值对应的频率。

真实数据通常包含噪声,傅立叶变换让我们看穿噪声,并了解哪些频率真正重要。

我们从之前的信号中加入随机噪声,我们仍然可以在傅立叶变换中清楚地看到信号的相同频率。这就是傅立叶变换从噪声中分离信号的方法。

本文在这里不深究傅里叶变换的数学和推导。如果你有兴趣,我推荐你在完成这个练习后观看 3Blue1Brown 的傅立叶变换的可视化介绍。我建议先做这个练习,因为傅立叶变换是这样一种概念,从一个实际例子开始会帮助你理解它背后的数学。

数据准备

首先,让我们导入并准备呼叫数据。为了跟进,您可以克隆这个 Github repo 并按照那里的说明进行操作。这里的代码是。原始数据来自 Kaggle

对于数据准备,让我们转换原始数据来计算每小时的调用次数。我们在小时级别上汇总呼叫数,因为分钟级别的呼叫量太低,我们不希望看到小时级别以下的任何季节性。根据经验,您希望采样频率是信号中预期最高分量频率的两倍。如果你的频率再低一点,一种叫做混叠的情况就会发生,并扭曲你的结果。满足“2 倍最高元件频率”规则的最小频率称为奈奎斯特速率。直觉上,这个概念是有意义的,因为我们不能通过计算每天的电话数量来回答一天中的某个时间是如何影响电话数量的。

此外,我们需要确保用零填充任何丢失的小时(没有 911 呼叫的地方)。最后,对于信号,让我们用图表来表示与平均调用计数的差异,而不是调用计数本身。这样,我们的数据以 0 为中心,就像一个真正的正弦波。

第一周的数据绘制在右侧。这里肯定有一些季节性,所以看起来我们的分析很有希望。

傅里叶变换

我们将使用 SciPy 软件包中的傅立叶变换子模块— scipy.fft。我们将使用 SciPy 快速傅立叶变换(scipy.fft.fft)函数来计算傅立叶变换。如果您熟悉排序算法,可以将快速傅立叶变换(FFT)视为傅立叶变换的快速排序。FFT 是一种更有效的计算傅立叶变换的方法,也是大多数软件包的标准。

只需将输入数据传递给函数,它就会输出转换的结果。对于振幅,取结果的绝对值。为了得到相应的频率,我们使用scipy.fft.fftfreq。我们可以绘制振幅和频率的图表。振幅最高的频率表示季节性模式。低振幅的频率是噪声。此外,scipy 的周期图功能也可以让你看到类似的图表。让我们标记出我们可以清楚地看到振幅峰值的频率。

如果我们查看那些振幅最高的频率,并将其转换为小时和天,我们会看到顶部季节模式有一个每日频率(周期约为 1 天)。

之后,振幅急剧下降,我们看到 8 小时和 7 天的季节性。前者表明一天有 3 次通话量高峰(可能是早上、晚上和深夜?).后者表明通话量在一周中的某一天达到峰值。

其他频率很难联系上下文,但鉴于它们的低振幅,它们不是很重要。

傅里叶逆变换

我们的分析目前还不太可行。我们知道每天都有季节性,但不知道一天中的哪个时间实际上有更高的季节性。为了解决这个问题,我们可以使用傅立叶逆变换。理论上,这可以让我们转换滤波结果,只查看信号。

如果我们针对前 5 天的数据将滤波后的信号与原始信号进行对比,结果会是这样。

看起来很有希望!过滤信号中的峰值在下午 5 点左右与原始信号对齐。问题是,当我们将这延伸到最后一周的数据时,峰值反而在上午 11 点开始出现。

那么是什么原因呢?问题是我们的频率不是每 24 小时一次。实际上是每 23.996 小时一次,在整个数据集的过程中,这个小的偏差累积起来。

我们能做什么?

因此,我们已经回答了最初关于数据中的季节性的问题,但我们还不能准确回答季节性何时达到峰值。为了让我们的分析更上一层楼,我们需要将季节性纳入我们的回归模型。

这将有助于我们通过尝试受傅立叶结果启发的不同输入来找出季节性高峰的时间。此外,这将允许我们在回归模型中结合季节性和其他变量,以便我们可以更准确地预测未来的呼叫量。我们还将看到季节性是如何被用来解释回归模型中的残差的。

要了解更多关于分析季节性的信息,请查看下面链接的文章,在这篇文章中,我讲述了如何在回归模型中使用傅立叶分析。

[## 如何将傅立叶项添加到回归和季节性分析中(使用 Python 和 SciPy)

使用傅立叶项将季节性包括在回归中

towardsdatascience.com](/how-to-add-fourier-terms-to-your-regression-seasonality-analysis-using-python-scipy-99a94d3ae51)

有用的链接

Github 上的项目代码

分析市议会会议的情绪

原文:https://towardsdatascience.com/analyzing-sentiment-of-city-council-meetings-462214d2dbc6?source=collection_archive---------44-----------------------

情绪能表明会议中值得注意的部分吗?

来自 Pixabay凯文·诺里斯的图片

上周末,我参加了我的第一次黑客马拉松:2020 年圣黑客日,由民主实验室的伟大人物举办。这真是一次丰富的经历,我开始着手一个非常酷的项目!

委员会数据项目的目的是通过使市议会的活动不仅被立法者发现和理解,从而赋予公民和记者权力。这里是西雅图的现场直播。西雅图不提供他们会议的文本记录(只有视频记录),委员会数据项目自动创建文本记录,将其编入索引,然后在线提供。我一直想探索情感分析,并认为这是一个绝佳的机会!

情感分析是“文本的上下文挖掘,识别并提取源材料中的主观信息。” 具体来说,就我们的目的而言,我想探究每个发言者的情绪,看看情绪的变化,或情绪的总体高/低时期,是否表明发言者之间的辩论中有有趣的部分或时期。

在当前形式下,文本副本数据是无标签的,这意味着数据只是没有附加分类器的文本。这将我们可以使用的模型限制为无监督的模型。我确定了两个模型来尝试——NLTK 的 VADER 和 TextBlob。也有 IBM 的 Watson,但是它有价格标签,并且不能很好地扩展。在这个例子中,我将只使用 VADER,但我想很快用 TextBlog 试试。VADER 预先接受了社交媒体方面的训练,因此它不是这种情况下的完美工具。

我们来看看笔录!首先,我们需要导入我们将使用的所有模块,并从 CDP 下载数据:

**import** **pandas** **as** **pd**
**import** **nltk**
**from** **nltk.sentiment.vader** **import** SentimentIntensityAnalyzer
**from** **cdptools** **import** CDPInstance, configs
**import** **altair** **as** **alt**
**import** **numpy** **as** **np**
**import** **json**# connect to Council Data Project database
seattle = CDPInstance(configs.SEATTLE)*# Download a specific event, save the JSON*
manifest = seattle.get_transcript_manifest()found_event = manifest.loc[manifest.event_id == "bb35a74c-53db-40f7-9af0-c0b296c2696a"].iloc[0]save_path = seattle.file_store.download_file(found_event.filename)# Load transcript JSON file
**with** open(save_path, "r") **as** read_in:
    transcript = json.load(read_in)
    **for** s **in** transcript["data"][:2]:
        print(s){'speaker': '', 'data': [{'start_time': 12.679, 'end_time': 20.186, 'text': 'Good morning, and welcome to the February 11, 2020, meeting of the public safety and human services committee.'}, {'start_time': 20.186, 'end_time': 41.808, 'text': "It's 9:36A.M., and I am Lisa Herbold, the chair of this Committee and Councilmember to District 1\. I call the meeting to order, welcome to my Cocommittee members, council member Morales and Councilmember Lewis."}, {'start_time': 41.808, 'end_time': 45.178, 'text': 'I want to go over the agenda, quickly.'}, {'start_time': 45.178, 'end_time': 47.914, 'text': 'Today we will start with approval of agenda.'}, {'start_time': 47.914, 'end_time': 86.453, 'text': "Followed by public comment and the items of business include, appoint of two members of the community police Commission, hearing from the office of the inspector general about her 2020 work plan, and also a presentation from the human services Department that will include a focus on the Hsd, the human services director's report, and then a focus exclusively on the Nonhomelessness investments side of the human services side of the portfolio."}, {'start_time': 86.453, 'end_time': 92.225, 'text': 'If there are no objections, I would like to approve the agenda.'}]}

每当有人说话时,它会列出他们说的每一句话,以及开始和结束的时间。它目前不知道实际上是谁在说话(这就是为什么 speaker 是一个空字符串)。我编写了一个函数来解析这个 JSON,并将每个发言者及其句子存储在一个列表列表中。现在让我们来看看这种情绪!

# Download vader lexicon, and instatiate a SentimentIntensityAnalyzer objectnltk.download('vader_lexicon')
sid = SentimentIntensityAnalyzer()def get_vader_score(sent: str) -> list:
    # takes in a string and then outputs VADER sentiment score
    ss = sid.polarity_scores(sent)
    out_list = []
    for k in sorted(ss):
        out_list.append([k, ss[k]])
    return out_list# Let's look at the eighth speaker's first sentence
get_vader_score(transcript[7][0])> [['compound', 0.6486], ['neg', 0.0], ['neu', 0.361], ['pos', 0.639]]

分数范围从-1 到 1,表示该分数的极性。1 为正,-1 为负(为了明确起见)。就我的目的而言,我们实际上只需要复合分数,所以我重写了上面的函数,只输出复合分数。如果你有兴趣了解更多,这里有一篇关于 VADER 如何计算分数的深入文章。

我最初计算了每个句子的情感,得到的图形非常嘈杂。

VADER 在市议会会议上每句话的综合得分

接下来,我画出了每个演讲者句子的平均情绪。这被证明是非常有用的。请注意,可视化来自单独的会议。

市议会会议中每位发言人的平均 VADER 综合得分

看到那个巨大的向下的尖峰让我们非常兴奋——就是它了!现在让我们来看看提供这一结果的扬声器模块:

“你可能会受到房东的欺负。我被房东欺负了。我们被房东欺负了。”

然而,考虑到这个模型是如何运作的,这个结果是有意义的。欺凌是不好的(它被提到了三次),所以模型会反映这一点,但它对我们的目的并不真正有用。

结论

总的来说,当你看许多会议时,情绪总是很高。这是有道理的,因为理事会成员通常是热情和专业的,议会程序加强了这一点。

因此,VADER 对我们的目的来说并不太有用。我想试试 TextBlob 的情感分析包,但我不认为它会更有用。一个没有接触过市议会会议的独特语言的模型将不能正确地分析这种语言。

展望未来,我认为探索这一点的下一步将是手动标记论点或辩论的要点,然后使用监督学习技术。祝我好运!

来源:

  1. https://towards data science . com/opinion-analysis-concept-analysis-and-applications-6c 94d 6 f 58 c 17

用数据科学分析音扇艺术

原文:https://towardsdatascience.com/analyzing-sonic-fan-art-with-data-science-fddcaa8bbb68?source=collection_archive---------48-----------------------

使用 BeautifulSoup 刮除异常值的教程

音速小子的粉丝已经达到了互联网上很少有粉丝享有的恶名程度。众所周知,这种艺术是扭曲的、令人不安的,而且在很多情况下是露骨的。在我最新的 Youtube 视频中,我搜集了 DeviantArt 来分析扇子艺术,以确定它是否真的名副其实。这篇文章将带你了解我是如何做到的。

我首先想了解一下 DeviantArt(一个粉丝艺术分享网站)上有多少声波艺术作品。这里不需要刮擦——我只是在 DeviantArt 上搜索“Sonic ”,并记录出现了多少结果。我也为相似的角色做了同样的事情,像史莱克和皮卡丘。以下是这些可视化的结果:

DeviantArt 上每个角色的粉丝数量。根据知识共享协议授权的作者和人物剪贴画。

献给索尼克的粉丝数量超过了其他角色,达到了 140 万左右。哇!既然我们已经看到了 DeviantArt 上蓬勃发展的声波文化,是时候回答我们的研究问题了:声波粉丝艺术真的那么令人不安吗?

首先,我使用 BeautifulSoup 来收集帖子(完整代码可以在 Github 的这里获得)。下面的代码抓取了搜索“sonic”时出现的前 2200 个链接

base_url = "[https://www.deviantart.com/search/deviations](https://www.deviantart.com/search/deviations)?"
urls = np.array([])
for i in range(50):
    if i == 0:
        url = base_url + "q=sonic"
    else:
        url = base_url + "page=" + str(i) + "&q=sonic"
    request=urllib.request.Request(url,None,headers)
    if url in urls:
        pass
    else:
        bs = BeautifulSoup(urlopen(request), "html.parser")
        links = [item.get("href") for item in bs.find_all(attrs={"data-hook" : "deviation_link"})]
        urls = np.append(urls, links)

len(urls)

然后,我从每个 URL 中检索了几个属性,包括帖子的标题、标签以及每个帖子的浏览量、收藏和评论。下面是检索这些数据的代码:

for i in range(num):
    print(deviationurls[i])
    request=urllib.request.Request(deviationurls[i],None,headers) 
    bs = BeautifulSoup(urlopen(request), "html.parser")
    vals = [item.text for item in bs.find_all("span", class_="iiglI")]
    tag = [item.text for item in bs.find_all("span", class_="_3uQxz")]
    #print(vals)
    if len(vals) == 3:
        faves.append(vals[0])
        comments.append(vals[1])
        views.append(vals[2])
    else:
        faves.append(vals[0])
        comments.append(0)
        views.append(vals[1])
    tags.append(tag)
    titles.append(bs.find_all("h1")[0].text)

有时,注释字段为空,因此出现 if/else 条件。

这样做之后,你现在可以构建一个用于分析的声波风扇艺术品的数据框架!你的可能看起来和我的不一样,因为刮的时候可能会出现不同的艺术作品供你查询。但这是我的刮刀产生的数据集。

这是我的数据集中的艺术作品出版的年份分布。你可以看到 2010 年代的作品代表了大多数——也许人们在这个时候开始张贴更多的作品,或者也许那些作品只是我刮的时候首先出现的。

音帆作品出版年份分布。图片作者。

我不能在这篇博文中包含任何来自数据集的图片,因为我没有权利在这里重新发布它们。然而,我确实对我的视频中观看次数最多、最受欢迎的 10 件粉丝作品做出了反应。

在我的数据集中,两个被浏览最多的艺术品是人物塑造者(第一第二)。这并不奇怪,因为音速爱好者的很大一部分是制作原创角色,或“OCs”,并围绕这些角色写故事,就像任何爱好者一样。(然而,声波迷将这种消遣带到了一个新的水平。试着谷歌一下你的名字+“刺猬”,看看我在说什么)

动画短片漫画人物 参考文献也很受欢迎。

我的最终目标是分析 DeviantArt 上的艺术家使用的标签。哪些标签与哪些标签一起使用?为了做到这一点,我在我的数据框架中使用了“标签”列来创建一个关联矩阵。这是一个有点复杂的过程——第一步是创建一个用于解析的标签“语料库”。

data = np.unique(df.tags)[1:]
(data[0])
data_real = []
for strdata in data:
    str1 = strdata.replace(']','').replace('[','')
    l = str1.replace("'",'').split(",")
    l = [item.strip(' ') for item in l]
    data_real.append(l)texts = [[word.lower() for word in line] for line in data_real]
corpus = []
for text in texts:
 corpus.append(‘ ‘.join(text))
corpus

创建完语料库后,我使用 scikit-learn 创建了相关矩阵。

from sklearn.feature_extraction.text import CountVectorizer
cv = CountVectorizer(ngram_range=(1,1), stop_words = 'english') # You can define your own parameters
X = cv.fit_transform(corpus)
Xc = (X.T * X) # This is the matrix manipulation step
Xc.setdiag(0)
names = cv.get_feature_names() # This are the entity names (i.e. keywords)
df_tags = pd.DataFrame(data = Xc.toarray(), columns = names, index = names)
df_tags

最终的数据帧应该是这样的(是的,它会很大)。这里,数字表示两个标签相互使用的时间。

标签之间相关性的数据框架。图片作者。

我首先使用像 networkx 和 matplotlib 这样的 Python 库来可视化网络,但是这些都是难以分辨的混乱。很明显,我需要一个更强大的软件。

我开发的第一个网络。图片作者。

我将数据帧保存到一个 CSV 文件中,并输入到 gephi 中。在网络中,单词越大,使用的次数就越多。一个单词和另一个单词越接近,这些标签一起使用的次数就越多。在对网络功能进行微调后,它最终看起来是这样的:

声波风扇艺术标签网络。

这个网络显然是巨大的,但环顾四周是很有趣的——我建议花几分钟放大和环绕它,看看某些单词之间的联系。或者,你可以看一下我的总结这个分析的 YouTube 视频,看看我从网络上拿了什么。

就这样结束了!总之,虽然有令人不安或扭曲的声波风扇艺术的例子,但我认为我的分析表明,这肯定不是社区中最受欢迎的,甚至不是大多数作品。事实上,其中大部分是有益健康的。

如果你认为这篇文章还有很多需要改进的地方,请考虑观看我关于这个主题的视频。这篇文章更多的是解释我是如何做分析的。

分析股票投资组合

原文:https://towardsdatascience.com/analyzing-stocks-portfolio-d6035711dfc7?source=collection_archive---------26-----------------------

价值投资分析入门指南

电晕大流行期间的数据即和讲故事(2020 年 4 月)

来源(Unsplash)

更新我的价值投资股票组合

2019 年 5 月 22 日,我部署了我的投资仪表板,创建了我自己的投资组合,写了进度

[## 价值投资仪表盘,配有 Python Beautiful Soup 和 Dash Python

价值投资的 Web 抓取与快速 Dash 可视化概述

towardsdatascience.com](/value-investing-dashboard-with-python-beautiful-soup-and-dash-python-43002f6a97ca)

我一直用这个仪表板收集重要的比率和数字来计算股票价值。从那以后,我收到了更新我的股票投资组合的请求,尤其是在疫情时间。

值得庆幸的是,随着我目前的硕士学期即将结束,我慢慢养成了在家工作(WFH)的好习惯。我有时间重新评估和更新我的个人股票投资组合,尤其是在冠状病毒大流行的艰难时刻。这次疫情将是一次压力测试,看看我的投资组合表现如何。

免责声明:本分析是在 4 月 10 日进行的,这不是 3 月初科罗纳病毒疫情声明期间的股市低谷。我不是金融专业人士,也从未在投资机构工作过。相反,我是一名数据从业者,试图分析和交流想法,以解决问题并带来影响。我所做的所有投资组合和股票购买纯粹是为了我自己的个人利益和对我自己的价值投资工具和方法的评估。

结果

Tl:DR:在这次科罗纳疫情(2020 年 4 月 10 日)期间,我的投资组合收益是 22.98% (w/股息)和 21.02%(w/o 股息)。

这份投资组合分析包括我最近在低谷期(冠状病毒被标记为疫情——2020 年 3 月 13 日)购买的石油。然而,令人惊讶的是,如果你关注我在 2017 年和 2018 年期间的旧购买。我仍然从吉宝 DC 房地产投资信托基金高通获得了 75.87%** 的巨额股票收益。我分别在比特币崩盘和苹果诉讼后买了这些股票。**

我很惊讶我的投资组合仍然表现良好,尽管整个电晕病毒在股票市场大流行。这个数字表明,用保守的折扣率和风险计算,我在正确的安全边际(15%)内购买强劲的股票是正确的。

当你以便宜的价格买入强劲的股票时,你就不用那么担心市场了

为什么我在 2020 年 3 月 13 日买了石油股票

2020 年 3 月 11 日,世卫组织宣布冠状病毒为疫情,导致石油股票市场跌至 17 年低点,原因是现有的疫情以及沙特和俄国建立的价格竞争。这表明人们非常担心疫情会大幅降低石油股票价格。请随意在这里找到我部署的仪表盘

雪佛龙(CVX)

埃克森美孚(XOM)

强项

  1. 资产回报率高:资产回报率(ROA)是一个公司相对于其总资产盈利多少的指标。ROA 让经理、投资者、或分析师了解一家公司的管理层在利用其资产产生收益方面有多有效。雪佛龙和埃克森美孚的高 ROA(>50%)表明与其基础设施投资( Investopedia )相比收入强劲。在这种情况下,CVX 更有资格享有居留权。
  2. 利息保障率远远超过 20 倍:利息保障率是一个债务比率和盈利率比率,用于确定一家公司支付其未偿债务利息的难易程度。利息保障率超过 20 倍这一事实意味着,该公司不太可能无法偿还其投资的利息债务。

简而言之,我认为这些都是成熟的公司,它们甚至会在未来 10 年继续存在。石油消费将永远是需要的,而且不存在即将到来的类似可再生能源的威胁。

潜在威胁:

  1. 每股收益增长率波动:每股收益增长率表明,随着时间的推移,公司的盈利能力越来越强。对投资者来说,这将是一项有用的措施。在这种情况下,我们发现每股收益的同比增长率是波动的。但是,当我用行业比率匹配它时,它似乎是 2018 年美中贸易战发生时经济的反映。
  2. 净收入和长期债务之间的安全边际似乎很小:经过更多的研究,我意识到这是石油行业的典型特征。石油行业是基础设施重工业,在炼油厂或采矿的重型设备方面有大量投资。因此,这似乎是一个常见的场合。一个更好的指标是利息保障率,以观察公司多年来的健康状况。

总的来说,以下是一些警告信号和关键财务比率供您参考:

给定公司名单,找出投资的可行性
进入市场最少 10 年
有业绩记录(每年每股收益)
有效率(净资产收益率> 15%) —净收入/股东权益
确定操纵(ROA > 7%) —净收入/总资产
有小额长期债务(长期债务< 5
总收入)
低负债权益比
支付利息能力:(利息保障比率> 3) — EBIT /总资产
*

过度非持续性恐惧的好股票

油价低谷时的恐惧有很多原因,但我相信这种恐惧不会持续很久:

  1. 油价将随着经济复苏而回升:随着旅游和经济生产的停止,石油市场需求减少 25% 。然而,我相信从长远来看,科罗纳疫情赛季将会过去。随着经济复苏,会有大量的作品和旅游。油价就像一个被压缩的弹簧,一旦经济繁荣,就准备跳起来,比如在美中贸易战期间。航空、旅游和生产行业将从航班和生产低迷中复苏。油价长期不会被打压。
  2. 战争的代价是不可持续的。三月初,沙特和俄罗斯之间的石油价格战导致两国向全球能源市场供应廉价原油,使价格超过 50 美元/桶( Motley Fool )。这是非常低的,但我认为俄罗斯和沙特阿拉伯仍然无法维持这一水平。一旦这场争斗结束(很可能是石油输出国组织),我确信一切都会很快过去。

由于强势股票价格低,我决定将我的现金储备投入石油,目前我在 CVX 获得了 11.69% 的股票收益,在 XOM 获得了 16.47% 的股票收益,只有在经济完全复苏后,时间才能知道确切的总收益是多少。

投资作为分析和讲故事的工具

我认为,每个家庭都需要大量投资,才能对自己的财务状况做出明智的决定。在这个人们被解雇的艰难的疫情,拥有个人投资组合的风险非常高。希望这篇文章能成为你从定量和定性的角度思考投资的起点。

我个人喜欢看这些数字,并从这些数字中构思故事。此外,作为一名数据从业者,投资对我来说是一个很好的实践,可以锻炼我在分析和讲故事方面的技能和技能。NYU 大学教授估值的阿斯莫斯·达莫达兰在他的书中谈到了这种重要性。此外,请随意看看我的基础教程。

*** [## 12 分钟:熊猫和 Scikit 的股票分析-学习

使用 Python 快速分析、可视化和预测股票价格

towardsdatascience.com](/in-12-minutes-stocks-analysis-with-pandas-and-scikit-learn-a8d8a7b50ee7)

我希望在我整理完股票投资组合后,每个月都能发布这些更新。希望这些更新能激励你开始特别分析你的投资组合。

规则 1: 永远不要赔钱

规则 2: 永远不要忘记规则 1

—沃伦·巴菲特— Investopedia

最后…

我真的希望这是一本很棒的读物,是你发展和创新的灵感来源。

这个想法是从我的几个大学朋友开始的,他们热衷于管理自己的金融投资组合。他们给了我很多反馈,让我了解技术术语和家庭投资的常见问题。作为一名打算投资收入的应届毕业生,他们是我应该学习的合适人选。

请在下面的评论提出建议和反馈。就像你一样,我也在学习如何成为一名更好的数据科学家和工程师。请帮助我改进,以便我可以在后续的文章发布中更好地帮助您。

谢谢大家,编码快乐:)

关于作者

Vincent Tatan 是一名数据和技术爱好者,拥有在 Google LLC、Visa Inc .和 Lazada 实施微服务架构、商业智能和分析管道项目的相关工作经验。

Vincent 是土生土长的印度尼西亚人,在解决问题方面成绩斐然,擅长全栈开发、数据分析和战略规划。

他一直积极咨询 SMU BI & Analytics Club,指导来自不同背景的有抱负的数据科学家和工程师,并为企业开发他们的产品开放他的专业知识。

最后,请通过LinkedInMedium或** Youtube 频道 联系文森特*****

如何评价 Sweet Maria 的咖啡杯度量标准,以便更好地进行比较

原文:https://towardsdatascience.com/analyzing-sweet-marias-coffee-cupping-metrics-3be460884bb1?source=collection_archive---------45-----------------------

咖啡数据科学

检查 Sweet Maria's 的数据,帮助了解他们对咖啡的选择

完整声明:我喜欢甜甜玛利亚的咖啡,在过去的 6 年里,我经常从他们那里购买大部分的生咖啡。

我查看了他们咖啡等级的次级指标,以便更好地理解它们在咖啡相互比较中的作用。我用箱线图、相关性和主成分分析(PCA)来理解这些咖啡等级。这项工作是对大型 Q 级 CQI 数据库进行比较的另一个扩展,以了解他们的咖啡分级指标在区分咖啡方面有多有用。

杯突评分(修正的 Q 评分)

Sweet Maria 的与下面总结的 SCA 标准略有不同。很想看看甜度、均匀性和干净的杯子与其他数据相比如何。SCA 量表的这 3 个指标一开始都很完美,并且扣分,Sweet Maria 的指标让我们对咖啡有了更多的了解。

原始数据

从甜甜的玛丽亚那里提取数据并不容易。他们没有数据库可以提取,但是他们有 300 多种豆子的档案。正如在这篇文章中所讨论的,我手动提取了部分数据,通过这种方式提取数据,我为每种咖啡绘制了一个蜘蛛图。

来自https://www . sweet marias . com/Burundi-dry-process-gate Rama-aga hore-6431 . html经其许可

我编写了一个脚本来从这些蜘蛛图中提取子指标,并且我还能够检查数据的准确性。我将这些数据用于指标的相互比较分析。

分析

除了中美洲的一些异常值之外,非洲咖啡豆的得分分布具有最高的偏移。我想说这是由于加工类型,特别是干加工,但干加工的分布如此之广,我对此表示怀疑。

对于不熟悉盒图的人来说,这是一个图例:

作者的图片和其他图片一样

以下是区域和处理的总体指标。非洲的分数明显更高。就处理而言,干式处理的数据比其他数据具有更大的分布。

我们可以将 Q 值分解为分布广泛的子指标。希望这意味着他们能更好地辨别咖啡。

相互关系

相关性是衡量两个变量彼此相似程度的指标。高相关性并不意味着一个变量会引起另一个变量,而是当情况发生变化时,两个变量的涨跌幅度相同。我从一开始就假设一些分级变量会有很高的相关性,因为它们是从不同的时间点来看味道的。相关性可以是正的(趋势相同)或负的(趋势相反)。0 表示没有相关性。

我们可以查看每个参数以及它们之间的相互关系。看起来身体和一致性与其他任何指标甚至总分都没有什么关系。库珀的修正似乎与总分呈负相关,但考虑到库珀的修正是针对总分应该高于得分的咖啡,这是有道理的。香味和香气相互关联,这是意料之中的。香气和余味也一样。复杂性看起来比其他的更独立,但仍然对整体有用。

让我们按地区和方法对此进行细分,以了解每个分级指标与给定地区或处理指标的总分之间的相关性。区域之间肯定存在一些差异,在这些区域中,某些区域的均匀体或均匀性比其他区域具有更高的相关性。这意味着它们不是没有价值的指标,因为它们确实有助于区分咖啡的差异。

我们还可以看到处理对这些指标的评分有多大影响。这可以按地区和流程进一步细分。样本较少,所以遗漏了几个区域。

在这种情况下,均匀性对于非洲湿法比干法或其他方法更重要。南美咖啡和印度尼西亚咖啡的身体影响得分不同,因为它们分别呈正相关和负相关。

这在一定程度上肯定了我自己的非洲和南美咖啡豆混合策略有利于平衡不同的咖啡豆。我想知道使用这种类型的信息是否有助于发现新的混合物,而不是试错法。

我们可以用图表来比较南美和非洲的湿豆和干豆。他们大多是趋势,但他们有一些有趣的差异,特别是因为非洲豆不会像南美豆一样区分干湿。

主成分分析

PCA 意在将一组变量转换到一个新的空间,其中新的维度按照它们组成原始维度的可变性排序。一个简单的数据集可以在不损失保真度的情况下减少维数;在这种情况下,每个 Q-分数(甜玛丽亚的分级)由 11 个维度表示,但也许你不需要这么多来表示总分。也许你只需要三个主要部件或电脑。

从 PCA 空间来看,大部分指标对前 3 个 PC 的可变性有贡献,前 3 个 PC 包含了几乎 90%的可变性。

不同的地区和过程有各种各样的可变性。干法加工相对于其他加工似乎缺乏变化。大洋洲的情况类似,但样本量较小,因此这可能不是该指标的恰当用途。大多数地区都有很大的差异,这可以通过需要多少台电脑来捕捉差异来看出。

同样,我们可以查看 PC 的区域,并处理具有足够数据的区域。在非洲和南美洲,潮湿和干燥似乎是相似的。

与 CQI 的快速对比

我快速浏览了一下 CQI 和甜甜玛利亚的电脑,我的目标是稍后进行更全面的比较。所以我画出了前两个包含大部分辨别能力的 PC。CQI 的数据库比甜甜玛利亚的少得多。比起甜蜜的玛利亚,似乎更难看到 CQI 地区的分离。

总的来说,我发现 Sweet Maria 的指标包含了很好的可变性和辨别能力。每个指标对总分的贡献都是有价值的,这对于咖啡的分级非常重要。这与 CQI 的衡量标准或者更确切地说是 SCA 的衡量标准形成了对比,因为他们的三个衡量标准对于描述咖啡本身没有任何意义。

就个人而言,这让我非常兴奋,因为我在选择咖啡豆时依赖于他们的分级分数,所以这再次证明了他们是一家优质企业。有时,当人们有不同的口味分级系统时,你不知道这些指标在区分口味方面有多好,所以下一个有趣的练习是对 CQI 和甜甜玛利亚的分数进行更彻底的比较。

如果你愿意,可以在 TwitterYouTube 上关注我,我会在那里发布不同机器上的浓缩咖啡照片和浓缩咖啡相关的视频。你也可以在 LinkedIn 上找到我。

我的进一步阅读:

按地区、工艺、等级和价格分类的咖啡

家庭烘焙咖啡的经济学

咖啡豆脱气

解构咖啡:分割烘焙、研磨、分层以获得更好的浓缩咖啡

浓缩咖啡的预浸:更好的浓缩咖啡的视觉提示

咖啡的形状

搅拌还是旋转:更好的浓缩咖啡体验

香辣浓缩咖啡:热磨,冷捣以获得更好的咖啡

断续浓缩咖啡:提升浓缩咖啡

用纸质过滤器改进浓缩咖啡

浓缩咖啡中咖啡溶解度的初步研究

断奏捣固:不用筛子改进浓缩咖啡

浓缩咖啡模拟:计算机模型的第一步

更好的浓缩咖啡压力脉动

咖啡数据表

温度如何影响音乐偏好

原文:https://towardsdatascience.com/analyzing-temperature-and-music-preference-with-spotify-data-eabf68762f97?source=collection_archive---------38-----------------------

用 Spotify 数据分析一个国家的气温如何影响听众偏好。

sgcdesignco 在 Unsplash 上拍摄的照片

本报告是哥本哈根 DIS 大数据计算分析课程的最终项目成果。小组成员是迈克尔·达茨、姬满·金和我自己。

如果你想自己探索这些数据,请点击这里查看 GitHub:【https://github.com/brp616/Spotify-Temperature-Project/】T4。

介绍

来自斯堪的纳维亚的金属——来自拉丁美洲的舞曲——来自美国的嘻哈。这些只是我们在音乐流派和它们的发源地之间建立的一些联系。作为狂热的音乐爱好者,我们想探究这些流行观念是否在数据中得到证实。更深入地研究,我们想知道当地温度是否会在人们的听力偏好中发挥重要作用。毕竟,只有在寒冷的冬天躲在家里的丹麦人会喜欢一些焦虑的摇滚或平静的民谣,而在炎热的沙滩上跳舞的人会想听流行音乐或电子音乐,这才有意义。

为了说明这一点,我们决定使用不同国家顶级音乐流派的 Spotify 数据来构建一个交互式地图,并提供按温度显示全球听众趋势的视觉效果。在我们开始精彩内容之前,我们将向您展示我们是如何实现的,以便您更好地了解我们是如何获得我们的结果、我们的发现的潜在缺陷以及基础数据科学项目是如何执行的!

先决条件和定义

数据科学新手?这里有一些我们将会抛出的名称和术语,在继续之前你应该知道。

Python: 非常流行的数据科学编程语言,我们用它来获取和分析我们的数据。

NumPy,Pandas: 这些和其他你可能不认识的名字是我们引入 Python 的不同的库,它们允许我们轻松地执行强大的任务,而不必从头开始构建我们所有的系统。

数据抓取:将信息从网站导入保存在您电脑上的本地文件的过程。我们不得不在这个项目中收集一些数据,因为这些数据在现成的数据集中是不可用的。

API: 应用程序编程接口,基本上是数据科学家在请求数据时用于与网站或应用程序通信的语言。在我们的例子中,我们使用 Spotify 的特定 API 来获取各种歌曲的流派信息。

如果还有什么让你困惑,就在 Medium 上看看这里,或者谷歌一下你的答案!数据科学有很多很好的资源,我,写这篇文章的人,能够很快自学基础知识,你也可以。

数据收集和清理

数据清理,2020(彩色)

我们在这个项目中使用了 3 个主要的数据源,我将逐一解释。它们是:

温度数据

来自 Kaggle 的 Spotify 数据集

从 Spotify 抓取的流派数据

温度数据:

[## 气候变化:地球表面温度数据

探索 1750 年以来的全球气温

www.kaggle.com](https://www.kaggle.com/berkeleyearth/climate-change-earth-surface-temperature-data/data#GlobalLandTemperaturesByCountry.csv)

为了在我们的分析中获得国家的温度,我们使用了 Kaggle 的气候变化数据集,这基本上是伯克利地球表面温度研究数据的重新包装版本,该研究汇编了来自 16 个档案的超过 100 万个数据点。在我们的分析中,我们从 2013 年(该组中最后一个完整年份)的一系列日子中获取数据点,并对它们进行平均,以获得一个国家的温度指数。这种方法的一个潜在缺点是,通过将每个国家的热门歌曲与全国平均气温进行匹配,我们忽略了音乐偏好的区域差异,这些差异可能与美国等大国的气温相一致。

Spotify 数据集:

[## Spotify 的全球每日歌曲排名

53 个国家/地区每日播放量最高的 200 首歌曲

www.kaggle.com](https://www.kaggle.com/edumucelli/spotifys-worldwide-daily-song-ranking)

虽然 Spotify 听众数据的数据集都不完美,但这是最好的。Spotify 不发布任何关于地理听众的直接信息,即使在他们的 API 中也是如此,所以我们使用了退而求其次的东西——53 个国家的全球歌曲流行度排名。该数据集包含曲目名称、艺术家、Spotify 参考编号、图表日期和图表的国家代码。这是一个非常大的数据集,被证明是难以处理的,特别是在检索和匹配流派与曲目时(见 scraped data 部分)。时间根本不允许我们和整套设备一起工作。因此,我们通过创建一个新的数据框架,从每隔一个月的第一天开始创建排行榜,来对这一年进行快照。这个数据帧是我们分析的基础,因为正是从这里,我们将国家代码与温度数据进行匹配,并检索曲目信息,如流派和节奏。

抓取的数据:

这部分需要一些独创性。虽然我们的 Kaggle 数据给了我们关于曲目名称和艺术家的信息,但它并没有告诉我们关于音乐本身的更多信息。为此,我们必须找到源头——Spotify API。在 SpotiPy 库的帮助下,我们能够轻松地获取我们需要的东西,但有一些(大的)警告。虽然 Spotify 确实保存了流派数据,但它将流派分配给艺术家,而不是歌曲,所以我们必须使用 Spotify 来获取音轨的标记,从标记中获取艺术家,然后检索与该艺术家相关联的所有流派,然后将这些流派与歌曲配对。Spotify 的 API 也不喜欢你使用太多他们的带宽,所以他们在一个小时后切断你与他们服务器的连接,并让你再次请求访问。而且,每天过后,你都需要一个全新的请求。是的,这花了一天多的时间来运行,即使数据集有限。经过多次反复试验,我们通过将抓取过程分成几个步骤,并对触发周期性新 API 访问请求的已分析磁道数进行计数,解决了这个问题。这使我们能够获取每首歌曲的流派信息和音乐指标(速度、舞蹈性和声学)。请看下面,了解我们如何发出初始 API 请求以获取跟踪标记的过程,我们使用略有不同的请求重复该过程以获取我们需要的所有数据:

数据存储和操作

将音乐信息与国家和温度相匹配:

虽然我们最初试验了一个保存每首歌的流派和音频特征的单词矩阵包,但处理这种数据格式所需的内存几乎不可能,因为加载数据文件要么需要一天时间,要么会使我的编码笔记本崩溃。

相反,我们使用了 Python 内置的两种更简单的格式,列表和字典。为了对流派做到这一点,我们构建了一个字典,将数据集中每个唯一的曲目作为键,将流派列表作为值。然后,我们遍历原始数据框中的每首曲目,并添加了一个包含每首曲目流派的列。从这里,我们检查了数据框中的每个国家代码,并统计了每个流派出现的次数,以获得我们正在寻找的新词典-每个国家及其最受欢迎的流派,以及世界上最受欢迎的流派,以进行比较。在这本字典和另一本字典中,计数被转换为一个国家图表中所有歌曲的比例,我们只是用平均温度替换每个国家的名称,以构成我们热图和交互式地图的基础。

对于音频功能,我们使用了类似的程序,但在用国家名称代替温度之前,只是简单地对每个国家的速度、舞蹈性和声音进行了平均。

这个要点显示了我们是如何为每个国家建立流派计数的字典的。

构建交互式地图:

交互式地图显示了全球特定国家的平均气温,并将其与每个特定国家的最高气温进行比较。为了创建这张地图,我们从 Kaggle 收集了温度数据,并将其与 Geopandas(我们用来生成这张地图的库)可以读取的国家形状文件一起使用。

在清理数据以仅包含最近的温度数据之后,我们必须将温度数据与 shapefile 合并,以便它可以由 Geopandas 输出。合并这两个文件给了我们一个全球平均气温的地图,这只是我们想要看的一半。

然后,我们使用从 Spotify 收集的流派数据——一个几千兆字节大小的数据集,并对其进行解析,以获得每个国家的热门流派。还应该提到的是,地图上的许多国家都没有 Spotify 数据,所以我们不得不将这些数据排除在分析之外。我们使用 Spotify 数据重复了合并过程,并将其输入到 Geopandas 中,使用 Bokeh Python 库为我们提供了输出结果——这是一个交互式地图,显示了世界各地与天气数据相比的顶级流派。然而,在查看数据后,我们意识到,由于 Spotify 的流派分类系统,几乎每个国家的顶级流派都是“流行”,所以我们从数据集中剔除了这一点,并根据其余流派生成了我们的结果。结果就是你现在看到的这张交互式地图,上面有来自世界不同气候和地区的数据。

结果

A.交互式地图

我们的互动地图截图。显示美国的顶级流派。

看一看重新构建地图的代码,并使用交互式元素。正如你所看到的——从 Spotify 的类型数据来看,我们没有太多的相关性可以区分。流行音乐似乎在全世界都很流行,而地区偏好与温度几乎没有关联。

B.流派流行热图

此图显示了在各种不同气候下,与全球前 20 大最受欢迎的流派相关的最受欢迎的歌曲所占的份额,以及按国家组织的最受欢迎的流派,以供比较。暖色表示歌曲比例较高,因此受欢迎程度较高,而冷色表示歌曲较少。虽然很有趣,但这张图显示温度和流派偏好之间没有什么强有力的关系。在非常高的温度范围内,像 EDM、latin 和 reggaeton 这样的舞曲风格可能稍微更受欢迎,但是只有 dance-pop 显示出线性趋势,这可能表明明显的显著差异。这与大多数温暖国家的流行音乐比例略低(相对于其更高节奏的变体)相对应。事实上,流派和温度之间很少出现强线性联系。这将由线性垂直梯度表示。这表明,文化、语言和与艺术家的接近程度等其他因素可能决定流派偏好,而不仅仅是温度。

C.节奏与舞蹈性分析

这些图表上的每一个点都代表了一个国家以及可舞性、听觉、语速或节奏的水平。摘自 Spotify API: Danceability 根据音乐元素的组合(包括速度、节奏稳定性、节拍强度和整体规律性)描述了一首曲目是否适合跳舞。值 0.0 最不适合跳舞,1.0 最适合跳舞。声音度是音轨是否是声音的置信度,从 0.0 到 1.0。1.0 表示音轨是声学的高置信度。语音检测音轨中是否存在语音单词。越是类似语音的录音(例如脱口秀、有声读物、诗歌),属性值就越接近 1.0。高于 0.66 的值描述可能完全由口语单词组成的轨道。介于 0.33 和 0.66 之间的值描述可能包含音乐和语音的轨道,可以是分段的,也可以是分层的,包括说唱音乐。低于 0.33 的值很可能代表音乐和其他非语音类轨道。轨道的总体估计速度以每分钟节拍数(BPM)为单位。在音乐术语中,速度是给定作品的速度或节奏,直接来源于平均节拍持续时间。我们可以从温度数据与国家平均音频特征的比较分析中得出结论,平均国家温度和音频特征之间实际上没有相关性,因为它们都落在相同的值附近。一个很酷的实验是看看在不同的季节是否有轻微的偏差。

6.最后的想法

尽管我们在数据中看不到气候和收听习惯之间的任何关联,但这个项目仍然是寻找数据模式的一个很好的练习,因为它涉及到我们都喜欢的东西——音乐。

我们现在的情况表明,无论气候如何,流行音乐在全世界都很受欢迎,这并不是什么新鲜事。我们认为 Spotify 的数据是有缺陷的,因为它只给出了基于艺术家的流派标签,如果流派是基于歌曲给出的,我们的数据可能会有更多的差异,因为它会提供更深层次的分类。还有一种可能性是,Spotify 将更多的优先权和营销放在流行音乐之后,这可能会扭曲数据,使流行歌曲保持流行。

我们也可以把这个项目更进一步,根据季节比较听音乐的习惯,这样我们可以看到更正常的流派分布。总的来说,这个项目是一个新颖的想法,如果有一个更加平衡的数据集,可以提供一些有见地的结果。

用文本挖掘技术分析 2020 年总统辩论

原文:https://towardsdatascience.com/analyzing-the-chaotic-presidential-debate-2020-with-text-mining-techniques-238ed09d74c1?source=collection_archive---------30-----------------------

审视与数据科学的争论

查尔斯·德鲁维奥在 Unsplash 上拍摄的照片

多亏了互联网,现在全世界都知道了失去控制的 2020 年总统辩论。所有主要的新闻站都在报道参与者是如何相互打断和攻击的。

我决定整理一篇文章,重点分析事件中使用的词语,看看是否有什么隐藏的见解。

这篇文章的重点是找出每个发言人使用最多的词,并对发言进行情感分析。

作者图片:2020 年第一次总统辩论中使用最多的词——“人民”

第一次 2020 年总统辩论概述

:
-现任总统唐纳德·川普
-前副总统乔·拜登(民主党提名人)之间

主持人:
——克里斯·华莱士

涵盖的主题:

  1. 候选人的政治记录
  2. 最高法院
  3. 冠状病毒
  4. 经济
  5. 城市中的种族和暴力
  6. 选举的公正性

清理数据集

在这次活动中,总共使用了近 20,000 个单词。在去掉名字和常用的停用词后,剩下大约 6000 个词用于分析。

#tokenize
text_df <-  text %>%
  unnest_tokens(word, Text)#Remove stop words
my_stop_words <- tibble(
  word = c("chris","wallace","trump","donald","joe","biden","vice","president"))#Prepare stop words tibble
all_stop_words <- stop_words %>%
  bind_rows(my_stop_words)textClean_df <- text_df %>%
  anti_join(all_stop_words, by = "word")

作者图片:分析中使用的 2020 年总统辩论的总字数

简而言之,第一次总统辩论

单词相关网络图说明了在辩论中单词是如何在同一个句子中使用的,或者是如何在相邻的句子中使用的。我将一些可能与辩论主题相关的单词进行了归类:

  • 美国经济话题包括“负担得起”、“工作”、“行动”等词汇。
  • 最高法院话题包括‘正义’、‘理性’、‘法官’等词汇。
  • 种族和暴力城市主题包括“和平”和“抗议”等词
  • 选举话题包括“选票”、“管理”、“邮件”等词汇。

作者图片:单词的共现

用相应的词性对辩论进行分类

我们还可以用词类(名词、专有名词、形容词、副词等)来标记每个参与者的单词。).本节将特别关注特朗普和拜登使用最多的专有名词、名词和形容词。

  • “中国”是特朗普使用最多的专有名词
  • 特朗普和拜登在辩论中各使用了 60 多次“人民”
  • 有趣的是,特朗普使用最多的形容词是乔·拜登的“错误”和“正确”
library(udpipe)
udmodel <- udpipe_download_model(language = "english")
udmodel <- udpipe_load_model(file = udmodel$file_model#annotate data frame
tidy_text <- udpipe_annotate(udmodel, x = text$Text,doc_id = text$ï..Spokeperson)
tidy_text <- as.data.frame(tidy_text)library(igraph)
library(ggraph)
library(ggplot2)

#how many times nouns and adjectives are used in the same sentence
cooc <- cooccurrence(x = subset(textClean_df, upos %in% c("NOUN", "ADJ")), 
                     term = "lemma", 
                     group = c("doc_id", "paragraph_id", "sentence_id"))wordnetwork <- head(cooc, 50)
head(wordnetwork)
wordnetwork <- graph_from_data_frame(wordnetwork)
ggraph(wordnetwork, layout = "fr") +
  #geom_edge_link(aes(width = cooc, edge_alpha = cooc), edge_colour = "#FF62BC") +
  geom_edge_link(aes(edge_alpha = cooc), edge_colour = color2) +
  geom_node_point(color = color1, size = 1) +
  geom_node_text(aes(label = name), col = color1, size = 5, repel = TRUE) +
  theme_graph(base_family = "Arial") +
  theme(legend.position = "none") +
  labs(title = "First Presidential Debate 2020: Cooccurrences within sentence", subtitle = "Nouns & Adjective")ggsave("C:/Users/fengyueh/Documents/data analysis/R/17 presidential debate/cooccurence N&A 50.png",
       width = 6, height = 6, dpi = 1500)

按作者分类的图像:词性条形图

library(lattice)tidy_textToUse <- textClean_df %>%
#filter(doc_id == "Donald Trump")
#filter(doc_id == "Chris Wallace")
#filter(doc_id == "Joe Biden")#stats <- subset(tidy_textToUse, upos %in% c("PROPN"))
#stats <- subset(tidy_textToUse, upos %in% c("NOUN"))
#stats <- subset(tidy_textToUse, upos %in% c("VERB"))
#stats <- subset(tidy_textToUse, upos %in% c("ADJ")) 
#stats <- subset(tidy_text, upos %in% c("ADV")) 
stats <- txt_freq(stats$token)
stats$key <- factor(stats$key, levels = rev(stats$key))
barchart(key ~ freq, data = head(stats, 20), col = color1, labels = stats$freq,
         main = "First Presidential Debate 2020: 
         Most occurring nouns - Joe Biden", xlab = "Freq")

辩论的极端性

情感分析的基本任务之一是理解给定文本的极性,无论文本中表达的观点是积极的、消极的还是中性的。

下面的第一个图显示了辩论活动中每个发言人的发言文本的极性。蓝色勾号表示使用了负面意见,红色勾号表示正面意见。有趣的是,特朗普的第 1500-2500 句之间有一个空白。

下面的第二个图显示了总统辩论发言人使用的负面、中性和正面词汇的数量。

作者图片:发言人分组的 2020 年第一次总统辩论的极性

总体而言,拜登和特朗普似乎表达了几乎等量的正面和负面意见。

总统辩论中每个参与者的积极性

下面的堆积条形图显示了每位发言人的总体极性,将他们的发言分为积极或消极两类。

根据图表,与特朗普相比,拜登在辩论中似乎使用了更多积极的词语。接下来,我们将看看发言人的原话,带有极性/积极色彩。

作者图片:发言人的极性

word_sentiment <- word_counts %>%
  inner_join(get_sentiments("nrc"))word_sentiment %>%
  count(ï..Spokeperson, sentiment, sort = TRUE)# Count by person & sentiment
words_person_count <- word_counts %>%
  inner_join(get_sentiments("nrc")) %>% 
  filter(grepl("positive|negative", sentiment)) %>% 
  count(ï..Spokeperson, sentiment)data_pos <- words_person_count %>%
  group_by(ï..Spokeperson) %>% 
  mutate(percent_positive = 100 * n / sum(n) )ggplot(data_pos, aes(x = reorder(ï..Spokeperson, n), y = n, fill = sentiment)) +
  # Add a col layer
  geom_col(position = "fill") +
  coord_flip() +
  theme(axis.text.x = element_text(angle = 90, vjust = 0.1)) +
  ggtitle("Polarity of participants, sorted by sentiment word frequency ") +
  xlab("Person") +
  ylab("Polarity") +
  My_Theme

每个发言人的正面词汇云

在单词云中,消极情绪的单词用蓝色表示,积极情绪的单词用红色表示。单词的大小取决于它们各自的频率。

特朗普用得最多的正面词是“赢了”,拜登用得起。两位参与者都经常使用“错误”这个词。“支持”这个词也出现在所有的单词云中。

作者图片:积极词汇云——唐纳德·特朗普和乔·拜登

作者图片:积极词汇云——克里斯·华莱士

接下来,我们来看看辩论中的一些情绪分析。

2020 年第一场总统辩论的情感分析

下面的条形图显示了与每种情绪相关的辩论中使用的字数。在辩论中,与“信任”这种积极情绪相关的词汇出现得最多,而与“厌恶”这种消极情绪相关的词汇出现得最少。

textClean_df %>%
#  filter(ï..Spokeperson == "Chris Wallace")  %>%
#  filter(ï..Spokeperson == "Donald Trump")  %>%
#  filter(ï..Spokeperson == "Joe Biden")  %>%
  inner_join(get_sentiments("bing")) %>%
  count(word, sentiment, sort = TRUE) %>%
  acast(word ~ sentiment, value.var = "n", fill = 0) %>%
  comparison.cloud(colors = c(color1, color2),title.size=1.0,
                   max.words = 50)

作者图片:2020 年第一次总统辩论的情感分析

text_nrc_sub %>%
  count(ï..Spokeperson, sentiment, ï..Spokeperson) %>%
  mutate(sentiment = reorder(sentiment, n), Spokeperson = reorder(ï..Spokeperson, n)) %>%
  ggplot(aes(sentiment, n, fill = sentiment)) +
  geom_col() +
  facet_wrap( ~ ï..Spokeperson, scales = "free_x", labeller = label_both) +
  theme(panel.grid.major.x = element_blank(),
        axis.text.x = element_blank()) +
  labs(x = NULL, y = NULL) +
  ggtitle("NRC Sentiment Analysis - First Presidential Debate 2020") +
  coord_flip()

接下来,我们将看看与每种情绪相关的确切词汇,由每位发言人进行分类:

华莱士最常用的词是“先生”,在辩论中出现了 40 多次。

作者图片:情感词频——克里斯·华莱士在 2020 年第一场总统辩论中

根据下面的图表,包括“投票”、“交易”和“税收”在内的词汇被拜登提到的频率很高。

作者图片:情感词频——2020 年第一次总统辩论中的乔·拜登

特朗普高度提到了一些词,包括“军事”、“法律”和“工作”。

作者图片:情感词频——唐纳德·特朗普 2020 年首次总统辩论

nrc_words %>%
  # Count by word and sentiment
#  filter(ï..Spokeperson == "Donald Trump") %>%
#  filter(ï..Spokeperson == "Joe Biden") %>%
#  filter(ï..Spokeperson == "Chris Wallace") %>%
  count(word, sentiment) %>%
  # Group by sentiment
  group_by(sentiment) %>%
  # Take the top 10 words for each sentiment
  top_n(10) %>%
  ungroup() %>%
  mutate(word = reorder(word, n)) %>%
  ggplot(aes(word, n, fill = sentiment)) +
  geom_col(show.legend = FALSE) +
  facet_wrap(~ sentiment, scales = "free") +
  coord_flip() +
  ggtitle("First Presidential Debate: Sentiment word frequency - Joe Biden") +
  My_Theme

结论

这场辩论在网上引起了不小的轰动,我觉得分析事件的发言会很有趣。在分析中,一个特别的词引起了我的注意——“人”这个词被两个参与者高度提及,似乎是他们唯一认同的话题。People 还延伸到其他词,如 job、affordable、court 等。,与讨论的主题相关。

法国和韩国冠状病毒疫情分析

原文:https://towardsdatascience.com/analyzing-the-coronavirus-outbreak-in-france-and-south-korea-8f467ef385de?source=collection_archive---------56-----------------------

我们目前正在经历新型冠状病毒的负面影响。这种病毒迅速改变了我们的生活,并让我们许多人对即将发生的事情感到困惑和恐惧。

作为工程师和数据科学家,我们希望帮助理解海量的可用数据。我们认为,我们有责任分享我们的见解,以便集体找到解决办法,防止疾病进一步爆发。

本文的重点是分析这种疾病最初在法国和韩国的传播。我们的分析涵盖了截至 2020 年 3 月 12 日收集的所有数据。多亏了我们的数据科学家蜜琪拉·皮萨尼的工作,它才得以整合。

编者按: 走向数据科学 是一份以数据科学和机器学习研究为主的中型刊物。我们不是健康专家或流行病学家,本文的观点不应被解释为专业建议。想了解更多关于疫情冠状病毒的信息,可以点击 这里

我们的数据来源

Kaggle 为数据科学家提供不同的数据集。它们可以用来练习和解决机器学习问题。

以下链接包含来自世界各国的最新数据集,提供了感染冠状病毒的患者的统计数据:

更多信息资源可以在这里找到。

问题

此时此刻,科学界的问题远多于答案。提出问题很容易,但大多数答案都需要等待。

我们可以很容易地编造其中的一些,比如:

  • 数据中是否有某种模式,第一次感染是如何发生的?有容易识别的集群吗?
  • 阳性病例和死亡病例的比例是多少?
  • 我们能预测未来的阳性病例吗?
  • 病毒轨迹是怎样的?随着时间的推移,病毒是如何传播的?

如前所述,在本文中,我们将探讨该病毒最初是如何在法国和韩国传播的,并通过聚类分析对两者进行比较。

聚类分析的任务是对一组对象进行分组,使同一组(称为聚类)中的对象比其他组(聚类)中的对象更相似(在某种意义上)。

步骤 1 —我们的数据集

有两个数据集分别包含法国和韩国的类似信息。所以,我们的想法是合并这些数据集,看看我们是否能找到有意义的聚类。
我们可以在这里找到数据的链接:

让我们首先对我们将在这里使用的库进行一些初始设置。

import pandas as pd
import numpy as np
import random
from sklearn import preprocessing
from sklearn.cluster import KMeans
import matplotlib.pyplot as plt
from kneed import KneeLocator
from matplotlib import cm
from mpl_toolkits.mplot3d import Axes3D

我推荐你看看这篇关于熊猫图书馆和数据清理的文章。

步骤 2 —检查数据质量

在深入挖掘之前,我们必须探索我们的数据集,看看它看起来像什么,以及我们希望如何开始我们的分析。

首先,我们将检查空值。我们将加载两个数据集,并查看找到多少空(NA)值。

NA 值对应缺失或空信息。

根据我们的发现,我们可能需要为每个特定的列做出一些决定,以便为分析准备数据。

def load_data(data_path):
    df = pd.read_csv(data_path + '/patient.csv')
    df['released_date'] = pd.to_datetime(df['released_date'])
    df['confirmed_date'] = pd.to_datetime(df['confirmed_date'])
    df['month'] = df['confirmed_date'].dt.month
    df['day'] = df['confirmed_date'].dt.day
    return dfdf_france = load_data('coronavirusdataset_france')
df_france.isnull().sum()

我们会得到:

id                  2067
sex                 1851
birth_year          1936
country                1
region                 1
department          195
city                1804
group               1905
infection_reason    1906
infection_order     2068
infected_by         2056
contact_number      2073
confirmed_date         4
released_date       2064
deceased_date       2048
status              1481
health              1849
source               199
comments            1637
month                  4
day                    4
dtype: int64

接下来,我们将检查韩国。

df_south_korea = load_data('coronavirusdataset_south_korea')
df_south_korea.isnull().sum()

我们发现以下数据

patient_id             0
sex                 7190
birth_year          7203
country                0
region              7432
disease             7841
group               7783
infection_reason    7715
infection_order     7833
infected_by         7799
contact_number      7816
confirmed_date         0
released_date       7813
deceased_date       7833
state                  0
month                  0
day                    0
dtype: int64

步骤 3-解决缺失值

为了只保留必要的数据,应该删除一些列,比如 department、comments 和 health,因为它们对于这个特定的分析并不重要。

我们将填充出生年份的缺失值。这个过程被称为数据插补。

通过使用出生日期,我们可以创建年龄变量,将其减去实际日期。缺失的信息将用从分布中抽取的随机数来填充。考虑到每个国家的人口统计数据,可以填写与人口年龄分布相关的信息:

创建“simulate_age”函数是为了根据可用数据模拟人口年龄。在这种情况下,有了每个年龄的范围和占总人口的百分比,我们可以使用均匀分布来模拟每个范围的年龄分布。

df_france.drop(['departement','region','comments', 'id', 'infected_by','health','city','source'],axis=1,inplace=True)df_south_korea.drop(['region','disease','patient_id','infected_by'], axis=1, inplace=True)def simulate_age(ranges, percents, total_pop):
    simulated_pop = np.array(0)
    for (low, high), percent in zip(ranges, percents):
        simulated_pop = np.append(simulated_pop, 
                  np.random.randint(low=low, high=high, size=int(total_pop*percent/100)))
return simulated_pop*#France*
france_population = 67364357
'''
0-14 years: 18.48% 
15-24 years: 11.8% 
25-54 years: 37.48% 
55-64 years: 12.42%
65 years and over: 19.82%
'''
ranges = [(0,14),(15,24),(25,54),(55,64),(65,90)]
percents = [18.48,11.8,37.48,12.42,19.82]
france_simulated_pop = simulate_age(ranges, percents, france_population) f, (ax1, ax2) = plt.subplots(1, 2, figsize=(15,5))
ax1.hist(france_simulated_pop,bins=20, color='mediumaquamarine', edgecolor='k', alpha=0.5)ax1.set_title('France - Simulated age distribution')#South Korea
south_korea_population = 51418097
'''
0-14 years: 13.03% 
15-24 years: 12.19%
25-54 years: 45.13%
55-64 years: 15.09% 
65 years and over: 14.55% 
'''
percents = [13.03,12.19,45.13,15.09,14.55]
south_korea_simulated_pop = simulate_age(ranges, percents, south_korea_population)
ax2.hist(south_korea_simulated_pop,bins=20, color='mediumaquamarine', edgecolor='k', alpha=0.5)
ax2.set_title('South Korea - Simulated age distribution')plt.show()

现在,我们可以在数据框中创建一个年龄列,并用从我们刚刚模拟的分布中选择的随机值填充缺失值。

import math
actual_year = pd.to_datetime('today').yeardef calculate_age(x):
    if math.isnan(x):
        return x
    else:return int(actual_year - x)#France
df_france['age'] = df_france['birth_year'].apply(calculate_age)
df_france.fillna({'age':int(random.choice(france_simulated_pop))}, inplace=True)
df_france.drop(['birth_year'], axis=1, inplace=True)#South Korea
df_south_korea['age'] = df_south_korea['birth_year'].apply(calculate_age)
df_south_korea.fillna({'age':int(random.choice(south_korea_simulated_pop))}, inplace=True)
df_south_korea.drop(['birth_year'], axis=1, inplace=True)

对于缺失的性别值,我们可以根据每个人群的性别比例,用一个概率值来画一个随机数。

'''
Considering m as men and w as women. 
m/w=ratio -> m=ration*w
m+w=total_pop
'''
def calculate_values(ratio, total_pop):
    w = (france_population/(1+ratio))/total_pop
    m = 1 - w
    return (w,m)# 0 (woman) and 1 (man) with the calculated probabilities
# France
# total population: 0.96 male(s)/female (2018 est.)w,m = calculate_values(0.96, france_population)
df_france['sex'] = df_france['sex'].str.lower()
df_france["sex"].replace({"male\xa0?": "male"}, inplace=True)
df_france.fillna({'sex': np.random.choice(['female','male'],p=[w,m])}, inplace=True)# South Korea
# total population: 1 male(s)/female (2018 est.)w,m = calculate_values(1, south_korea_population)
df_south_korea['sex'] = df_south_korea['sex'].str.lower()
df_south_korea["sex"].replace({"male\xa0?": "male"}, inplace=True)
df_south_korea.fillna({'sex': np.random.choice(['female','male'],p=[w,m])}, inplace=True)# France
# total population: 0.96 male(s)/female (2018 est.)w,m = calculate_values(0.96, france_population)
df_france['sex'] = df_france['sex'].str.lower()
df_france["sex"].replace({"male\xa0?": "male"}, inplace=True)
df_france.fillna({'sex': np.random.choice(['female','male'],p=[w,m])}, inplace=True)# South Korea
# total population: 1 male(s)/female (2018 est.)w,m = calculate_values(1, south_korea_population)
df_south_korea['sex'] = df_south_korea['sex'].str.lower()
df_south_korea['sex'].replace({"male\xa0?": "male"}, inplace=True)
df_south_korea.fillna({'sex': np.random.choice(['female','male'],p=[w,m])}, inplace=True)

由于法国数据集的 status 列和韩国数据集的 state 列具有相同的含义,我们可以重命名其中一个数据集的列,并将值更新为相同的类别。

df_france.rename({'status':'state'}, axis=1, inplace=True)
df_france['state'] = df_france['state'].apply(lambda x: 'isolated' if (x=='hospital' or x=='home isolation') else x)

此外:

  • 国家变量的空值将分别用法国或韩国填充。
  • 将为感染原因、组、状态变量创建一个新类别“未知”
  • 为 infection_order 添加了一个新类别,代码为 0
  • 联系号码的空值将用 0 填充
df_france.fillna({'country':'France','infection_reason':'Unknown','group':'Unknown', 'state':'Unknown','infection_order':0, 'contact_number':0} , inplace=True)df_south_korea.fillna({'infection_reason':'Unknown','group':'Unknown', 'infection_order':0, 'contact_number':0, 'state':'Unknown'} , inplace=True)

现在,让我们检查一下是否还有需要解决的缺失值。

df_france.isnull().sum()sex                    0
country                0
group                  0
infection_reason       0
infection_order        0
contact_number         0
confirmed_date         4
released_date       2064
deceased_date       2048
state                  0
month                  4
day                    4
age                    0
dtype: int64 df_south_korea.isnull().sum()sex                    0
country                0
group                  0
infection_reason       0
infection_order        0
contact_number         0
confirmed_date         0
released_date       7813
deceased_date       7833
state                  0
month                  0
day                    0
age                    0
dtype: int64

干得好!我们剩下的不多了。现在我们需要解析 released_date 和 dead _ date 空值。

  • 如果 released_date 为空,则意味着此人仍携带病毒。
  • 如果 dead _ date 为空,则表示此人没有死亡。

我们可以计算感染持续时间(以天为单位)并去除其他 3 个变量。此外,我们希望将 death _ date 转换为一个二进制列,指示该人是否已经死亡。

df_france['released_date'] = df_france[['released_date','deceased_date']].fillna(df_france['deceased_date'])
df_france['released_date'] = df_france[['released_date']].fillna(pd.to_datetime('today'))
df_france['infection_duration'] = pd.to_datetime(df_france['released_date']).sub(df_france['confirmed_date'], axis=0)df_france = df_france[df_france['infection_duration'].dt.days>=0]
df_france['infection_duration'] = df_france['infection_duration'].dt.days
df_france.drop(['released_date','confirmed_date','deceased_date'], axis=1, inplace=True)
df_south_korea['released_date'] = df_south_korea[['released_date','deceased_date']].fillna(df_south_korea['deceased_date'])
df_south_korea['released_date'] = df_south_korea[['released_date']].fillna(pd.to_datetime('today'))df_south_korea['infection_duration'] = pd.to_datetime(df_south_korea['released_date']).sub(df_south_korea['confirmed_date'], axis=0)
df_south_korea = df_south_korea[df_south_korea['infection_duration'].dt.days>=0]df_south_korea['infection_duration'] = df_south_korea['infection_duration'].dt.daysdf_france.columns
Index(['sex', 'country', 'group', 'infection_reason',
'infection_order','contact_number', 'state', 'month', 
'day', 'age', 'infection_duration'], dtype='object')df_south_korea.columns
Index(['sex', 'country', 'group', 'infection_reason',
'infection_order', 'contact_number', 'state', 'month', 
'day', 'age', 'infection_duration'], dtype='object')

步骤 4 —数据融合

最后,我们准备将两个数据集放在一起,开始我们的分析。

最后,我们准备将两个数据集放在一起,开始我们的分析。

df = df_france.append(df_south_korea, sort=False)
df.isnull().sum()sex                   0
country               0
group                 0
infection_reason      0
infection_order       0
contact_number        0
state                 0
month                 0
day                   0
age                   0
infection_duration    0
dtype: int64

虚拟编码

模型的输入必须是数值。因此,我们必须将分类变量转换成数字。由于类别没有顺序,我们将把每个类别值转换成一个二进制列(0 或 1 值)。这种技术被称为虚拟编码。

df = pd.concat([df, pd.get_dummies(df['sex'])], axis=1)
df = pd.concat([df, pd.get_dummies(df['country'])], axis=1)
df = pd.concat([df, pd.get_dummies(df['state'], drop_first=True)], axis=1)
df = pd.concat([df, pd.get_dummies(df['infection_reason'], drop_first=True)], axis=1)
df = pd.concat([df, pd.get_dummies(df['group'], drop_first=True)], axis=1)

降维

当我们应用哑编码时,我们最终会有更多的变量,因为每个类别都被转换成一个列。

因为我们有太多的变量,所以很难在聚类中找到模式。首先,我们可以通过分组相似的类别来减少分类变量的数量。第二,我们可以应用降维技术来减少输入变量的数量,使模型更容易解释。

df = df_france.append(df_south_korea, sort=False)

Transform infection_reason:我们将列出这个变量所有可能的值,并对它们进行分组。之后,我们会将类似的原因分组,并将其转换为虚拟变量。最后,我们将删除原来的列。

df.infection_reason.unique()array(['visit to Italy', 'contact with patient', 'visit to Mulhouse religious gathering', 'Unknown', 'contact with person who visited Italy', 'visit to Egypt', 'unknown', 'Visit to Venice, Italy', 'contact with patient in Auray', 'visit to Mulhouse', 'visit to Milan', 'Italian', 'visit to Lombardy', 'parishioner', 'Creil military base\xa0?', 'visit to Senegal', 'visit to Alsace', 'visit in Lombardy', 'visit to Bretagne', 'Visit in Italy', 'In contact with someone contamitaminated in Oise', 'Religious Meeting in Mulhouse', 'work in a medical environment ', 'Visit family in Oise', 'health professional', 'visit to Wuhan', 'contact with patient in Japan', 'residence in Wuhan', 'visit to Thailand', 'contact with patient in Singapore', 'visit to China', 'visit to Daegu', 'pilgrimage to Israel', 'contact with patient in Daegu', 'visit to Vietnam', 'visit to Japan', 'visit to ooo'], dtype=object)def transform_reason(value):
    if ('religious' in value or 'parishioner' in value):
        return 'religious'
    elif ('visit' in value or 'residence' in value):
        return 'visit'
    elif ('contact' in value):
        return 'contact'
    elif ('medical' in value or 'health professional' in value):
        return 'medical'
    elif ('militar' in value):
        return 'militar'
    elif ('italian' in value):
        return 'italian'
    elif ('pilgrimage' in value):
        return 'pilgrimage'
    else:
        return 'unknown'df['infection_reason'] = df['infection_reason'].str.lower()
df['infection_reason'] = df['infection_reason'].apply(transform_reason)  
df = pd.concat([df, pd.get_dummies(df['infection_reason'], prefix='infection_reason', prefix_sep='_')], axis=1)
df.drop(['infection_reason_unknown'], axis=1, inplace=True)

此外,“group”变量提供了与 infection_reson 类似的信息。我们可以很容易地移除它。

df.drop(['group'], axis=1, inplace=True)

现在,我们可以将其他分类变量转换成虚拟变量:国家、州和性别。

df = pd.concat([df, pd.get_dummies(df['country'])], axis=1)
df = pd.concat([df, pd.get_dummies(df['state'], prefix='state', prefix_sep='_')], axis=1)
df = pd.concat([df, pd.get_dummies(df['sex'])], axis=1)

主成分分析

现在,我们将为一项非常强大的技术准备数据:主成分分析(PCA)。

这种技术找到解释数据的原始变量的线性组合。主要目标是通过寻找新的变量“组件”来减少变量的数量。它基于正交向量,这使得这些分量不相关。

我们需要定义哪些变量是输入,并删除我们从中创建虚拟变量的变量(它们是多余的)。

此外,有必要将我们的数据标准化,为此我们使用 StandardScaler。标准化数据意味着将所有变量放在同一尺度上,以避免累积的数值误差。只有这样,我们才能比较数据点之间的距离。

features = df.drop(['country','state','sex','infection_reason'], axis=1)
from sklearn.preprocessing import StandardScaler
from sklearn.decomposition import PCA
x = StandardScaler().fit_transform(features.values)
pca = PCA(random_state=20)
pca.fit(x) PCA(copy=True, iterated_power='auto', n_components=None, random_state=20, svd_solver='auto', tol=0.0, whiten=False)

为了确定组件的数量,我们需要查看每个组件的解释方差。
以解释最大方差的方式计算组件。例如,我们将添加组件,直到达到解释的方差的定义阈值。典型地,阈值在 0.7 和 0.9 之间。这意味着它解释了 70%到 90%的差异。

在这种情况下,我们将选择 0.8 作为阈值。

# determine number of components with threshold=0.8n_components=np.where(np.cumsum(pca.explained_variance_ratio_)>0.8)[0][0]+1# explained variance
v = round(np.cumsum(pca.explained_variance_ratio_)[n_components-1]*100,1)
print(f'It is needed {n_components} components to explain {v}% variance of the data')

我们需要 12 个分量来解释数据的 83.1%的方差

现在我们有了一些组件,我们可以计算这些新变量的值。

pca = PCA(n_components=n_components, random_state=20)
pcs = pca.fit(x)
components_name = list(range(0, n_components))
components_name = list(map(lambda x: 'PC' + str(x), components_name))
pd.DataFrame(data=pcs.components_, columns = features.columns, index=components_name)

我们可以用一个矩阵来显示每个变量对每个组成部分的重要性。

components_range = np.arange(1, n_components+1, 1)
components_names = list(map(lambda x: 'PC' + str(x), components_range))
plt.matshow(pcs.components_,cmap='viridis')
plt.yticks(range(0,n_components), components_names,fontsize=10)
plt.colorbar()
plt.xticks(range(0,len(features.columns)),features.columns,rotation=90,ha='left')plt.show()

变量的值越高,意味着主成分的影响越大。较低的值意味着对主成分的负面影响。
因此,根据热图,主成分分析的一种可能解释是:

  • PC1:男性不是孤立的,也不是朝鲜人
  • PC2:第一个月
  • PC3:国家释放
  • PC4:状态死者
  • PC5:感染原因宗教
  • PC6:感染原因访问
  • PC7:感染原因意大利语
  • PC8:感染原因军事
  • PC9:感染原因医疗
  • PC10:感染原因朝圣
  • PC11:高感染顺序
  • PC12:来自蒙古国

步骤 5 — K 均值聚类

K-means 试图将数据分成 k 个组,其中一个组的元素彼此接近。该方法基于数据点之间的距离。
因此,目标是最小化点到质心的距离。质心是每个簇/组的“中间”点。

该算法从随机选择的质心开始,在每次迭代中,它重新计算质心的位置。

为了确定 k,即组的数量,我们使用了一个图来显示数据相对于分类数量的失真。这种方法被称为肘测试。失真被定义为到聚类中心的平均距离。扭曲开始以线性方式减少的点是肘部,这表示最佳的群集数量。这意味着添加另一个集群不会改变太多的失真。

让我们根据主成分得分创建一个数据框架,并将其用于聚类分析。

pca_df = pd.DataFrame(data = pca.fit_transform(x), columns = components_names)
pca_df.head()

使用弯头测试来确定最佳聚类数。

def elbow_test(df, n_init, max_clusters, max_iter):
    distortions = []
    for i in range(1, max_clusters):
        km = KMeans(
            n_clusters=i, init='random',
            n_init=n_init, max_iter=max_iter,
            tol=1e-04, random_state=20
        )
        km.fit(df)
        distortions.append(km.inertia_)
plt.plot(range(1, max_clusters), distortions, marker='o')
    plt.xlabel('Number of clusters')
    plt.ylabel('Distortion')
    plt.show()kn = KneeLocator(
        range(1, max_clusters),
        distortions,
        curve='convex',
        direction='decreasing',
        interp_method='interp1d',
    )
    return kn.kneen_clusters = elbow_test(pca_df, 10, 20, 300)
print(f'the optimal number of clusters is {n_clusters}')

根据我们的分析,最佳的聚类数是 4

我们将知道用四个集群运行 K-means 算法,看看我们会发现什么!

km = KMeans(n_clusters=n_clusters, random_state=20)
y = km.fit_predict(pca_df)
idx = np.argsort(km.cluster_centers_.sum(axis=1))
lut = np.zeros_like(idx)
lut[idx] = np.arange(n_clusters)
pca_df['cluster'] = lut[km.labels_]
df['cluster'] = lut[km.labels_]

我们可以用下面的代码保存/加载我们的模型:

import pickle   
pickle.dump(km, open('kmeans_model.sav', 'wb'))# Load
km = pickle.load(open('kmeans_model.sav', 'rb'))pca_df[pca_df['cluster']==3]

我们可以看到 PC7 值很高。它对应于感染原因“意大利语”。我们可以通过查看实际数据来证实这一点:

df[df['cluster']==3]

以下函数将绘制我们的数据。

第一个是散点图,用于比较两个主成分,并查看它们之间的聚类分布情况。第二个创建了一个 3d 图来比较由聚类着色的三个主成分。

这些图表将帮助我们确定聚类的含义。

def draw_scatter(df, col_1, col_2, cluster_column, num_clusters, title):
    fig = plt.figure(figsize=(10,10))
    ax = fig.add_subplot(111)
    ax.set_title(title)
    ax.set_xlabel(col_1)
    ax.set_ylabel(col_2)
    labels = list(range(0,num_clusters))
    colors = plt.cm.Spectral(np.linspace(0, 1, num_clusters))
    axs = []
    for i in labels:
        axs.append(ax.scatter(df[df[cluster_column]==i][col_1], df[df[cluster_column]==i][col_2], cmap=colors[I]))ax.legend(axs, labels, loc='center', bbox_to_anchor=(0.92, 0.84), ncol=1)
    plt.show()def create_3d_scatter(df, col_1, col_2, col_3, cluster_column, num_clusters, title):
    fig = plt.figure()
    ax = fig.add_subplot(111, projection='3d')
    ax.set_title(title)
    ax.set_xlabel(col_1)
    ax.set_ylabel(col_2)
    ax.set_zlabel(col_3, rotation=90)
    labels = list(range(0,num_clusters))
    colors = plt.cm.Spectral(np.linspace(0, 1, num_clusters))
    axs = []
    for i in labels:
        d = df[df[cluster_column]==i]
        axs.append(ax.scatter(d[col_1], d[col_2], d[col_3], cmap=colors[i]))
    ax.legend(axs, labels, bbox_to_anchor=(0.2, 0.5), ncol=1)
    ax.set_xticklabels([])
    ax.set_yticklabels([])
    ax.set_zticklabels([])
    plt.show()create_3d_scatter(pca_df, 'PC1', 'PC2', 'PC3', 'cluster', n_clusters, '')

我们可以看到,一些集群是根据前两个主成分分布的。此外,PC3 似乎对团簇的分离没有太大的影响。

在此表中,我们可以看到每个组件的值如何影响分类的意义。

让我们画出 PC1 和 PC2 上的群集,以便更清楚地验证这一假设。

draw_scatter(pca_df, 'PC1', 'PC2', 'cluster', n_clusters, 'Clusters - PC1/PC2')

draw_scatter(pca_df, 'PC1', 'PC3', 'cluster', n_clusters, 'Clusters - PC1/PC3')

请记住,主要组件的含义定义如下:

  • PC1:不是孤立的,也不是韩国人
  • PC2:第一个月
  • PC3:国家释放

因此,从图表中我们可以得出结论:

结论

聚类分析包括将对象放入独立的组(簇),相似的对象放在同一个组中。物体之间的相似性是基于它们之间的距离,彼此靠近的物体有一些共同点。因此,接近的对象属于同一个集群。

在这种情况下,通过 K-means 发现的聚类显示,这些病例按照患者中的某些特征进行分组。这些特征集中在性别、感染原因、国家和月份等变量上。根据这些变量的值,数据点分布在聚类中。

聚类是具有相似特征的患者组。

我们发现的集群是:

聚类 0 将 3 月份感染病毒的韩国女性患者分组。聚类 1 包含来自韩国但在其他月份感染病毒的另一组妇女。聚类 2 指的是来自法国的男性,聚类 3 将因为与来自意大利的人接触而感染病毒的患者分组。

做这样的分析让我们对疫情有了更清晰的认识。尽管冠状病毒很快就脱离了我们的控制,但我们相信,随着更多数据的可用,我们可以做好准备,以便能够在未来做出更好的应对。

计算机科学/工程领域高等院校性别差异分析

原文:https://towardsdatascience.com/analyzing-the-gender-disparity-among-higher-academia-in-computer-science-engineering-2d8cecefa76e?source=collection_archive---------47-----------------------

你不会疯狂地认为高等教育中男性明显更多——以下是数据

在学术和专业环境中,妇女在计算机科学/工程领域的代表性不足。这话我听过很多次了,但我一直在想,我们怎么才能做出这种说法。具体来说,有哪些数据支持这种说法,这些数据告诉我们这些领域代表性不足的情况是什么?在本文中,我将通过回答以下 3 个问题来探索和分析高等院校计算机科学/工程专业的性别差异:

  • 1。在美国,女性和男性在计算机科学教师职位上的比例相等吗?
  • 2。随着时间的推移,在美国获得计算机&信息科学(CIS)教师职位的女性比例发生了怎样的变化?
  • 3。随着时间的推移,美国学术界女性工程师的代表性发生了怎样的变化?

这是伯克利大学的一个更大项目的一部分

在 Ursatech Berkeley,我们通过在加州大学伯克利分校社区内提供无偿咨询服务以及将我们的专业知识借给大学附属团体来回馈我们的校园社区。

我们客户的主要目标是发现和减少阻碍少数群体在计算机科学/工程领域追求成功的学术和职业生涯的障碍。为了帮助实现这一目标,UrsaTech 将开展一个数据分析项目,该项目涉及搜集网络信息,以合并和识别大学人口数据中的模式。这将有助于我们确定学生在本科生、研究生课程和/或教师职位中如何通过计算机科学/工程课程取得进步。我这个项目的重点是性别。

问题 1:在美国,女性和男性在计算机科学教师职位上的比例相等吗?

大学第一次遇到女计算机科学家。她是我暑期实验室研究的教授!在此之前,我从未真正遇到过女性工程师或计算机科学家。随着我继续接受教育,我开始注意到在我大学的计算机科学/工程系中女教授的人数很少。如果我学校的系是这个样子,美国其他 CS 系是什么样子?具体来说,在美国,女性计算机科学教员和男性计算机科学教员的代表性相比如何?

我从下面 7 个不同的 CS 大学系收集了样本。

数据源:

如果重复,我会争取更大的样本量。

Web 报废

我写了下面的函数来从麻省理工学院、斯坦福大学和加州大学收集数据。其他学校的数据被我的队友废弃了。

我将性别列初始化为全是男性,然后针对每个学校相应地将其更改为女性。可能会有一些偏差,因为我是根据大学提供的照片人工确定这个人是男是女的。

from requests import get
from bs4 import BeautifulSoup
import re
import pandas as pd
import urllib.request
import numpy as npdef lst_data(website: str, tag: str, attrs_key: str, attrs_txt: str):
 response = get(website)
 html = BeautifulSoup(response.text, ‘html.parser’)
 name_data = html.find_all(tag, attrs={attrs_key: re.compile(attrs_txt)})
 return name_data#names = [first name, last name]
def index_values(names, name_data):
 lst = []
 for name in names:
 name_str = [str(x) for x in name_data]
 new_list = [name_str.index(x) for x in name_str if re.search(name, x)]
 lst.append(new_list[0])
 return lst#initialize all as male and change to female accordingly
def make_df(name_lst, school, female_lst):
 df = pd.DataFrame({‘Name’: name_lst, ‘School’: school, ‘Gender’: ‘male’})
 df.index = df[‘Name’]
 df.loc[female_lst, ‘Gender’] = ‘female’
 df = df.reset_index(drop=True)
 return df

以下是我如何从斯坦福大学取消教师姓名的一个例子。

name_data = lst_data(‘[https://cs.stanford.edu/directory/faculty'](https://cs.stanford.edu/directory/faculty'), ‘a’, ‘href’, ‘^http’)# Returns index values [8,67]. Use to index name_data
index_values([‘Maneesh Agrawala’, ‘Matei Zaharia’], name_data)lst = []
for faculty in name_data[8:68]: 
 lst.append(faculty.text) #female faculty names
female_lst = [‘Jeannette Bohg’, ‘Emma Brunskill’, ‘Chelsea Finn’, ‘Monica Lam’, ‘Karen Liu’, ‘Dorsa Sadigh’, \
 ‘Caroline Trippel’, ‘Jennifer Widom’, ‘Mary Wootters’]stanford_df = make_df(lst, ‘Stanford’, female_lst)

在收集和整理了适当的数据后,我发现了每个学校计算机科学系中女性教员的比例。处理数据的其余代码链接在本文末尾的我的 github 中。

假设检验:1 样本 T 检验

  • 为什么选择 1 个样本进行 T 检验:样本量< 30 因为只有 7 所学校,并且我们有一个未知的总体标准差
  • 样本:各学校计算机科学系中女性教员的比例(图 1)

图 1——相关大学中女性计算机科学教师的比例

  • 零假设: p = 0.5,因为我们正在测试计算机系女性教员的百分比是否等于计算机系男性教员的百分比,所以计算机系女性教员= 50%
  • 显著性水平(alpha): 5%,当零假设实际上为真时,我们拒绝零假设的概率是 5%
from scipy.stats import ttest_1samptset, pval = ttest_1samp(x, 0.5) #x = sample 
print(‘t-statistic:’, tset)
print(‘pval:’, pval)if pval < 0.05: # alpha value is 0.05 or 5%
 print(“Reject”)
else:
 print(“Accept”)

使用 5%的显著性水平,我们从假设检验中得到 2.82e-07 的 p 值。假设零假设为真,观察到样本数据(图 1)的概率为 0.0000282%。由于 p 值小于显著性水平,我们拒绝零假设。测试表明,女性在计算机科学教师职位中的比例与男性在这些职位中的比例不相等。

这是为什么呢?cs 教师职位合格候选人的性别统计数据是什么样的?

2.在美国,获得计算机与信息科学(CIS)博士学位的女性在教师职位中的比例随着时间的推移发生了怎样的变化?

在美国,计算机科学系的女性人数比男性少得多。CS 教授职位的合格女性是否明显较少?随着时间的推移,在 CIS 获得博士学位并有资格获得 CS 教师职位的女性比例如何?

  • 数据来源:数据来自美国国家科学与工程统计中心(NCSES)。他们提供了关于人口统计特征、财政支持来源和博士获得者教育历史的数据表。他们的数据是通过调查收集的。
    链接:https://ncses.nsf.gov/pubs/nsf20301/data-tables/#group3一旦打开链接,点击“博士获得者,按性别和主要研究领域:2009-18”
  • 数据处理:数据集由特定研究领域的所有博士学位获得者(男性获得者和女性获得者)精心组织。我把 NCSES 数据表下载成 Excel 文件,然后转换成 csv 文件。表 2 显示了导入的 csv 文件/数据的前五行。
    我过滤了数据框,只包括男性和女性“计算机和信息科学”收件人。处理和分析数据的其余代码在下面我的 GitHub 中。

表 2:“2009-2018 年按性别和主要研究领域分列的博士获得者”,由国家社会经济研究中心提供

  • 调查结果:

作者根据国家社会经济普查数据提供的数字

作者根据国家社会经济普查数据提供的数字

值得注意的是,从 2009 年到 2018 年,计算机与信息科学(CIS)博士学位获得者的比例相对保持不变,男性占 80%,女性占 20%。数据趋势表明,自 2009 年以来,独联体博士学位获得者中的女性比例没有增加。

这让我进一步质疑,随着你在学术界的地位越来越高,女性在工程领域的总体代表性。

注意:没有专门针对计算机科学人口统计的数据集,所以我主要关注工程学。

3。随着时间的推移,美国学术界女性工程师的代表性发生了怎样的变化?

如果获得计算机和信息科学博士学位的女性比例较小,那么从本科到研究生的工程项目中女性的比例如何变化?

  • 数据来源:数据来自美国国家科学与工程统计中心(NCSES)。他们提供了关于人口统计特征、财政支持来源和博士获得者教育历史的数据表。他们的数据是通过调查收集的。
    链接:https://ncsesdata . NSF . gov/grad postdoc/2018/html/GSS 18-dt-tab 001-2c . html
  • 数据处理:来自 NCSES 的原始数据表如下表 3 所示。该表包含 1977 年至 2018 年研究生、博士后和持有博士学位的非学术研究人员的数量和百分比。然而,为了保持一致性,我删除了任何具有 na 值的行,并以 1979-2018 年的数据结束。处理和分析数据的其余代码在下面我的 github 中。

表 NCSES 的“工程领域研究生、博士后和持有博士学位的非学术研究人员的性别:1977-2018”

  • 基于数量/计数视角的调查结果:

作者根据国家社会经济普查数据提供的数字

作者根据国家社会经济普查数据提供的数字

作者根据国家社会经济普查数据提供的数字

  • 基于比例视角的调查结果:

作者根据国家社会经济普查数据提供的数字

作者根据国家社会经济普查数据提供的数字

自 1979 年以来,高等院校中男女工程师的比例一直在下降。然而,在高等学术机构中,女工程师和男工程师的人数仍有很大差距。 学术界职位越高,工程领域的男女比例差距越大。

调查结果摘要和重要性

注:以下分析仅针对美国人口

麻省理工学院、斯坦福大学、加州大学伯克利分校、康奈尔大学、卡耐基梅隆大学、普林斯顿大学和耶鲁大学的女性计算机科学教师比例样本表明,在美国,女性和男性在计算机科学院系中的比例并不平等。经过仔细观察,合格的计算机和信息科学(CIS)男性和女性博士之间有很大的差距。从 2009 年到 2018 年,独联体博士学位获得者的比例相对保持不变,男性占 80%,女性占 20%。数据趋势表明,自 2009 年以来,独联体博士学位获得者中的女性比例几乎没有增加。另一方面,自 1979 年以来,学术界男女工程师的比例一直在下降。然而,在高等学术机构中,女性和男性工程师的人数仍有很大差距。学术界职位越高,工程领域的男女比例差距越大。

大学是让我们的下一代接触充满机遇的世界的时候!直到大学的时候,我遇到了一位女性计算机科学家,我才开始尝试编码。对我来说,看到女性代表很重要。对许多其他人来说,大学可能是他们第一次在他们想从事的职业中看到与他们相似的人。我注意到我的研究中的一个限制是我寻求更多的数据。至于下一步,我想扩大视角/收集更多数据,并研究其他因素,如种族、年龄、居住地、健康状况等。此外,我希望调查是什么阻碍了更多女性进入高等院校的计算机科学/工程专业。

这个项目的完整代码可以在我的 github 中找到。
如果你喜欢阅读或者学到了新的东西,请随时给我一个👏
考虑分享这个来开始对话!🤔

用 nasapy 分析未来十年的近地天体

原文:https://towardsdatascience.com/analyzing-the-next-decade-of-earth-close-approaching-objects-with-nasapy-8a6194c4a493?source=collection_archive---------40-----------------------

昆廷·凯梅尔在 Unsplash 上的照片

在这个例子中,我们将通过提取美国宇航局喷气推进实验室的小天体数据库确定的未来 10 年接近地球的物体,来遍历nasapy库的一个可能的用例。

在开始之前,导入我们将用来提取和分析数据的包。数据分析库 pandas 将用于获取数据,而 seaborn 用于绘制数据。加载神奇命令[%matplotlib inline](https://ipython.readthedocs.io/en/stable/interactive/plotting.html#id1)以显示生成的图形。

**import** **nasapy**
**import** **pandas** **as** **pd**
**import** **numpy** **as** **np**
**import** **matplotlib.pyplot** **as** **plt**
**import** **seaborn** **as** **sns**

%**matplotlib** inline

nasapy库的close_approach方法允许人们访问 JPL SBDB 以提取与地球附近已知流星体和小行星相关的数据。设置参数return_df=True会自动将返回的 JSON 数据强制转换成 pandas 数据帧。提取数据后,我们将几个变量转换成float类型。

ca = nasapy.close_approach(date_min='2020-01-01', 
date_max='2029-12-31', return_df=**True**)

ca['dist'] = ca['dist'].astype(float)
ca['dist_min'] = ca['dist_min'].astype(float)
ca['dist_max'] = ca['dist_max'].astype(float)

返回数据的dist栏以天文单位(AU)描述了物体的标称接近距离。一个天文单位或 AU 大致是地球到太阳的距离,约为 92,955,807 英里或 149,598,000 公里。使用[.describe](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.describe.html)方法,我们可以显示总结数据的描述性统计数据。

ca['dist'].describe()count    729.000000
mean       0.030775
std        0.012718
min        0.000252
25%        0.021520
50%        0.031960
75%        0.041442
max        0.049983
Name: dist, dtype: float64

我们看到澳大利亚的平均进近距离约为 0.031,我们可以将其转换为英里数:

au_miles = 92955807.26743
ca['dist'].describe()['mean'] * au_miles2860693.219664113

因此,在未来十年中,接近地球的物体的平均距离约为 286 万英里,是地球到月球距离(238,900 英里)的 10 倍以上。

未来十年内最接近地球的物体会怎样?使用[.loc](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.loc.html)方法,我们可以找到距离最近的物体。

ca.loc[ca['dist'] == ca['dist'].min()]

最近的已知天体预计将在 2029 年 4 月 13 日接近地球,距离地球 0.00023 天文单位。将天文单位转换成英里,我们可以更好地了解物体的接近距离。

print('Distance: ' + str(au_miles * ca['dist'].min()))
print('Minimum Distance: ' + str(au_miles * ca['dist_min'].min()))
print('Maximum Distance: ' + str(au_miles * ca['dist_max'].min()))Distance: 23440.92769543333
Minimum Distance: 644.2481158331191
Maximum Distance: 23874.510393069424

我的天啊。看起来这个物体会比较接近地球,大约 23000 英里,在 64423874 英里的范围内。作为比较,最大距离大约是地球到月球距离的 1/10。

让我们对未来十年每年接近地球的物体数量有个大概的了解。首先,我们使用[.apply](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.Series.apply.html)[to_datetime](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.to_datetime.html)的组合将临近日期的年份提取到一个新列approach_year中。

ca['approach_year'] = ca['cd'].apply(**lambda** x: pd.to_datetime(x).year)

使用[.groupby](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.groupby.html)方法,我们创建一个新的数据帧,其中包含每年接近物体的总数。

approaches_by_year = ca.groupby('approach_year').count().reset_index()

我们可以使用 seaborn 的柱状图函数来绘制每年接近物体的数量。

plt.figure(figsize=(10, 6))
p = sns.barplot(x='approach_year', y='h', data=approaches_by_year)
plt.axhline(approaches_by_year['h'].mean(), color='r', linestyle='--')
p = p.set_xticklabels(approaches_by_year['approach_year'], rotation=45, ha='right', fontsize=12)

每年接近地球的物体数量

有趣的是,今年(2020 年)将有最多的活动,然后在接下来的几年里会有所下降,直到十年结束。平均来说,这十年中每年有不到 80 个接近地球的物体。

作为最后一个例子,让我们使用 seaborn 的[.kdeplot](https://seaborn.pydata.org/generated/seaborn.kdeplot.html)绘制接近物体距离的分布,它创建了一个内核命运图。我们也可以像上面那样添加一条距离的中线。

plt.figure(figsize=(14, 6))
plt.axvline(ca['dist'].astype(float).mean(), color='r', linestyle='--')
sns.kdeplot(ca['dist'], shade=**True**)

地球近距离物体距离的分布

正如我们上面提到的,平均接近距离略大于 0.03,我们可以在上面的密度图中看到。最后,我们可以使用 numpy.random.normal 绘制距离分布的正态分布图,以快速比较实际分布和正态分布。

plt.figure(figsize=(14, 6))

x = np.random.normal(size=len(ca['dist']), 
                     scale=ca['dist'].std(), 
                     loc=ca['dist'].mean())

plt.axvline(ca['dist'].mean(), color='r', linestyle='--')

sns.kdeplot(ca['dist'], shade=**True**)
sns.kdeplot(x, shade=**True**)

与正态分布相比的地球近距离接近物体距离的分布

我们看到距离的分布不太正常。肯定有更复杂的技术来分析数据集的分布,但这将留到将来可能的练习中。

机器学习中分类模型的性能分析

原文:https://towardsdatascience.com/analyzing-the-performance-of-the-classification-models-in-machine-learning-ad8fb962e857?source=collection_archive---------46-----------------------

探索混淆矩阵、ROC-AUC 曲线和机器学习中分类的成本函数的基础。

萨法尔·萨法罗夫在 Unsplash 上拍摄的照片

混淆矩阵

混淆矩阵(也称为误差矩阵)用于分析分类模型(如逻辑回归、决策树分类器等)的好坏。)执行。我们为什么要分析模型的性能?分析模型的性能有助于我们发现并消除偏差和方差问题(如果存在),也有助于我们微调模型,使模型产生更准确的结果。混淆矩阵通常用于二分类问题,但也可以扩展到多分类问题。

混淆矩阵的术语

二元分类的混淆矩阵。图片来源

用例子来说明概念会更好理解,所以让我们考虑一个例子。让我们假设一个家庭去检测 COVID19。

真阳性(TP): 真阳性是被预测为阳性的病例,他们确实患有该疾病。

假阳性(FP): 假阳性是已经被预测为阳性但他们并没有那种疾病的病例。

真阴性(TN): 真阴性是被预测为阴性的病例,他们确实没有患那种疾病。

假阴性(FN): 假阴性是已经被预测为阴性,但是他们患有该疾病的病例。

敏感度:敏感度又称为召回率和真阳性率。敏感度是实际阳性中被正确预测为阳性的比例。换句话说,敏感度是真阳性与真阳性和假阴性之和的比率。

PC:作者

特异性:特异性也叫真阴性率。特异性是被正确预测为阴性的实际阴性的比例。换句话说,特异性是真阴性与真阴性和假阳性之和的比率。

PC:作者

精度:精度是被正确预测为阳性的预测阳性的比例。换句话说,精度是真阳性与真阳性和假阳性之和的比率。

PC:作者

F1 得分: F1 得分定义为精度和召回率的调和平均值。F1 分数从 0 到 1,0 为最差分数,1 为最高分数。当数据遭受类别不平衡时,可以使用 F1 分数,因为它同时考虑了假阳性和假阴性。

PC:作者

准确度:模型的准确度被定义为真阳性和真阴性之和与预测总数的比率。精确度范围从 0 到 100。当必须获得真阳性和真阴性时,可以使用准确性。

PC:作者

ROC 曲线

ROC-AUC 曲线(接收操作者特征-曲线下面积)有助于分析不同阈值设置下的分类性能。类别的高真阳性率(TPR/灵敏度)描述了该模型在分类该特定类别时表现良好。可以比较各种模型的 ROC-AUC 曲线,具有高 AUC(曲线下面积)的模型被认为表现良好。换句话说,该模型在各种阈值设置下表现得非常好,产生了高 TPR(真阳性率)。

PC:作者

分类的成本函数

成本函数通过考虑实际值和预测值来帮助衡量模型的性能。

交叉熵损失

交叉熵损失也称为对数损失。对数损失可以应用于目标是二进制的二进制分类问题,也可以应用于多类分类问题。让我们考虑 C 是目标变量中类的数量。

如果 C = 2(二进制分类),对数损失或二进制交叉熵损失计算如下,

  • 当实际值 y = 0 时,应用[(1-y) * log(1- 𝑦̂)]其中𝑦̂是 y 的预测值
  • 当实际值 y = 1 时,应用[y *log(𝑦̂]]𝑦̂是 y 的预测值

PC:作者

曲线图为-y * log(𝑦̂)当 y = 1 时(y 是实际值)

PC:作者。用完了德斯莫斯

图为-[(1- y) * log(1- 𝑦̂)]当 y = 0 时

PC:作者。用完了德斯莫斯

如果 C > 2(多类分类)log 损失或 多类交叉熵损失 计算如下,

PC:作者

多类交叉熵损失是针对单个数据实例定义的,而多类交叉熵误差是针对整组数据实例定义的。

稀疏多类交叉熵损失

稀疏多类交叉熵损失非常类似于多类交叉熵损失,除了不同于多类交叉熵损失的真实标签的表示。

PC:作者

在多类交叉熵损失中,真实标签被一热编码,而在稀疏多类交叉熵损失中,真实标签被原样保留,从而减少了计算时间。

多类交叉熵损失中真实标签 y 的表示,

PC:作者

多类稀疏交叉熵损失中真实标签 y 的表示,

PC:作者

摘要

  • 当数据集不平衡时,可以使用 F1 分数。当一个类的样本数多于另一个类的样本数时,数据集被称为不平衡的。
  • ROC-AUC 曲线使用不同阈值设置下的真阳性率和假阳性率绘制。ROC-AUC 曲线有助于找到分类的最佳阈值。
  • 交叉熵损失可以应用于二元和多类分类问题。
  • 稀疏多类交叉熵损失在计算上比多类交叉熵损失更快。

参考

[1] Jason Brownlee,训练深度学习神经网络时如何选择损失函数

[2] Scikit-learn,接收操作员特征

[3] ML- cheatsheet,损失函数-ML 术语文档

LinkedInTwitter上与我联系!

快乐的机器学习!

谢谢大家!

分析超级碗历史数据集(1967-2020)

原文:https://towardsdatascience.com/analyzing-the-superbowl-history-dataset-1967-2020-fdee01a760c9?source=collection_archive---------33-----------------------

历史超级碗数据的探索性数据分析

Jean-Daniel Francoeur 在 Pexels 上拍摄的照片

超级碗是一年一度的比赛,决定了美国国家橄榄球联盟(NFL)的冠军。这是世界上最受关注的年度体育赛事之一,拥有大量的国内观众。平均每年美国有超过 1 亿人收看超级碗比赛。

在本帖中,我们将分析超级碗历史数据集(1967–2020)。我们将为获胜球队、体育场、获胜点数和最有价值球员等信息生成汇总统计数据和数据可视化。我们将使用的数据可以在这里找到。

让我们开始吧。

首先,让我们使用 pandas 导入数据:

import pandas as pd
df = pd.read_csv("superbowl.csv")

接下来,我们可以打印列的列表:

print(df.columns)

如你所见,有 10 列。让我们打印前五行:

print(df.head())

我们可以看到有几个分类列。让我们定义一个将数据框、列名和限制作为输入的函数。当被调用时,它打印分类值的字典以及它们出现的频率:

def return_counter(data_frame, column_name, limit):
   from collections import Counter    print(dict(Counter(data_frame[column_name].values).most_common(limit)))

让我们将我们的函数应用到最有价值球员(“MVP”)一栏,并将我们的结果限制在五个最常见的值:

return_counter(df, 'MVP', 5)

我们看到汤姆·布拉迪拥有最多的 MVP 记录,其次是乔·蒙塔纳。

来源

让我们将函数应用于“Stadium”列:

return_counter(df, 'Stadium', 5)

路易斯安那州的 Superdome、Rose Bowl 和 Orange Bowl 在数据集中出现了五次。

让我们试试“获胜者”栏,它对应于获胜的团队:

return_counter(df, 'Winner', 5)

新英格兰爱国者队和匹兹堡钢人队并列六胜。

我鼓励您将此函数应用于剩余的分类列,如“州”、“城市”和“失败者”。

正如您所看到的,这是一个有用的快速测试,可以查看数据中是否有任何明显的不平衡,这通常是建模时需要处理的一个关键问题。

接下来,从数字列(如“Winner Pts ”,即获胜团队获得的分数)中生成汇总统计数据会很有用。让我们定义一个采用数据框、分类列和数字列的函数。每个类别的数字列的平均值和标准偏差存储在数据框中,并且数据框根据平均值以降序排序。如果您想要快速查看特定类别对于特定数字列是否具有更高或更低的平均值和/或标准偏差值,这将非常有用。

def return_statistics(data_frame, categorical_column, numerical_column):
    mean = []
    std = []
    field = []
    for i in set(list(data_frame[categorical_column].values)):
        new_data = data_frame[data_frame[categorical_column] == i]
        field.append(i)
        mean.append(new_data[numerical_column].mean())
        std.append(new_data[numerical_column].std())
    df = pd.DataFrame({'{}'.format(categorical_column): field, 'mean {}'.format(numerical_column): mean, 'std in {}'.format(numerical_column): std})
    df.sort_values('mean {}'.format(numerical_column), inplace = True, ascending = False)
    df.dropna(inplace = True)
    return df

我们可以查看“获胜者”和“获胜者分数”的汇总统计数据:

stats = return_statistics(df, 'Winner', 'Winner Pts')
print(stats.head(15))

旧金山 49 人队的平均“赢家分数”和“赢家分数”的标准差最高。

接下来,我们将使用箱线图来显示基于最小值、最大值、中值、第一个四分位数和第三个四分位数的数值分布。如果您对它们不熟悉,可以看看文章了解 Boxplots

与汇总统计函数类似,此函数采用数据框、分类列和数值列,并根据限制显示最常见类别的箱线图:

def get_boxplot_of_categories(data_frame, categorical_column, numerical_column, limit):
    import seaborn as sns
    from collections import Counter
    keys = []
    for i in dict(Counter(df[categorical_column].values).most_common(limit)):
        keys.append(i)
    print(keys)
    df_new = df[df[categorical_column].isin(keys)]
    sns.set()
    sns.boxplot(x = df_new[categorical_column], y =      df_new[numerical_column])

让我们为 5 个最常见的获胜团队的“获胜者分数”生成箱线图:

get_boxplot_of_categories(df, 'Winner', 'Winner Pts', 5)

我们还可以定义一个函数来显示获胜点的时间序列图。首先,让我们将“日期”转换成日期时间对象:

df['Date'] = pd.to_datetime(df['Date'])

接下来,让我们定义一个函数,该函数将数据框和数字列作为输入,并显示“Winner Pts”的时间序列图:

def get_time_series(data_frame, numerical_column):
    import matplotlib.pyplot as plt
    df_new = data_frame
    plt.scatter(df_new['Date'], df_new[numerical_column])
    plt.xlabel('Date')
    plt.ylabel(numerical_column)

让我们用数据帧和“赢家 Pts”调用函数:

get_time_series(df, 'Winner Pts')

最后,让我们定义一个函数,它将一个数据框和一个数字列作为输入,并显示一个直方图:

def get_histogram(data_frame, numerical_column):
    df_new = data_frame
    df_new[numerical_column].hist(bins=100)

让我们使用数据框调用函数,并生成优胜点直方图:

get_histogram(df, 'Winner Pts')

我就讲到这里,但是请随意处理数据并自己编码。

概括地说,我回顾了几种分析超级碗历史数据集的方法。这包括定义生成汇总统计数据的函数,如平均值、标准差和分类值的计数。我们还定义了用箱线图、直方图和时间序列图来可视化数据的函数。我希望这篇文章有趣。这篇文章的代码可以在 GitHub 上找到。感谢您的阅读!

用 R .分析乌克兰战争

原文:https://towardsdatascience.com/analyzing-the-war-in-ukraine-using-r-39d0c3379938?source=collection_archive---------42-----------------------

使用 tidyverse 和地图对冲突进行探索性分析!

2014 年,乌克兰顿涅茨克州和卢甘斯克州的俄罗斯分裂分子宣布独立。自行宣布成立的顿涅茨克和卢甘斯克人民共和国与乌克兰政府之间爆发了冲突。这一冲突非常复杂,涉及无数变数。人们写了关于这场冲突的书籍、文章和各种观点。我很好奇数据显示了什么。

经过一番努力,我偶然发现了人道主义数据交换:https://data.humdata.org/。那里有很多非常有趣的数据。对我来说幸运的是,有一个关于乌克兰冲突的非常好而且相当精细的数据集。数据集中的每一行代表一个动力学事件。数据跨度从 2014 年的亲欧盟示威革命到 2018 年。我将数据读入 R 中,并通过删除一些我不感兴趣的列(我在下面的代码中省略了这一部分)对其进行了修改。我们将从检查 na 的数据开始(确保您加载了 tidyverse!):

# read in the data 
conflict <- read_csv('conflict_data_ukr - Raw.csv')# make.names on the columns
colnames(conflict) <- make.names(colnames(conflict))# check for NA's in the column adm_1
sum(is.na(conflict$adm_1))#119
conflict[is.na(conflict$adm_1),]#   all of the NAs in adm_1 are for the donets basin area which 
#   isn't contained in one oblast.# replace NA's in adm_1 w/ 'Donets Basin Area'
conflict$adm_1 <- conflict$adm_1 %>% replace_na('Donets Basin Area')
any(is.na(conflict)) # FALSE

“adm_1”变量包含事件发生在哪个州的信息(想象一个像美国大县一样的州)。我担心的是那一栏中的 na。第一行代码告诉我那里有 119 个 na。我能够对第二行代码中的数据进行子集划分,并根据“where_coordinates”列中的信息了解到所有 na 都是“Donets Basin Area”中的事件。我将 adm_1 列中的 na 替换为“Donets Basin Area ”,然后重命名了一些列:

# rename some columns 
conflict <- conflict %>% rename(gov.forces = side_a
                                , op.forces = side_b
                                , kia = deaths_a
                                , ekia = deaths_b
                                , civcas = deaths_civilians
                                , region = adm_1)

出于本分析的目的,KIA =乌克兰军队被杀,EKIA =分离势力被杀。其余的变量非常简单。我首先想探究乌克兰在一个事件中损失最多的是什么:

# what was the maximum number of KIA in an event
max(conflict$kia)
[1] 366

哇哦。在一次事件中,政府军损失了 366 名军人。让我们进一步探讨这个问题。

# subset for the information regarding the mass casualty event
conflict[conflict$kia == 366,]

这行代码为我提供了该特定事件的所有数据。事情发生在 2014 年 9 月 2 日,顿涅茨克州的伊洛瓦伊斯克。伊洛瓦伊斯克战役意义重大,因为据称这是俄罗斯正规军首次越过边境参与冲突。继续,我们来看看起亚的分布。

hist(conflict$kia, breaks = 100
     , xlab = "KIA"
     , main = "Ukrainian KIA\n2014-2018")

数据分布非常不对称。像伊洛瓦伊斯克战役这种大量死亡的事件正在创造一条向右的长尾。让我们把数据标准化。

# histogram of KIA normalized
hist(log10(conflict$kia)
     , xlab = "Number of KIA (Normalized)"
     , main = "Ukrainian KIA\n2014-2018")

我决定采用这个概念,为起亚和 EKIA 绘制密度图:

# initialize the plot space
par(mfrow = c(2,1))
par(bty = 'n')# density plot for kia (Ukrainian Government Forces)
a <- density(log10(conflict$kia))
plot(a
     , xlab = 'Number of Deaths (normalized)'
     , ylab = 'Frequency'
     , main = "Distribution of Ukranian Forces Killed-In-Action per Engagement\n
     2014-2018")
polygon(a, col = '#7E8777', border = "black")# density plot for ekia (Russian Separatist Forces)
b <- density(log10(conflict$ekia))
plot(b
     , xlab = 'Number of Deaths (normalized)'
     , ylab = 'Frequency'
     , main = "Disribution of Enemy Forces Killed-In-Action per Engagement\n
     2014-2018")
polygon(b, col = '#DA291C', border = 'black')

这些图表明的是,大多数事件的双方都有一个或更少的伤亡。然而,也有一些导致重大损失的重大交战。这就是为什么我们有长尾巴。

接下来的问题是,战斗集中在哪里?让我们创建一个柱状图来提供一些背景信息:

#----------------------------------------------------------
# barplot for deaths by region
#----------------------------------------------------------
df <- aggregate(conflict$best, list(conflict$region), sum)
df <- as_tibble(df) 
df <- df %>% rename(Region = Group.1
                    , no.ofDeaths = x) 
df <- df %>% arrange(no.ofDeaths)par(mar = c(5,13,5,1), bty = 'n')barplot(df$no.ofDeaths 
        , names.arg = df$Region
        , horiz = T
        , las = 1 
        , col = '#DA291C'
        , border = NA
        , xlab = "Total Number of Deaths"
        , main = 'Deaths by Region in the Ukrainian Conflict\nNote: "Donets Basin Area" includes both Luhansk and Donetsk Oblasts\nYears: 2014-2018')

看起来顿涅茨克州是战斗最激烈的地方。然而,目前还不清楚有多少打着“顿涅茨克盆地地区”标签的战斗实际上发生在顿涅茨克州或卢甘斯克州。我认为理解这些数据的更好的方法是用 R!

# load some packages
library(pacman)
p_load(maps, mapproj, rnaturalearth, scales, ggplot2)# load the data
ukr <- read_csv('conflict_data_ukr - Raw.csv')# Calculate area of a circle based on deaths per event
radius <- sqrt( ukr$best/pi )# Plot the map
ykr <- raster::getData('GADM', country = "UKR", level = 1)map(ykr)# add the points
points(ukr$longitude, ukr$latitude
       , cex = rescale(radius, c(0, 12))
       , col = 'red'
       , pch = 21)

这样更好,不是吗?圆圈区域表示每个事件的死亡人数(包括 KIA、EKIA 和平民死亡人数)。你可以看到在交战双方多次交战的区域周围形成了同心圆。地图中心附近环绕基辅的圆圈代表亲欧盟示威运动革命。底部附近还有一个小圆圈,代表亲欧盟示威运动革命期间发生在敖德萨的事件。让我们仔细看看顿涅茨克州和卢甘斯克州(俗称顿巴斯)。

# plot the Donbass
map(ykr, namefield = 'NAME_1', region = c("Donets'k", "Luhans'k"))# add the points
points(ukr$longitude, ukr$latitude
       , cex = rescale(radius, c(0, 12))
       , col = 'red'
       , pch = 21)

在上图中,顿涅茨克州在左边,卢甘斯克州在右边。如你所见,大部分战斗发生在顿涅茨克或边境。地图上最大的圆圈表示伊洛瓦伊斯克战役,前面提到过。

冲突是社会中令人讨厌的现实,但它产生了一些有趣的数据供探索。希望这篇文章能帮助你找到一些代码,或者对你理解东欧地缘政治有所启发。我很喜欢这篇文章的一些反馈,所以如果你愿意,请留下评论!

分析“倾斜”赢得更多游戏(英雄联盟)

原文:https://towardsdatascience.com/analyzing-tilt-to-win-more-games-league-of-legends-347de832a5b1?source=collection_archive---------23-----------------------

来源

“一种精神或情感混乱或沮丧的状态,玩家采用了非最佳策略,通常导致玩家变得过于激进”

—倾斜的定义(维基百科)

倾斜的。扑克玩家中常用的一个术语,尽管它也被更广泛的游戏社区完全采用。据说,它的起源来自机械弹球机,如果玩家试图倾斜机器,它会冻结脚蹼,有时甚至会显示警告:“倾斜”。

现在它被用来形容一个人由于某种极端情绪(通常是愤怒)而做出不理智的行为。在扑克游戏中,这可能是连败后在弱牌上全押。在游戏中,这可能是在敌人在聊天中奚落你之后向他们冲锋。然而,它也可以应用于任何数量的“现实生活”场景。当他们过了糟糕的一天,谁没有对他们所爱的人发过脾气?这是倾斜。当情绪高涨的时候你的行为是不理智的

现在,那些玩过任何竞争性团队游戏的人无疑会成为他们的同伴玩家的倾斜诱导行为的受害者。这一点在热门网游《英雄联盟》中尤其成问题。该游戏既有激烈的形式(激烈的 5 对 5 场比赛)和梯形排名系统,使其成为一种情感的努力。最近我自己经历过(非常不幸的 13 连败),我决定看看是否有任何方法可以在游戏数据中识别它。

注: 所有收集的数据都是使用 Riot Data API,运行超过 10 万场。GitHub:https://GitHub . com/JackWillz/Projects/blob/master/Data % 20 analysis % 20 and % 20 API % 20-% 20 ol % 20 tilt/tilt _ trend . ipynb

我想回答的第一个问题很简单。输了之后,你更有可能输吗?答案并不令人惊讶,如下所示:

基于玩家先前游戏结果的胜率。

注: 我稍微调整了一下基线,因为我取样的选手胜率都在平均水平以上。

这意味着最近输了 2 场比赛的玩家(根据图表,2 连败)将有 48.8%的机会赢得下一场比赛。然而,我们不能在这里结案,宣布 tilt 已经被证实。据统计,最近输了 2 场比赛的玩家比最近赢了 2 场比赛的玩家更有可能成为糟糕的玩家!玩家可能没有“倾斜”,他们可能只是比平均水平差。

在这种情况下,我们如何更好地理解 tilt,而不陷入这个将坏玩家与好玩家进行比较的陷阱?为此,我们利用了倾斜是一种暂时情绪状态的事实,这种情绪状态随着时间迅速衰减。有没有对某人发脾气却很快又后悔的经历?这同样适用于这里。

知道了这一点,我们可以看看这些失败玩家的胜率,除了这一次他们在玩下一个游戏之前等待了多长时间。为此,我们研究了排名在“金牌”(前 24%的玩家)的人,他们已经输掉了前 2 场比赛。然后我们发现了他们上一次游戏结束和下一次游戏开始之间的时间差。其结果可以在下面找到:

黄金的胜率对玩家的连败进行排名,这取决于他们在玩下一个游戏之前等待的时间。

那些在两连败后采取不休息的人,到目前为止胜率最低!根据我们对 tilt 的了解,我们可以认为,那些带着因失败而积累的情绪包袱参加下一场比赛的人会导致他们在下一场比赛中表现不佳,从而大大影响他们球队的机会。

然而,有趣的是,那些只短暂休息的人不仅胜率有所提高,而且与平均水平(刚刚输了两场比赛的金牌玩家)相比,胜率超过了 3%。有几个理论,我已经为此辩论过,但我更喜欢的是,输了 2 场比赛并休息一段时间的球员既“热身”,又没有情感债务。如果你有任何替代的想法,请让他们被听到!最后,那些长时间休息(平均 3-4 个小时,但可能是几天)的人,他们的胜率会回到接近 2 场比赛输球的平均水平。

关于这个话题,我感兴趣的最后一个问题是,不管玩家的技能等级如何,这种关系是否成立。为了测试这个理论,我对“钻石 I”玩家(前 1%)重新进行了实验。结果出乎意料..

Diamond I 的胜率对玩家连败进行排名,取决于他们在玩下一个游戏之前等待的时间。

虽然长时间休息的玩家以同样的方式回归均值,但是“立即”和“短时间休息”之间的关系已经被翻转了!那些排名第一梯队的玩家实际上会发现,如果他们在失败后定期短暂休息,他们的胜率会大幅下降,而立即玩的玩家会发现,与普通玩家相比,他们的胜率会有小幅提高

对此我有两种流行的理论:在这个竞争激烈的环境中(许多玩家每天投入超过 8 个小时),他们已经习惯了处理倾斜,否则,他们永远不会走到这一步。其次,在这个层面上,已经变成了数字游戏。竞争的程度如此之高,以至于真正起作用的是你投入的时间。这个级别的英雄联盟一场比赛平均 27 分钟长。一个不休息的球员比一个输球后坐 20 分钟来恢复的球员一天能挤出更多的比赛。

但是,这些都是理论。我相信有一个与游戏相关的心理行为科学的更大的世界相对来说还没有被开发。如果有什么不同的话,我希望这篇文章能让你简短地思考一下可能性——至少在你准备好面对下一场比赛之前。

你已经看到文章的结尾了!我叫 Jack J,是一名将人工智能应用于竞技游戏和电子竞技的专业数据科学家。我是 iTero 的创始人。GGjung.gg 。你可以在 推特 上关注我,加入 iTero Discord 或者给我发邮件 jack@itero.gg 。下一场见。

最初发表于:https://itero.gg/blog

使用 Wordcloud 分析热门 Kickstarter 活动

原文:https://towardsdatascience.com/analyzing-top-kickstarter-campaigns-with-wordcloud-2541cd3f05c8?source=collection_archive---------52-----------------------

照片致谢

自从开始数据科学之旅,我就迷上了文字云。词云(或标签云)是一种独特的数据可视化工具,用于通过词频或意义来探索文本数据。当您有大量的文本数据,并且想要获得分布的基本概念时,这非常方便。

作为一名数据科学家,经过几个月的发展,我终于在上周厌倦了。我想学习如何制作我见过的(但从未忘记的)超级酷的单词云!!!所以,我找到了一个有用的数据营教程,读了一点文档,我准备实践我的新知识。

实践新的可视化类型的第一步是找到适合该可视化的数据集。鉴于我们正在尝试练习一个单词云,我们可能会找到一些包含文本的数据。有许多很好的选择可以实践,比如客户评论、推文或 Youtube 视频字幕,但我在 Kaggle 上找到了一个关于 Kickstarter 项目的数据集,我认为它非常适合这种情况。

import numpy as np
import pandas as pd
from PIL import Image
from wordcloud import WordCloud, STOPWORDSimport matplotlib.pyplot as plt 
%matplotlib inline df = pd.read_csv('ks-projects-201801.csv') 
df.head()

根据数据集在 Kaggle 上的文档,usd_pledge_real 和 usd_goal_real 列是由 Fixer.io API 生成的以美元为单位的真实转换,因此我们应该使用它们并删除所有其他与货币相关的列。

此外,因为 Kickstarter 的目的是筹集尽可能多的资金来启动你的业务,所以一个恰当定义成功的栏是承诺给每个活动的资金量。让我们使用 usd _ pledged _ real 列根据承诺金额检索排名前 500 的 Kickstarter 活动。

# Rename columns and drop unneeded ones 
df['usd_pledged'] = df.usd_pledged_real
df['usd_goal'] = df.usd_goal_realdf_ks = df.drop(columns=['goal', 'usd pledged', 'usd_pledged_real', 'usd_goal_real'])# Get the top 50 Kickstarter campaigns by $ pledgeddf_sorted = df_ks.sort_values(by='usd_pledged', ascending=False)
df_top = df_sorted.head(500)

现在,有趣的部分来了。我们可以利用之前导入的 wordcloud 库,构建一个包含 Kickstarter 前 500 名活动名称的 wordcloud。

WordCloud 对象有一个方法。generate()将从一个字符串生成一个 wordcloud。我们可以使用列表理解来构建一个巨大的字符串,其中包含 Kickstarter 活动的所有名称。

请注意,我们还从 wordcloud 库中导入了停用词。这是一组内置的词,通常不会给文本分析增加太多价值(比如 it,they 等)。).我们可以用 stopwords 参数传入这组单词,以确保排除那些不需要的单词。

有关如何正确设置插值参数的更多信息,请查看 matplotlib 的文档,但现在,它被设置为双线性。

# Join all names and separate them with whitespace
text = " ".join(str(name) for name in df_top.name) # Create stopword list:
stopwords = set(STOPWORDS) # Generate a word cloud image
wordcloud = WordCloud(stopwords=stopwords).generate(text)# Display the generated image:
# the matplotlib way:
plt.imshow(wordcloud, interpolation='bilinear')
plt.axis("off")
plt.show()

只要几行代码,我们就有了单词云!一张小小的照片,这里有很多东西,我们来做一些改进吧。

让我们把背景改成白色,这样更容易阅读。让我们把最小字体变大一点。最后,让我们排除“第一”和“聪明”这两个词,这样我们就可以看到其他词的出现。

# Create stopword list:
stopwords = set(STOPWORDS)# This time, add in your own words to ignore stopwords.update(["First", "Smart"])# Generate a word cloud image
wordcloud = WordCloud(stopwords=stopwords, background_color="white", min_font_size=8).generate(text)# Display the generated image:
# But make it a little larger this time..
plt.figure(figsize=(9,6))
plt.imshow(wordcloud, interpolation='bilinear')
plt.axis("off")
plt.show()

很有意思。以下是我们使用 word cloud 进行分析的一些要点:

  • 每个人都喜欢说自己是第一,和聪明..这似乎很有效,因为他们在这个数据集中做了前 500 个 Kickstarter 活动。
  • 好像和技术有关的战役很多:电动耳机相机3D 打印机等。
  • 这里提到的游戏也比我想象的多…谁知道 Kickstarter 这么有趣?

你已经正式创建了一个单词云!恭喜你!如果您仍然渴望更多的分析,您可以构建更多的词云来研究这些主题:

  • Kickstarter 数据附带了一个类别列,其中包含诗歌和音乐等值。不同类别是否有某些词比较常见?
  • 该数据还有一个包含日期的“已启动”列。旧的活动和新的活动有明显不同的词频吗?

原载于 2020 年 4 月 30 日https://data dreamer . io

在没有 API 的情况下用 Python 分析 Twitter 关系

原文:https://towardsdatascience.com/analyzing-twitter-relationships-in-python-without-an-api-a35b5b502cb?source=collection_archive---------14-----------------------

使用一些著名的 Twitter 账户进行关系分析的简单方法。

社交媒体分析是数据科学的热门话题之一。人们喜欢这些分析,并对它们感兴趣,因为每个人都熟悉这个世界。我们大部分时间都花在了推特、Instagram、脸书和其他一些社交媒体应用上。

作为一名数据爱好者,这个话题引起我的注意并不奇怪。然而,访问官方的 Twitter API 是非常具有挑战性的。于是,我搜索了另一种解决方案,找到了 twint 。这是一个 python 库,使你能够在没有 API 访问的情况下废弃 twitter 数据。

在本文中,我将简要介绍如何借助 twint 收集 twitter 数据,并基于一组 Twitter 用户之间的关注和提及来分析一些关系。

初始化 Python 代码

我们需要 twint 库来抓取数据,需要pandas来创建 dataframes,需要 集合 来获取列表中的分组值计数。

import twint
import pandas as pd
from collections import Counter

然后我们开始创建一个包含 twitter 账户的用户列表。我们的分析将包括这些用户的关系。由于代码运行时间较长,我不建议将拥有超过 5K 追随者的用户添加到此列表中。类似地,一个很长的列表可能也会出现同样的问题。

我选择了一些大家都知道的热门 Twitter 用户,让我们的分析更有趣。

users = [
 **'shakira',
    'KimKardashian',
    'rihanna',
    'jtimberlake',
    'KingJames',
    'neymarjr',**
]

跟踪关系分析

让我们从关系分析开始,并为此编写一个名为get _ followers的函数,用用户名向 twint 库发送请求。该函数将返回我们的输入用户关注的用户列表。

def get_followings(username):

    c = twint.Config()
    c.Username = username
    c.Pandas = True

    twint.run.Following(c)
    list_of_followings = twint.storage.panda.Follow_df

    return list_of_followings['following'][username]

使用 get_followings 函数,我们将为用户列表中的每个人获取不同的列表,并将结果存储到一个字典( followings )和一个列表( following_list )。following_list 是所有 following 的联合版本,我们将在下一节使用它来计算最受关注的 Twitter 帐户。

下面的 for 循环创建了这两个变量。有时 Twitter 不响应我们的请求,在这种情况下,我们会得到一个索引错误。对于这种情况,我在代码中添加了一个例外来跳过这些用户。****

followings = {}
following_list = []
for person in users:
    print('#####\nStarting: ' + person + '\n#####')
    try:
        followings[person] = get_followings(person)
        following_list = following_list + followings[person]
    except KeyError:
        print('IndexError')

我们的用户关注最多的是谁?

在获得以下所有列表后,我们可以简单地计算 following_list 变量中最常见的值,以获得用户中最受欢迎的帐户。为了获得最受关注的 10 个账户,我们将使用收藏库中的计数器函数。

Counter(following_list).most_common(10)

该函数的结果如下所示。蕾哈娜似乎被所有其他人追随,在我们的用户群中,她绝对是最受欢迎的一个。

**[('rihanna', 5),
 ('RyanSeacrest', 3),
 ('BarackObama', 3),
 ('Oprah', 3),
 ('TheEllenShow', 3),
 ('kendricklamar', 3),
 ('KDTrey5', 3),
 ('Drake', 3),
 ('Nike', 3),
 ('NBA', 3)]**

遵循用户之间的关系

如果我们想知道我们的用户组中谁在关注谁呢?为了研究这个问题,我编写了一个 for 循环来检查用户中是否有人在另一个人的列表中。因此,它创建了一个列表字典,显示由真和假表示的以下状态。

follow_relations ={}
for following_user in followings.keys():
    follow_relation_list = []
    for followed_user in followings.keys():
        if followed_user in followings[following_user]:
            follow_relation_list.append(True)
        else:
            follow_relation_list.append(False)
    follow_relations[following_user] = follow_relation_list

在下面的代码中,结果字典被转换成一个 pandas 数据帧,以实现更加用户友好的可视化。数据帧的行显示跟随的用户,而列指示被跟随的用户。

following_df = pd.DataFrame.from_dict(follow_relations, orient='index', columns=followings.keys())

您可以在下面看到分析的输出。我们再次确认蕾哈娜在这张表中的受欢迎程度。所有其他人都跟着她。但是,对于金·卡戴珊,我们不能用类似的方式说话,根据分析,我们的用户群中只有贾斯汀·汀布莱克追随她。

以下关系表

提及次数分析

提及次数是 Twitter 用户之间的另一个强有力的关系指标。下面的函数(get _ note _ count)就是为此而编写的,它返回两个用户之间的单向提及次数。我们应该将提到的用户名放在提词中,在函数中,为了更精确地分离提词,在它的开头添加了一个“ @ ”字符。

def get_mention_count(user, mention_word):

    c = twint.Config()
    c.Username = user
    c.Search = '@' + mention_word
    c.Store_object = True

    twint.run.Search(c)
    tweets = twint.output.tweets_list
    mention_count = len(tweets)
    tweets.clear()

    return mention_count

在分析中,我们将使用两个嵌套的 for 循环来检索每个用户对我们组中所有其他用户的提及次数。因此,我们将得到提 _ 关系字典。

mention_relations = {}
for mentioning_user in users:
    print('#####\nStarting: ' + mentioning_user + '\n#####')
    mention_count_list = []
    for mentioned_user in users:
        mention_count = get_mention_count(mentioning_user, mentioned_user)
        mention_count_list.append(mention_count)
    mention_relations[mentioning_user] = mention_count_list

最后,我们把这本字典转换成一个熊猫的数据框架,它变成了一个可理解的和更容易解释的表格。

mention_df = pd.DataFrame.from_dict(mention_relations, orient='index', columns=mention_relations.keys())

我们可以看到下面提到次数表的输出。同样,行显示提到的用户,列显示提到的用户。对角线值显示了用户提到自己的次数,这些是由转发引起的。如果我们忽略这些值,我们会看到勒布朗·詹姆斯被小组中的每个人提及,而蕾哈娜看起来被除了内马尔之外的每个人提及。另一方面,这个团体中没有人在他们的推文中提到过内马尔。另一个有趣的推论可能是,夏奇拉在她的推文中提到蕾哈娜 52 次,然而蕾哈娜只提到她 7 次。

提及次数表

结论

我试着在著名的 Twitter 用户上解释一些基本的社交媒体分析,只是为了好玩,同时也是为了在不复杂的 python 代码的帮助下做好准备。我希望它们对你有所帮助。最后,可以肯定的是,这些分析有待改进,如果您对本文有任何建议或补充,请不要犹豫,与我们分享。

使用机器学习分析英国大选结果

原文:https://towardsdatascience.com/analyzing-uk-general-election-results-using-machine-learning-137d130634b1?source=collection_archive---------23-----------------------

英格兰各地选区的聚类和分类。

介绍

2019 年 12 月 12 日 22:00,出口民调是英国大选结果的第一个指标。这项民意调查对英格兰、苏格兰和威尔士 144 个选区的 2 万多人进行了调查,准确预测了选举结果。这篇文章使用机器学习来展示英格兰每个选区的选举结果和社会经济条件之间的关系。这是出于对英国地缘政治格局的兴趣,特别是出于进一步了解社会和经济因素如何影响投票方向的愿望。

本文使用 8 个不同的社会经济类别对英格兰 533 个威斯敏斯特议会选区进行聚类和分类(只选择了英格兰选区,因为英国其他民族的数据更少)。相似性传播用于聚类数据,然后随机森林和 AdaBoost 技术用于分类和预测投票方向。

本报告中使用的大部分数据来自下议院网站,除了关于英国退出欧盟公投的数据是由 Hanretty 汇编的估计值(因为英国退出欧盟公投的结果没有按选区统计)。

数据

本文中使用的数据和包含代码清单的 Juypter 笔记本可以从这里下载。

  • 房价中位数:该数据记录于 2019 年 3 月,范围在利物浦、沃尔顿的 72,500 英镑至肯辛顿的 1,450,000 英镑之间。
  • 周工资中位数:该数据记录于 2019 年 4 月,范围从莱斯特东部的 420 到温布尔登的 890。
  • 在英国出生的居民比例:该数据记录在 2011 年人口普查中,从布伦特北部的 41%到霍顿和桑德兰南部的 98%不等。
  • 住房使用权:对拥有自己住房的人的百分比的一种衡量。这一数据是在 2011 年人口普查中收集的,从哈克尼南部和肖尔迪奇的 20%到塞夫顿中部的 97%不等。
  • GCSE 成绩:2018 年 GCSE 成绩达到 5 A*到 C 的学生比例已有记录,从 Knowsley 的 38%到 Altrincham 和 Sale West 的 85%不等。
  • 年龄分布:这一类别将选区居民人口划分为 9 个年龄组(0-9 岁、10-19 岁等,直到 80 岁以上),并已作为一个社会指标包括在内。这一数据是在 2011 年人口普查中收集的。
  • 英国退出欧盟立场:对每个选区投票脱离欧盟的居民百分比的估计。这一数据是从 2016 年英国退出欧盟公投结果中推断出来的,范围从哈克尼北部和斯托克纽因顿的 20%到波斯顿和斯开格内斯的 76%不等。

测绘

这些地块是使用 Geopandas 库结合来自 UK-GeoJson 站点的边界形状数据创建的。

使聚集

相似性传播(AP)是一种基于通信的聚类算法,由 Frey 和 Dueck 于 2007 年首次提出。该算法将数据点描述为网络中的一个节点,该节点通过经由图的边彼此发送信号来传达它们的聚类偏好。该通信用于定位最佳范例节点(代表它们自己的集群的最佳数据点)并将最合适的数据点分配给该集群。用于确定信号幅度的主要度量是相似性得分,其被计算为两个数据点之间的负欧几里得平方距离。使用相似性传播,首先识别和标记最小和最离散的聚类。这解释了当你离开伦敦市中心时,从暗到亮的颜色渐变,如图 1 所示。

图 1:英格兰选区使用相似性传播聚类成 32 个组。

使用 Davis Bouldin 指数,聚类的最佳数量被确定为 6。新的选区聚类如图 2 所示(使用 K-means)。伦敦市中心形成了自己独特的集群,这在英格兰其他地方是看不到的。这可能是由极高的房价推动的。

图 2:英格兰选区使用 K 均值聚类成 6 组。

分类

我用随机森林和 Adaboost 技术预测了每个选区的投票方向。随机森林产生的分类如下图 3 所示。

图 3: 2019 选举结果预测 vs 现实使用随机森林。使用 Photoshop 对图像进行色彩校正。

决策树分类器和随机森林在训练数据上始终达到 100%的准确率,在测试数据上达到 80-90%的准确率(当训练 2017 年和 2019 年的选举结果时)。每个属性的预测能力都是从随机森林中提取的,如图 4 所示。引入一个随机变量作为参考。任期被证明提供了关于投票方向的最多信息。对此的一种解释是,在城市里有更多的租房者,城市席位往往被工党赢得(特别是在伦敦)。

图 4:分别使用 2017 年选举和 2019 年选举结果的每个属性的预测能力。

AdaBoost 的表现相对较差,经常使用不合逻辑的属性组合来进行糟糕的分类。这是因为 AdaBoost 不能很好地处理外围数据:它会陷入尝试对错误数据点进行分类的循环中。属于众议院议长、绿党和潜在的自由民主党的席位可能会在这里引发问题。

结论

使用相似性传播和 K-means 聚类算法,有可能将英语选区分解成逻辑组,这些逻辑组已被绘制以便于可视化。相似性传播提供了关于哪些选区最相似以及哪些聚类最离散的信息;这最终产生了一种从伦敦市中心向外延伸的颜色渐变,这种渐变基于标签被分配的顺序。

使用随机森林可以提取每个属性的预测能力,并证明它们都为决策过程提供了有意义的贡献。分类的准确率在 80-90%之间,但是除了工党和保守党之外,没有其他席位被预测到。任期被证明是两次选举的主导因素,在 2019 年贡献了大约 19%的预测力。你可以看到 2017 年至 2019 年间英国退出欧盟立场的预测能力有了有趣的增长,这可能是由于工党席位数十年来首次转向保守党,因为他们希望看到英国退出欧盟完成。

参考

[1]汉莱蒂,克里斯。"面积插值和英国的欧盟成员资格公投."《选举、舆论与政党杂志》27.4(2017):466–483。

[2]弗雷、布伦丹·j 和德尔伯特·杜克。"通过在数据点之间传递消息进行聚类."科学 315.5814(2007):972–976。

分析和可视化亚马逊红移数据—教程

原文:https://towardsdatascience.com/analyzing-visualizing-amazon-redshift-data-tutorial-239aa6443d43?source=collection_archive---------58-----------------------

学习使用 Knowi 分析和可视化亚马逊红移数据

活动发起人Unsplash 上的照片

目录

介绍

亚马逊红移是亚马逊基于云的关系数据库管理系统(RBDMS)。像亚马逊的大多数产品一样,亚马逊红移非常受欢迎,这是有原因的:它不仅是目前最快的云数据仓库,而且每年都在变得更快。

在 Knowi,我们为分析和报告提供与亚马逊红移的广泛原生集成。这使我们的用户能够不受任何限制地利用 Redshift 的速度和可扩展性,并快速分析来自 Redshift 的数据,形成有价值的见解。如果您有兴趣学习如何使用 Knowi 来分析来自 Amazon Redshift 的数据,那么您来对地方了。

设置您的 Amazon 红移数据源

登录到你的 Knowi 试用账户后,你要做的第一件事就是连接到 Amazon Redshift 数据源,并确认你的连接成功。这是怎么回事:

1.在屏幕左侧的面板上找到“数据源”并点击它。

2.前往“数据仓库”,点击亚马逊红移。

3.我们不需要改变这里的任何参数;我会自动为我们输入所有信息。只需点击屏幕底部的“测试连接”。

4.确认连接成功后,单击“保存”

您的数据源现在已经设置好了。干得好!

从数据源中查询数据

您的数据源现在已经设置好了,这意味着是时候开始查询数据了。下面是如何做到这一点:

1.保存数据源后,您应该会在页面顶部收到一个警告,提示“Datasource Added”。配置查询。点击单词查询。(否则,您可以返回到屏幕左侧的面板,转到“数据源”下方,然后单击“查询”。然后从右上角选择“新查询+”。)

2.在屏幕左上方的“报告名称*”中为您的报告命名。我们将在这里分析一个电子邮件活动,所以我们称之为“电子邮件活动”

3.在查询构建器中,单击“表”栏内部。向下滚动到“public.demo_sent”并点击它。这将自动设置一个红移查询,返回该表中的数据。

4.前往屏幕的左下方,点击蓝色的“预览”按钮,预览数据。您应该看到电子邮件活动的结果,其中包括各种数据,如发送、打开和点击的电子邮件数量,以及消息类型和客户。

5.查看完数据后,滚动到屏幕右下角,点击绿色的“保存并立即运行”按钮。

一旦您的查询成功完成,Knowi 会自动将您的查询结果保存为虚拟数据集,然后将该查询的结果作为数据集存储在其弹性数据仓库中。每次运行查询时,我都会这样做。

分析和可视化您的数据

尽管你花了一点时间查看你的数据,但是你不太可能从它的格式中学到任何东西。利用我们的数据,我们可以发现很多事情,但假设我们必须回答一个紧迫的问题:发送给某些客户的电子邮件有更高的转化率吗?Knowi 允许我们有效地回答这个问题,然后通过以下步骤可视化我们的结果:

1.前往屏幕左侧面板的顶部,点击“仪表板”单击橙色加号图标并命名您的仪表板。我们称之为“电子邮件可视化”

2.回到面板,就在“仪表板”下面,点击“小部件”选择您刚刚创建的“电子邮件活动”小部件,并将其拖到您的控制面板上。

3.现在,你看到的可视化只是一个数据网格。我们将把它改成对眼睛稍微好一点的东西,但是首先我们必须添加我们正在寻找的度量。为此,请滚动到小部件的右上角。单击 3 点图标,然后向下滚动到“分析”并单击它。

4.前往屏幕的左上角,找到“+添加功能”我们要创建的函数非常简单:它叫做“转化率”,计算方法是将“转化率”除以“发送量”为了进行计算,点击“+添加函数”,然后将“名称”设置为转换率,将“操作”设置为(转换率/发送数)*100。

5.现在,我们看到的只是每个电子邮件活动的转换率。我们希望看到的是按客户分组的所有电子邮件活动的转换率,我们还希望我们的数据按转换率排序。首先,我们需要将“customer”栏从屏幕左侧拖动到“Grouping/Dimensions:”框中,然后放开。

6.现在,我们只需将新的“转换率”指标从“字段/指标:”拖动到“排序方式:”并将方向改为降序,以便从最高转换率到最低转换率对数据进行排序。如你所见,脸书邮件的转化率刚刚超过 1%,而网飞邮件的转化率不到 0.5%。

7.现在是时候想象一切了。回到屏幕顶部,点击“可视化”将可视化类型从“数据网格”更改为“列”

8.现在你可以看到每个客户的电子邮件转化率排名。前往屏幕的右上角,点击看起来像两张纸的“克隆”图标。将此命名为“电子邮件活动—转化率”,然后单击橙色的“添加到仪表板”按钮。

就这样,您将原始数据转化为包含有价值信息的可视化数据。脸书的转换率大约是网飞的两倍半,这一事实可能会在未来的决策中得到考虑。

向可视化添加明细

改进我们的可视化的下一步是通过添加钻取使它更具交互性和可导航性。向下钻取是 Knowi 中的一个强大功能,允许用户只需点击一下鼠标,就可以更深入地钻取原始数据的过滤部分。下面是我们如何向小部件添加明细:

1.单击新部件右上角的 3 点图标,向下滚动到“明细”并单击它。

2.将您的钻取类型设置为“Widget”,设置为在单击“Customer”时钻取“Email Campaign”,并在可选的钻取过滤器中设置 customer = customer。单击明细弹出窗口右下角的橙色“保存”按钮。

3.点击转化率最高的客户脸书进行测试。如您所见,这将返回脸书作为客户的所有活动。然后回到你最初的可视化,回到你的部件的右上角,点击那个角中间的左箭头图标。

使用基于搜索的分析查询您的数据

您的仪表板已经设置好,这意味着您已经做好充分准备,可以使用基于搜索的分析来查询您的数据。这意味着您可以与任何讲英语的人分享您的仪表板和数据,即使他们不熟悉 Knowi。以下是如何使用基于搜索的分析来查询您的数据:

1.前往你原来的“电子邮件活动”部件的右上角,点击 3 点图标。向下滚动并点击“分析”

2.假设您想要按月监控电子邮件活动,以查看不同月份的情况是否有所不同。为了做到这一点,在你的屏幕顶部的搜索栏中输入“发送总量,打开总量,点击总量,按月转换总量”,然后点击回车。Knowi 的自然语言处理会很快给你提供你想要的东西。

3.现在是时候可视化这些数据了。回到“可视化”,将可视化类型设置为“区域”这将显示我们发送的电子邮件总数,以及每月的转换总数。转化率如此之低,以至于我们肉眼很难看到数字的任何变化,但这没关系。

4.回到右上角,再次点击“克隆”图标。将此小部件命名为“发送和转换—区域”,克隆它,然后将其添加到您的仪表板。

5.最后,回到你的仪表板。将新的“电子邮件活动—区域可视化”小部件拖到仪表板的顶部,这将使原来的“电子邮件活动”小部件位于底部。

这些数据传达了另一个有价值的见解:这些电子邮件活动收到的打开和点击数量很低,他们发送的每封电子邮件的转换数量也非常低。虽然这些数字每月都保持在较低水平,但它们似乎确实随着发送的电子邮件总数的增加而增加。

同样重要的是要记住,我们不需要丰富的编码知识或 Knowi 经验来做我们刚刚做的事情。这里的低使用门槛使得任何好奇的英语使用者都可以使用 Knowi 的仪表盘。

摘要

总之,我们连接到一个 Amazon Redshift 数据源,并对我们的新数据源进行查询。这将我们的查询结果存储在 Knowi 的弹性数据仓库中。然后,我们分析并可视化我们的数据,并向我们的可视化添加钻取信息,使用户能够钻取原始数据中他们希望了解更多信息的过滤部分。最后,我们使用基于搜索的分析来回答另一个问题,并将我们的答案可视化。

分析和可视化 Couchbase 数据—教程

原文:https://towardsdatascience.com/analyzing-visualizing-couchbase-data-tutorial-15e518b98881?source=collection_archive---------47-----------------------

使用 Knowi 连接到 Couchbase,运行查询,分析和可视化您的查询结果,并通过基于搜索的分析提出问题。

科琳·库兹在 Unsplash 上的照片

目录

介绍

Couchbase 是一个强大的 NoSQL 数据库,使企业能够存储和查询大量非结构化数据。Couchbase 的可伸缩性、灵活的数据模型和性能比率使其成为当今市场上最好的 NoSQL 数据库之一。

目前,大多数现有的商业智能和分析平台需要定制编码或繁琐的 ETL 过程,才能使用来自 Couchbase 的非结构化 NoSQL 数据。幸运的是, Knowi 是为数不多的提供 Couchbase 原生集成的商业智能平台之一,这使得我们的用户能够利用 Couchbase 的优势。

在本教程中,您将学习如何使用 Knowi 来分析和可视化来自 Couchbase 的数据。

连接到沙发底座

一旦你登录到你的 Knowi 试用账户,你需要按照以下步骤连接到一个 Couchbase 数据源:

  1. 前往屏幕左侧的面板,选择“数据源”
  2. 转到 NoSQL 数据源的右上角,选择 Couchbase。
  3. 选择屏幕底部的“测试连接”。
  4. 确保连接成功后,选择“保存”

作者图片

查询 Couchbase 数据

当您保存数据源时,您应该会在屏幕顶部收到一个警告,提示“Datasource Added”。配置查询。按照以下步骤开始设置您的第一个查询:

  1. 点击“查询”开始当您这样做时,您将被带到一个查询构建器,并且屏幕顶部的警告将变为“获取存储桶”这意味着 Knowi 正在从您刚刚连接到的 Couchbase 数据源中自动索引“桶”(或数据集)。这将需要几秒钟的时间来完成,所以在您等待的时候,请将您的报告命名为“Queens Bakeries”(如果报告名称没有泄露,我们将分析纽约皇后区面包店的数据。)
  2. 当您的警报变为“已检索存储桶”后,悬停在“存储桶/数据集”上。使用查询生成器部分来发现和构建报告/查询。点击工具条内部,您将看到存储在您的 Couchbase 数据库中的每个存储桶。选择“restaurant”并知道我将自动生成 N1QL 查询,该查询查询餐馆数据集的前 10,000 行中的所有列。
  3. 选择屏幕左下角的“预览”。向下滚动以查看预览。如您所见,“地址”和“成绩”列都在嵌套的 JSON 中。我们的目标是分析地址字段中的特定信息,所以我们需要扩展它;Cloud9QL,Knowi 的 SQL 风格语法,让这变得轻而易举。只需向上滚动到您最初索引您的存储桶的区域,并在“Cloud9QL Query”下,键入“select * expand(address)”并再次预览您的查询。

作者图片

这一次,您会注意到预览数据已经扩展为包括几个新列:zipcode、coord、street 和 building。更重要的是,因为新的“coord”字段包含这些餐馆的坐标,Knowi 自动将您的预览图表可视化类型转换为地理聚类/自定义地图可视化。

使用基于搜索的分析完成您的查询

我们的查询还没有完成;我们甚至还没有拯救它,更不用说回到皇后区的面包店了,这是我们最初的目标。为了做这些事情,我们将从 Knowi 的基于搜索的分析功能中获得一点帮助,该功能允许我们用简单的英语查询我们的数据,并实时接收结果。为了使用基于搜索的分析来完成您的查询,请遵循以下步骤:

  1. 前往预览数据顶部的搜索栏,键入“显示皇后区的每家面包店”如您所见,Knowi 将自动过滤数据,只包含 cuisine 等于 Bakery,borough 等于 Queens 的行。
  2. 向下滚动到预览图表。如您所见,数据现在包括了更小的餐馆子集,而且只包括皇后区内的餐馆。这是我们从一开始的目标。选择屏幕右下角的“保存并立即运行”以保存此查询。

运行查询后,Knowi 将结果作为数据集存储在其弹性数据仓库中。每次成功运行查询时,Knowi 都会这样做。

分析和可视化您的数据

除了将查询结果作为数据集存储在 Knowi 的弹性数据仓库中,Knowi 还会在您运行查询时将预览图表可视化保存为一个小部件。现在,您的第一个微件已经创建完毕,是时候通过创建一个仪表盘来放置它,并通过以下步骤进一步可视化数据集中的数据了:

  1. 前往屏幕左侧面板的顶部,选择“仪表板”选择“+”图标创建一个新仪表板,将其命名为“Queens Bakery Dashboard”,然后单击“OK”
  2. 回到屏幕左侧的面板,在“仪表板”的正下方,点击“Widgets”在这里,您将看到刚刚创建的“Queens Bakeries”小部件。将它拖到您的仪表板上进行添加。
  3. 现在,您已经将微件添加到了仪表板中,您可以进一步分析您的数据并创建新的可视化效果。单击小部件右上角的 elipses 图标,然后选择“分析”这将引导您找到驱动您的小部件的原始数据。
  4. 如果你看看屏幕左侧的栏,你会注意到在“坐标”和“成绩”旁边有一个加号图标。这意味着这些字段仍然是嵌套的 JSON 格式,因为我们在最初的查询中没有扩展它们。没关系;我们也可以在这里扩展它们。为此,请单击等级旁边的加号图标,然后单击[*]旁边的加号图标。
  5. 我们感兴趣的字段是“等级”字段。我们知道每个面包店都被给定了一个等级,我们想分析这些等级的分布,以找出每个等级的真正含义;饼状图应该可以做到这一点。为了设置您的饼图,将“等级”拖到“分组/维度:”上,然后将“等级”拖到“字段/指标:”上,并将“操作”从“无”更改为“计数”这将显示每个等级在我们的数据中出现的次数。
  6. 如你所见,我们有 A 级、B 级、C 级,还有……P 级和 Z 级?还有一些被标为“尚未分级”P 级和 Z 级是相当罕见的,我们不知道它们意味着什么,所以我们最好摆脱它们。为此,请拖动您的“分数[*]。将“指标”改为“排序依据”,并将方向改为“降序”如你所见,最常见的成绩是 A、B 和 c。因为这是我们唯一感兴趣的 3 个成绩,所以请转到“Limit:”并输入 3。这将把我们的数据限制在 3 个最常见的等级。
  7. 现在,转向“可视化”,将“可视化类型”改为“饼图”我们不希望保存和覆盖我们的地理聚类图,而是希望我们的饼图与其并排放置,因此,请前往屏幕的右上角,单击克隆图标,该图标类似于两张叠放在一起的纸。将您的小部件命名为“皇后面包店-等级分布”,然后单击“克隆”然后,点按“添加到仪表板”

作者图片

正如你从我们的饼状图中看到的,皇后区的面包店获得 A 级非常普遍,如果没有,他们几乎肯定会获得 B 级。这种等级的 C 级在这里就像在罗德学者的成绩单上一样罕见,所以我们可以得出结论,C 级可能是一个危险信号,而不是表明质量差,而不是平均或平庸的质量。这是非常有用的信息。

摘要

回顾一下,我们通过连接到 Couchbase 数据库并使用 Knowi 的基于搜索的分析功能查询我们数据库中的餐馆数据集来开始本教程,以使查询更容易。成功运行该查询将查询结果作为数据集存储在 Knowi 的弹性数据仓库中,并将预览图表作为小部件存储在 Knowi 帐户中。然后,我们创建了一个新的仪表板来存储我们的新部件,并创建了另一个部件来进一步分析我们的数据集并回答一个重要的问题。

探索 WhatsApp 数据

原文:https://towardsdatascience.com/analyzing-whatsapp-chats-with-python-20d62ce7fe2d?source=collection_archive---------16-----------------------

来自 Unsplash莫兰的照片

使用 Python 分析您的 WhatsApp 聊天数据

分析数据可能会很吸引人,很有趣,并且能够给出关于某个主题的非常有趣的见解。当这些数据与我们或我们的环境联系在一起时,分析这些数据就变得更加有趣,因为这些信息总是近在眼前,但却从来没有从分析的角度来看待过。

为此,我们通过社交网络与他人的互动是一个很好的数据来源。如今,我们每天都使用社交网络和互联网来工作或休闲。事实证明,这些方法真的很有帮助,让一些过程变得更有效率,甚至缓解了异地恋。它们影响了我们交流的方式,甚至重塑了我们的社交行为。社交网络注册,现在比以往任何时候都更需要我们如何什么交流。

在这方面, WhatsApp 已经成为最广泛使用的与他人聊天的应用之一【1】。在这篇文章中,我将尝试展示我们如何使用 python 从 WhatsApp 聊天中轻松提取和分析数据。

概述

这篇文章的概要如下

  • 从手机中导出 WhatsApp 聊天
  • 使用 python 库 whatstk
  • 结论

从手机中导出 WhatsApp 聊天

从手机导出聊天内容的过程相当简单。但是,它会根据您的移动操作系统而变化(参见下面的剪辑)。

导出聊天时,确保选择无媒体的选项。一旦生成,您可以通过邮件发送给自己,并下载/保存到您的计算机上。

Android 上,可能会导出几个文件。我们只对文本文件感兴趣(即txt扩展名文件)。在 iOS 上,聊天被导出为zip。一旦你把它下载到你的电脑上,解压它以获得txt

左: Android 9,WhatsApp 2.20.123(自带源码,录屏);右:iOS 12,WhatsApp 2.20.31(自带源码,好友 塞尔吉的 )

使用 python 库 whatstk

whatstk 是一个 GPLv3 许可的 python 库,它是我帮助开发的,提供了探索、分析和可视化 WhatsApp 聊天的工具。它支持多种聊天格式,并分别使用 pandasplotly 来处理和可视化数据。

写这篇帖子的时候,最后一个稳定版本是 0.4.1 。要安装它,只需使用画中画。

$ pip install whatstk

要求至少 Python 3.7。

加载聊天

我们将从使用类WhatsAppChat及其方法from_source将聊天加载到 python 开始。对于这个演示,我将使用我使用库工具随机生成的聊天。以下是摘录:

[2019-04-16 02:09] +1 123 456 789: Et labore proident laboris do labore ex. 
[2019-04-16 03:01] Mary: Reprehenderit id aute consectetur aliquip nostrud culpa, fugiat ex deserunt magna, nostrud officia id aliquip in fugiat. 🇩🇰
[2019-04-17 12:56] John: Amet magna officia ullamco pariatur ipsum cupidatat, laborum sint nostrud adipiscing sit. ✈
[2019-04-17 13:30] Mary: Cillum aute et cupidatat ipsum, occaecat lorem sint tempor ullamco elit. 🏊🏻
[2019-04-17 15:09] John: Eiusmod irure laboris dolore anim, velit velit proident qui commodo. 
[2019-04-17 17:55] Mary: Aute sed quis deserunt, veniam non veniam qui ipsum velit, aliqua sunt eu incididunt incididunt duis. 🤨

在您的情况下,更改参数filepath的值,使其指向您导出的聊天。聊天被自动解析,加载为数据帧并存储为类属性df

注意:如果您无法以这种方式加载您的聊天,您可能需要使用 hformat 输入参数手动指定您的聊天标题的格式。对于我们的聊天,应该是这样的:

首次数据概述

首先,我们将获得一些关于聊天活动的基本信息。

我们可以获得第一个和最后一个发送消息的时间戳,这给了我们一个初始的时间上下文。接下来,我们获取每个用户发送的消息数量,以及聊天活动期间每天发送的消息量。为了完成这个,我们利用了 pandas 提供的工具,特别是它的 groupby 方法。

我们注意到记录的聊天开始于 2019 年 4 月 16 日,结束于 2020 年 6 月 13 日。此外,朱塞佩和 2019 年 7 月 1 日似乎相当活跃!

谁说的最多?

现在,让我们仔细看看每个用户发送的消息数量。为此,我们使用类FigureBuilder,它简化了生成和显示基于绘图的可视化的过程。

我们使用累积计数,这意味着对于给定的一天 D ,我们对之前每个用户的所有干预进行计数。

虽然一个用户发送了很多消息,但这并不一定意味着他们发送了更多的字符。为了找出谁发送了最多的字符,我们可以使用函数的参数msg_len=False

用户消息长度

我们现在将看看每个用户发送的消息的长度,因为一些用户倾向于发送更少但更长的消息,而另一些用户发送几条短消息(那就是我)。

Mary 似乎有更高的中位数,这可能表明与聊天中的其他用户相比,他们倾向于发送更长的消息。使用平均值可能会产生误导,因为它会受到离群值(即长消息)的严重影响

用户交互

最后,最后一幅图展示了用户之间是如何互动的。具体来说,它显示了一个矩阵,该矩阵计算了从用户 A 发送到用户b的响应数量。库做出假设消息 n 总是对前一消息 n-1 的响应。虽然这可能与事实相去甚远,但它仍然设法捕捉到了某种现实。

在这个随机生成的聊天中,我们观察到最大的消息流是从 Giuseppe 到+1 123 456 789。

结论

在这篇文章中,我们看到了如何用 python 加载 WhatsApp 聊天作为数据帧,并使用 plotly 可视化相关见解。可视化效果是使用工具生成的,但是,一旦聊天被加载,可能性是无限的。

我个人觉得这真的很有趣,因为每次我探索和分析 WhatsApp 聊天时,我最终都会使用一个独特的、动态的、可访问的数据集。

你可以在这里查看这篇文章的代码。

变更日志

  • 2020 年 7 月 30 日:文章发表。whatstk版本 v0.3.1
  • 2021 年 5 月 27 日:修复断开的链接。whatstk版本 0.4.1

参考

[1] Birgit Bucher, WhatsApp,微信和 Facebook Messenger 应用——全球 Messenger 使用、渗透和统计,Messenger People 2020,博客文章

分析#WhenTrumpIsOutOfOffice 推文

原文:https://towardsdatascience.com/analyzing-whentrumpisoutofoffice-tweets-7169b3e5ca35?source=collection_archive---------45-----------------------

R 中清理和分析 tweets 的分步指南

达伦·霍尔斯特德Unsplash 上拍摄的照片

随着美国下届总统大选的临近,我想知道人们对被提名人的看法。现任总统会继续留在白宫,还是我们会看到一位不那么愤怒的推特咆哮的新美国总统?

获取数据集

我用 R 包rtweet下载了标签为# WhenTrumpIsOutOfOffice 在 2020 年 3 月发的推文。结果,我找到了 6000 多条带有标签的推文。

library(rtweet)**# create token named "twitter_token"**
twitter_token <- create_token(
  app = appname,
  consumer_key = consumer_key,
  consumer_secret = consumer_secret,
  access_token = access_token,
  access_secret = access_secret)**#download tweets into csv files**
tweets <- search_tweets(
  "#WhenTrumpIsOutOfOffice", n = 18000, include_rts = FALSE)df <- apply(tweets,2,as.character)
write.csv(df,"csv file path" )**#read the csv file**
text <- read.csv("csv file path", stringsAsFactors = FALSE)

清理数据集

像任何其他数据科学项目一样,数据清理是必不可少的。在本文中,我从 tweet 文本中删除了 URL、标签和常用的停用词。

**#data pre-processing**
remove_reg <- "&amp;|&lt;|&gt;"
text <- text %>%
  mutate(text = str_remove_all(text, remove_reg)) %>%
  mutate(text = tolower(text)) %>%
  mutate(text = str_replace_all(text, regex("@\\w+"),"" )) %>%
  mutate(text = str_replace_all(text, regex("http\\w+"),"" )) %>%
  mutate(text = str_replace_all(text, regex("://t.co/\\w+"),"" ))%>%
  mutate(text = str_replace_all(text, regex("<\\w+"),"" )) %>%
  mutate(text = str_replace_all(text, regex("/+[a-zA-Z0-9<>]+"),"")) %>%
  mutate(text = str_replace_all(text, regex("fa[a-zA-Z0-9+<>]+"),"" )) %>%
  mutate(text = str_replace_all(text, regex("#[a-zA-Z0-9<>]+"),"" ))

接下来,我使用 R 包udpipe来注释我们下载的 tweets。

library(udpipe)**#download and load the pre-trained models**
udmodel <- udpipe_download_model(language = "english")
udmodel <- udpipe_load_model(file = udmodel$file_model)**#annotate the data frame with uipipe model**
tidy_text <- udpipe_annotate(udmodel, x = text$text)
tidy_text <- as.data.frame(tidy_text)

注释完数据集后,我们可以从数据集中删除停用词。

**#Remove stop words**
my_stop_words <- tibble(
  word = c("#whentrumpisoutofoffice", "[@realdonaldtrump](http://twitter.com/realdonaldtrump)", "trump","ill"))**#Prepare stop words tibble**
all_stop_words <- stop_words %>%
  bind_rows(my_stop_words)tidy_tweets <- text %>%
  anti_join(all_stop_words, by = "word")**#To check how many words removed after stop word anti join, deleted** 
tibble(
  total_words = nrow(text),
  after_cleanup = nrow(tidy_tweets))

数据清理前后的字数

数据分析

在本文中,您将发现四种不同的文本挖掘技术应用于推文:

1\. Most frequent words in the tweets
2\. Keyword extraction
3\. Sentiment analysis
4\. Word association network graphs

转发次数最多的文本

两条推文的转发量超过了一千条:

当特朗普下台时,这些无法无天、腐败、肆无忌惮的白痴将和他一起消失。 — 1216 转推

我会不由自主地被投票让糟糕的政客下台所吸引。我刚刚开始投票;它就像一块磁铁。投票吧。我都不等了。当它是民主的时候,他们让你做它。# when trumpisoutofoffice—1101 转发

单字和双字的词频

流行的文本挖掘技术之一是找出一个单词在一个或多个文档中出现的频率。在这篇文章中,我们将看看单字和双字的词频。虽然单个词让我们对文档中最常用的词有所了解,但双词通常能给读者更好的洞察力。

从下面的条形图中,我们可以做出一些假设:

  • 特朗普卸任时,将是 2025 年 1 月
  • 当特朗普不在办公室时,人们会去参加派对(“派对”在推文中出现了 165 次)

词频条形图

**#Unigram bar chart**
tidy_tweets %>%
  count(word, sort = TRUE) %>%
  mutate(word = reorder(word, n)) %>%
  filter(n > 150) %>%
  ggplot(aes(word, n)) +
  geom_col(fill = "red") +
  xlab(NULL) +
  coord_flip() +
  ggtitle("#WhenTrumpIsOutOfOffice - 1-Word Frequency") +
  geom_text(aes(x = word, label = n), vjust = 0, hjust = -0.3, size = 4)**#Bigram bar chart (showing codes for the bar chart only)** ...
bigrams_united %>%
  count(bigram, sort = TRUE) %>%
  mutate(bigram = reorder(bigram, n)) %>%
  filter(n > 20) %>%
  ggplot(aes(bigram, n)) +
  geom_col(fill = "blue") +
  xlab(NULL) +
  coord_flip() +
  ggtitle("#WhenTrumpIsOutOfOffice - 2-Word Frequency") +
  geom_text(aes(x = bigram, label = n), vjust = 0, hjust = -0.3, size = 4)

除了找出词频,我们还可以使用预先构建的算法/包快速提取关键词。在我们的例子中,我使用 RAKE 方法(快速自动关键字提取)提取关键字,该方法在 udpipe R 包中可用。

根据下面的 RAKE 关键字条形图,我们可以得出以下结论:

  • 得分最高的两个关键词是“白宫”和“新总统”。人们担心特朗普是否会继续他第二个任期,或者被新总统取代。
  • 与词频的结果类似,人们计划在特朗普离任时举办最大的派对
  • 有趣的是,“更好的地方”也在 RAKE 关键字列表的顶部

RAKE 方法确定的关键字条形图

**#Keywords identified by RAKE method bar chart**
stats <- keywords_rake(x = tidy_text, term = "lemma", group = "doc_id", 
                       relevant = tidy_text$upos %in% c("NOUN", "ADJ"))
stats$key <- factor(stats$keyword, levels = rev(stats$keyword))stats %>%
  **#filter data**
  filter(freq > 10 & ngram > 1) %>%
  **# set value for x, y, and fill**
  ggplot(aes(x = reorder(keyword, rake), y =  rake  )) +
  **# show in bars**
  geom_col(fill = "red") +
  **# flip the bars to be horizontal**
  coord_flip() +
  **# show value labe**l
  geom_text(aes(label = round(rake, digits = 2), vjust = 0, hjust = -0.3 )) +
  **# change y-axis name**
  xlab("keywords")+
  **# add title**
  ggtitle("Keywords identified by RAKE method") +
  **# hide legend**
  theme(legend.position = "none")

另一个有趣的文本挖掘技术是给每个单词一个语法标记,称为词性标记(POS)。在这种情况下,我们指定从 tweets 中提取名词短语。

POS 标记条形图的一些要点:

  • 当特朗普不在办公室时,与乔·拜登相比,迈克·彭斯/彭斯总统是被频繁提及的短语
  • 当特朗普离开办公室时,人们松了一口气

由词类标签识别的关键字—简单名词短语条形图

**#Keywords identified by POS tags - Simple noun phrases bar chart**
tidy_text$phrase_tag <- as_phrasemachine(tidy_text$upos, type = "upos")
stats <- keywords_phrases(x = tidy_text$phrase_tag, term = tolower(tidy_text$token), 
                          pattern =  "(A|N)*N(P+D*(A|N)*N)*", 
                          is_regex = TRUE, detailed = FALSE)
stats <- subset(stats, ngram > 1 & freq > 100)
stats$key <- factor(stats$keyword, levels = rev(stats$keyword))stats %>%
 **#data**
  #filter(freq > 100 & ngram > 1  ) %>%
 **# set value for x, y, and fill**
  ggplot(aes(x = reorder(keyword, freq), y =  freq  )) +
 **# show in bars**
  geom_col(fill = "red") +
 **# flip the bars to be horizontal**
  coord_flip() +
 **# show value label**
  geom_text(aes(label = freq, vjust = 0, hjust = -0.3 )) +
 **# change y-axis name**
  xlab("keywords")+
 **# add title**
  ggtitle("Keywords identified by POS tags - Simple noun phrases") +
 **# hide legend**
  theme(legend.position = "none")

情感分析

情感分析是自然语言处理(NLP)中的一个领域,它试图识别文本数据中的情感。舆情分析可以让我们快速了解公众对特定话题或个人的情绪。因此,Twitter 是文本挖掘的绝佳数据源,在那里你可以找到大量公开表达的观点。

#WhenTrumpIsOutOfOffice 的总体喜好度

使用“NRC”词典,我们可以将单词标记为 8 种基本情绪(信任、期待、恐惧等)。)和两种情绪(正面和负面)。

在我们的例子中,我们看到推文中正面和负面词汇的分布几乎相等。请注意“信任”在其他情绪中所占的比例最高,而“惊讶”所占的比例最少。在下一节中,我们将详细了解与情感和情绪相关的词汇。

情感排名列表

**#Sentiment ranking list**
nrc_words <- tidy_tweets %>%
  inner_join(get_sentiments("nrc"), by = "word")**#Rank of sentiments**
sentiments_rank <- nrc_words %>%
  group_by(sentiment) %>%
  tally %>%
  arrange(desc(n))**#Find percentage**
sentiments_rank %>%
  mutate(percent = (n/8169)*100)

我们也可以用饼状图的形式来显示情感排名:

按情感分类的词频饼图

**#Word frequency pie chart categorized by sentiments**
sentiments_rank_clean <- sentiments_rank %>%
  filter(sentiment != "positive") %>%
  filter(sentiment != "negative")**# Create bar chart first**
bp<- ggplot(sentiments_rank_clean, aes(x=reorder(sentiment, -n), y=n, fill=sentiment))+
  geom_bar(width = 1, stat = "identity")**#Turn bar chart into a pie chart** pie <- bp + coord_polar("x", start=0) +
  ggtitle("#WhenTrumpIsOutOfOffice - Sentiment pie chart") +
  xlab("Word frequency - Sentiment")

基于情感的词频

让我们看看出现在“信任”类别中的单词,它的词频最高。请注意,“总统”这个词出现了 400 多次。

其他有趣的见解:

  • “地狱”一词是愤怒、恐惧和悲伤情绪类别的首选。
  • “终于”这个词是另一个排在厌恶、喜悦和惊讶榜首的词。
  • 如果你仔细看看负面情绪下面的单词,你会发现大多数单词都与仇恨、监狱等有关。而不是与悲伤或失望相关的词语。

基于情绪的词频

**#Word frequencies based on emotions**
nrc_words %>%
 **# Count by word and sentiment**
  count(word, sentiment) %>%
 **# Group by sentiment**
  group_by(sentiment) %>%
 **# Take the top 10 words for each sentiment**
  top_n(10) %>%
  ungroup() %>%
  mutate(word = reorder(word, n)) %>%
 **# Set up the plot with aes()**
  ggplot(aes(word, n, fill = sentiment)) +
  geom_col(show.legend = FALSE) +
  facet_wrap(~ sentiment, scales = "free") +
  coord_flip() +
  ggtitle("Word frequency based on emotion")

阳性词云

我们还可以提取与积极和消极情绪相关的单词,并将它们绘制成单词云。

标签# WhenTrumpIsOutOfOffice 的肯定性词云

**#Positivity word cloud for the hashtag: #WhenTrumpIsOutOfOffice**
positivity_wordcloud <- tidy_tweets %>%
  inner_join(get_sentiments("bing"), by = c("word" = "word")) %>%
  count(word, sentiment, sort = TRUE) %>%
  acast(word ~ sentiment, value.var = "n", fill = 0) %>%
  comparison.cloud(colors = c( "#A32D39","#177255"),  title.size = 1.8,
                   max.words = 50,scale=c(2,0.7))

#WhenTrumpIsOutOfOffice 的词关联图

最后,我们还可以看到一条 tweet 和整个 tweet 文档中的单词是如何相互关联的。

WhenTrumpIsOutOfOffice 的词联想网络图

左边的网络图标题为“句内共现”,显示了在一个句子中经常一起使用的短语。在网络图中,节点代表年度字母中使用的单词,边(链接)的阴影反映每个单词之间的紧密程度。如果术语关系密切,边缘的色调就越暗。两对单词在图表中脱颖而出,这是“长期”和“利润少”的单词组合。

至于右边的网络图,该图向我们显示了在一个文档中彼此“相邻”的单词。在这个图表中,我们可以看到“Pence”和“female/ woman”与单词“president”密切相关

**#Cooccurrences within sentence network graph**
**#how many times nouns and adjectives are used in the same sentence**
cooc <- cooccurrence(x = subset(tidy_text, upos %in% c("NOUN", "ADJ")), 
                     term = "lemma", 
                     group = c("doc_id", "paragraph_id", "sentence_id"))library(igraph)
library(ggraph)
library(ggplot2)wordnetwork <- head(cooc, 50)
wordnetwork <- graph_from_data_frame(wordnetwork)
ggraph(wordnetwork, layout = "fr") +
  geom_edge_link(aes(edge_alpha = cooc), edge_colour = "red") +
  geom_node_point(color = "red", size = 1) +
  geom_node_text(aes(label = name), col = "blue", size = 5, repel = TRUE) +
  theme_graph(base_family = "Arial") +
  theme(legend.position = "none") +
  labs(title = "Cooccurrences within sentence", subtitle = "Nouns & Adjective")**#Words following one another network graph**
cooc <- cooccurrence(tidy_text$lemma, relevant = tidy_text$upos %in% c("NOUN", "ADJ"), skipgram = 1)
dev.off()
head(cooc)
wordnetwork <- head(cooc, 50)
wordnetwork <- graph_from_data_frame(wordnetwork)
ggraph(wordnetwork, layout = "fr") +
  geom_edge_link(aes( edge_alpha = cooc),edge_colour = "blue") +
  geom_node_point(color = "red", size = 1) +
  geom_node_text(aes(label = name), col = "red", size = 5,  repel = TRUE) +
  theme_graph(base_family = "Arial") +
  labs(title = "Words following one another", subtitle = "Nouns & Adjective") +
  theme(legend.position = "none")

结论

总的来说,我们已经导出了几个关键词,从整体上探索了单词与#WhenTrumpIsOutOfOffice 的关系,并能够做出以下假设:

  • 人们担心特朗普卸任后,谁将成为下一任美国总统。总统会继续他的第二个任期,还是他的副总统会取代他?或者我们最终会看到这个国家的第一位女总统吗?
  • 总的来说,积极情绪与解脱和庆祝有关,而消极情绪与伤害和“坐牢”有关。

用 Python 分析世界股票指数的表现

原文:https://towardsdatascience.com/analyzing-world-stock-indices-performance-in-python-610df6a578f?source=collection_archive---------8-----------------------

马库斯·斯皮斯克在 Unsplash 上的照片

来自《走向数据科学》编辑的提示: 虽然我们允许独立作者根据我们的 规则和指导方针 发表文章,但我们并不认可每个作者的贡献。你不应该在没有寻求专业建议的情况下依赖一个作者的作品。详见我们的 读者术语

股票指数是选定公司的列表,其平均价格(或加权平均)反映了股票市场。股票指数也可以反映它们所涵盖的某个行业或地区。它们也经常被称为衡量基金业绩的基准。与当前价格相比,股票指数更重要的部分是某个确定时刻的表现或价格变化,无论是前一天还是前三个月。

本文的后半部分将介绍如何用 Python 评估股票指数的表现。

如何获取数据

与股票指数相关的数据可以从 Yahoo!金融。在 Python 中,有一个流行的模块可以更容易地从 Yahoo!金融。如果它还没有安装,您可以通过输入这段代码,然后导入所需的库/模块进行处理,将它安装到您的 Jupyter 笔记本中。

那么,我们如何获取股票数据呢?因为这篇文章是分析世界主要指数的表现,下一步要做的是从 Yahoo!财经网站。

现在,这是从网站上摘下的列表的标题/开头内容:

我们可以看到,对于每个股票指数,每行都有名称和符号。这些符号对于检索数据非常重要。现在,是时候为这些股票指数收集历史数据了。

为了收集每个股票指数的历史数据,首先,我们需要用 symbol 的参数调用 Ticker 模块。例如,如果我们想获得标准普尔 500 的历史数据,我们可以键入:

我们还需要定义数据检索的周期。应该获取日数据还是周数据?必须在“周期”参数中输入。还必须定义开始和结束日期。

在这里,将从 2020 年 1 月 1 日开始每天(“1d”)检索数据,直到 2020 年 9 月 30 日。参数中指定的结束日期将不包括在检索到的数据中(因此,只检索到前一天)

S&P 历史股票数据—左:数据的头/开始,右:数据的尾/结束

现在是时候收集所有股指的历史数据了。

获取股票指数的历史数据

世界主要股票指数历史数据—左:数据的开头/开头,右:数据的结尾/结尾

一些预处理和数据分析

在前面的部分中,我们已经成功收集了从 2010 年 1 月到 2020 年 9 月 30 日的世界主要股票指数的历史股票数据。现在,是时候为分析做一些预处理了。

我想做的第一步是按地区对每个指数进行分类。以下是我对该地区的定义:

区域索引

现在,为该区域创建一个新列。

def getRegion(ticker):
    for k in region_idx.keys():
        if ticker in region_idx[k]:
            return k
msi['region']= msi.ticker.apply(lambda x: getRegion(x))

自 2010 年初以来的价格变化

由于股票价格是以不同的货币表示的,因此要想全面了解业绩,最好以相同的单位或指标来查看。现在,让我们来看看这些指数自 2010 年 1 月 4 日以来十年间的价格变化。变化将以百分比(%)表示。价格变化使用当天的收盘价(“收盘”)。

在上面的代码块中,定义了一个新函数来计算价格变化。然后,对于主数据集中的每一行,调用该函数来输出价格变化。

现在,为了简化流程,让我们将数据集转换为 tickers 作为列,将行作为每个日期的价格变化。

现在,价格变化的新数据框架将如下所示:

每个股票指数的价格变动至 2010 年 1 月 4 日

是时候绘制这些价格变化了。对于这一部分,地块将在区域中。

基于 2010 年 1 月 4 日的价格变化

从上面的图中可以看出,自 2016 年以来,美国的股票指数增长迅速。自 2010 年以来,纳斯达克股票价格已经上涨了 5 倍。其他美国股票指数,如标准普尔 500、罗素 2000 指数和道琼斯 30 指数也上涨了股价,但表现不如纳斯达克。

其他地区的一些指数价格也上涨了 1-2.5 倍。日经 225 指数(日本)、S&P/新西兰 50 指数(新西兰)、S&P BSE SENSEX 指数(印度)和德国 DAX 指数(德国)是各自地区指数中最赚钱的股票指数(如果你自 2010 年 1 月以来以某种方式投资了它们或一些反映它们的基金)。

我们也不能不提所有这些股票指数都受到了新冠肺炎的影响。它们都在 2020 年初左右显著下降,但总体而言,大多数都在此后不断攀升。

短期至长期价格变化/回报

当我们谈论股票时,通常我们会将当前价格与前几个月或前几年的历史价格进行比较。对于这一部分,我们将分析短期到长期的价格变化。

为了清楚和详细,我们比较的是同一天的数据,而不是之前的数据。所以,2020 年 9 月 30 日的 6M(六个月)是指 2020 年 9 月 30 日前 6 个月的那一天,也就是 2020 年 3 月 30 日。同一天的 1Y(一年)表示 2019 年 9 月 30 日,以此类推。

首先,我们将数据集划分到 2020 年 9 月 30 日。

现在,要得到历史价格,有点棘手。我们想要引用的日期(过去的日期)可能不是工作日。除此之外,每个国家的工作日也不尽相同。因此,为了解决这个问题,我们选择过去日期之前的最后一个工作日。

现在,让我们来看看之前一些决定性时刻的价格变化。

让我们画出这些股票指数的价格变化/回报。

世界主要股票指数的短期至长期回报/价格变化

参照上面的图表,所有主要股票指数在 6 个月的时间里都表现良好(价格上涨)。另一方面,在其他时期,这些指数的表现参差不齐。10 年来,大多数股票指数都是盈利的,除了 STI 指数(但只是略有下降)。

总之,参考相同的图,一般来说,最持续盈利的股票指数是:

  • 标准普尔 500(美国)
  • 道琼斯 300 指数(美国)
  • 纳斯达克(美国)
  • 日经 225(日本)
  • 深证成分指数(中国)
  • S&P/新西兰 50 总指数(新西兰)
  • TSEC 加权指数(台湾)

请检查以下链接中的代码以供进一步参考:

[## 库存/库存指数分析

此时您不能执行该操作。您已使用另一个标签页或窗口登录。您已在另一个选项卡中注销,或者…

github.com](https://github.com/intandeay/StockIndicesAnalysis)

用 Python 和 Foursquare API 分析全球美食

原文:https://towardsdatascience.com/analyzing-worldwide-cuisines-with-python-and-foursquare-api-e63455c14246?source=collection_archive---------34-----------------------

分析世界上最大的城市最受欢迎的美食

照片由瑞秋·帕克Unsplash

世界上有很多著名的菜系,像中国菜、日本菜、墨西哥菜、西班牙菜……很明显,墨西哥是品尝墨西哥菜的最佳地点,泰国是品尝泰国菜的最佳地点,等等,但是去这么多国家品尝每一种不同的菜系是非常乏味的。然而,在世界上的每一个大城市,你都可以找到许多国际餐馆,在那里你可以尝试新的美食。但是体验会足够好吗?

今天,我将与你分享我最近参加的一个数据科学课程的最终项目,在那里我创建了一个 Jupyter 笔记本,用于使用 Foursquare API 探索纽约、东京、香港、悉尼、巴塞罗那、巴黎、莫斯科、布宜诺斯艾利斯和墨西哥 DF 的城市,并猜猜哪个城市最适合尝试每种美食

定义数据

对于这个项目,将使用 Foursquare API 。作为用户,您需要一个免费的开发人员帐户,以便获得认证您的 HTTP 请求的凭证。对于上面提到的每个城市,我们将一个接一个地为每种国际美食的餐馆请求 API,并将结果存储在一个Pandasdata framedata frame中,以便稍后处理它们并执行一些有趣的分析。

  • 待探索城市 : 纽约、东京、香港、悉尼、巴塞罗那、巴黎、莫斯科、布宜诺斯艾利斯、墨西哥 DF
  • 要查询的美食(列表摘自 Foursquare API 文档 ): 阿富汗、非洲、美国、缅甸、柬埔寨、中国、菲律宾、喜马拉雅、印度尼西亚、日本、韩国、马来、蒙古、泰国、西藏、越南、澳大利亚、奥地利、孟加拉、比利时、加勒比海、高加索、捷克、荷兰、白俄罗斯、波斯尼亚、保加利亚、罗马尼亚、鞑靼、英语、法语、德语、希腊、夏威夷、匈牙利、印度、意大利、萨尔瓦多、阿根廷、巴西、巴西

在开始使用 API 之前,需要提取一些信息,比如每种美食的categoryId,或者每个探索城市的当地美食。首先,场馆类别的完整列表可在网站 https://developer . four square . com/docs/build-with four square/categories/获得。从这里,我们将手动提取food下对应于国家美食的类别(西班牙、印度、泰国)...),以便能够只通过国籍来过滤餐馆,而不是其他不相关的标准,如素食主义者、清真、汉堡店...

对于上面提到的每个城市,我们将询问每个菜系的餐馆的 API:https://api.foursquare.com/v2/venues/explore?&near={city}&categoryId={category_id}。对于每个城市和菜系,餐馆的数量(['response']['totalResults'])将保存在一个 熊猫数据框 中。

所以直到现在,我们的笔记本看起来像这样:

检索数据

我们已经知道我们想要获得什么数据,以及在哪里和如何获得它。然后,下一步是编写一个循环,向 Foursquare API 询问每个城市中每种定义的美食有多少家餐馆,并将其存储到 Pandas 数据帧中。

转换数据

在生成的数据帧中,每一行将代表特定菜系和城市的餐馆数量,因此我们将有 NxM 行,其中 N 是定义的菜系数量, M 是城市数量。

下载数据的数据帧

这种格式不适合我们的需要,所以我们不得不将它转换成一个新的数据框架,其中每行代表一个城市,每列代表该城市中每种菜肴的餐馆百分比。转换将包括执行一次性编码,按城市对结果进行分组,最后按行对结果进行规范化。

转换后的数据帧

分析数据

现在,数据已经准备好进行分析,让我们定义四个洞察,并看看如何获得它们并在图中可视化它们:

  • 每个城市的十大美食
  • 各种美食的热门城市
  • 各城市本地美食的受欢迎程度
  • 世界上最受欢迎的美食

在展示结果之前,我想先观察一下。在这个项目中,我们将与 Foursquare 中的 65 种不同类型的美食合作。由于此处显示的结果是百分比(超过 100),在一个公平的划分中,1.54%对应于每种菜肴。我认为这是一个重要的澄清,因为如果没有它,某些城市的某些菜肴 20%的百分比可能看起来不算多,但实际上这是一个非常高的百分比。

每个城市的十大美食

对于每个城市,我们将绘制一个条形图,显示 10 种最受欢迎的美食,按照该城市餐馆的百分比排序。每个城市的当地美食将用红色标出。

每个城市十大美食的评选结果

在大多数城市,当地美食是赢家。然而,在一些地方,比如巴塞罗那或布宜诺斯艾利斯,当地美食和第二受欢迎的美食之间的差异比在巴黎或纽约要大得多。

在一些城市,我们可以看到最受欢迎的美食是如何来自邻近国家的:例如,在墨西哥,美国餐馆非常受欢迎;在香港,泰国菜和越南菜似乎也很受欢迎,或者在悉尼,来自中国、日本、越南或韩国等东亚国家的食物非常有名。然而,纽约和巴塞罗那的情况不同,纽约第一名和第十名之间的差距不到 3%,巴塞罗那排在西班牙菜和意大利菜之后,接下来的三种最受欢迎的美食是日本菜、中国菜和墨西哥菜,这些国家离西欧很远。

各种美食的热门城市

或许你可以在东京找到最好的寿司或者在巴塞罗那找到最好的玉米饼,但是看看上面的结果,看起来本地美食并不总是最受欢迎的。现在出现的问题和之前正好相反,每种美食排名靠前的城市是哪些。在这里,我们将为所分析城市的每种当地美食绘制一个条形图,对于每种美食,我们将看到在哪里可以找到该美食的更多餐厅。

每种美食最受欢迎城市的结果

显而易见,从分析的城市来看,拥有更多美式餐厅的城市是纽约、日本、东京等等。然而,令人惊讶的是,它并不总是这样。

  • 中国、澳大利亚、西班牙、俄罗斯、阿根廷和墨西哥菜肴在各自的城市比在世界其他地方更受欢迎。澳大利亚人和俄罗斯人的差异尤其大。
  • 美国、日本和法国的菜肴在一些外国城市更受欢迎,比如墨西哥城或布宜诺斯艾利斯,而不是在他们国家的首都。

每个城市当地美食的受欢迎程度

正如我们之前看到的,在一些城市,当地美食并不是最受欢迎的。在这里,我们将检查每个被分析城市的当地美食有多受欢迎。同样,通过条形图,我们可以看到每个城市本地美食餐厅的百分比。

当地美食在每个城市的受欢迎程度

从这个情节来看,似乎很明显,在巴塞罗那,布宜诺斯艾利斯,墨西哥 DF,或者香港,当地的美食真的很受欢迎,而在纽约或悉尼这样的城市,美食种类更丰富。

世界上最受欢迎的美食

前面的观察结果是否意味着西班牙、阿根廷、中国和墨西哥菜肴是世界上最受喜爱和最受欢迎的?在最后一部分,我们将在条形图中绘制每种美食的平均百分比,以检查全球前 10 大美食(基于 9 个分析的城市)。

全球各种美食的平均百分比结果

看起来世界上最受欢迎的菜系是中国、日本、法国、意大利和西班牙。我们看到香港的中国菜或巴塞罗那的西班牙菜非常受欢迎,而在巴黎或东京,当地菜肴的受欢迎程度与外国菜肴相当接近。

排名第一(中国,8%)和第十(越南,4%)的最受欢迎的菜肴之间没有太大的区别。从这前 10 名中,中国、日本、法国、西班牙、美国和墨西哥是一些被分析城市的地方美食。当我们分析每个城市最受欢迎的美食时,我们可以看到,对于巴塞罗那(西班牙)、中国香港、法国巴黎、美国纽约和墨西哥 DF,最受欢迎的美食是当地美食,而对于东京,日本料理排在第二位,非常接近第一位(意大利)。

意大利美食似乎很受欢迎,但我们没有分析任何一个意大利城市,所以我们无法判断它在国内是否也如此受欢迎。

未来建议

这个项目的结果非常有趣,尤其是对那些对旅游和美食感兴趣的人来说。然而,为了做更深入的研究,应该探索更多的城市。将一些意大利城市,如罗马、费伦泽或那不勒斯,以及伊斯坦布尔、曼谷和河内添加到列表中会非常有趣,因为意大利、土耳其、泰国和越南美食非常受欢迎,这样就有可能检查它们在各自的城市是否也受欢迎。

我们绝对应该包括一座意大利城市——照片由里卡多·戈麦斯·安吉尔拍摄

对于顶级美食,根据这个项目的结果是中国、日本、法国、意大利和西班牙,这将是一个好主意包括每个国家的更多城市。例如,在西班牙,巴塞罗那代表了该国 11%的人口,因此添加像马德里、巴伦西亚、毕尔巴鄂、塞维利亚或圣地亚哥德孔波斯特拉这样的城市不仅会提供该国更好的样本,而且还包括不同种类的西班牙美食,如加泰罗尼亚、巴斯克或加利西亚美食。

另一个建议是考虑餐馆的评级,因为这里所有的结果来自于场地的数量,而不是它们的质量。Foursquare API 提供了对场馆评级的访问,即使这会使 API 查询循环慢很多。

当然,我的 GitHub 库里有完整的笔记本。

参考

异常检测:检测异常值的技术

原文:https://towardsdatascience.com/anamoly-detection-techniques-to-detect-outliers-fea92047a222?source=collection_archive---------31-----------------------

阿里·哈坚Unsplash 上的照片

异常检测是对罕见项目、事件或观察结果的识别,这些项目、事件或观察结果通过与大多数数据显著不同而引起怀疑。通常,异常项目会转化为某种问题,如信用卡欺诈、网络入侵、医疗诊断、系统健康监控。

异常检测基于两个基本前提

  • 数据中很少出现异常。
  • 他们的特征明显不同于正常情况。

异常检测技术

四分位数间距(IQR)

识别数据中不规则性的最简单方法是标记偏离分布的常见统计属性的数据点,包括平均值、中值、众数和四分位数。

最流行的方法之一是四分位数间距(IQR)。 IQR 是统计学中的一个概念,通过将数据集分成四分位数来衡量统计离差和数据可变性。

简而言之,任何数据集或任何一组观察值都根据数据的值以及它们与整个数据集的比较情况被划分为四个定义的区间。四分位数将数据分为三个点和四个区间。

图片来源:维基百科

四分位距(IQR)很重要,因为它用于定义异常值。它是第三个四分位数和第一个四分位数的差值(IQR = Q3 -Q1)。在这种情况下,异常值被定义为低于(Q1 1.5 倍 IQR)或高于(Q3+1.5 倍 IQR)的观测值

图片来源:维基百科

履行

np.percentile是 Python 中的烘焙功能

q75, q25 = np.percentile(x, [75 ,25]) iqr = q75 - q25

缺点

IQR 技术在以下情况下不起作用

  1. 该模式基于季节性。这涉及到更复杂的方法,例如将数据分解成多个趋势,以确定季节性的变化。

2.随着恶意对手不断调整自己,异常或正常的定义可能会频繁改变

基于聚类的异常检测

聚类是无监督学习领域中最流行的概念之一。潜在的前提是相似的数据点倾向于属于相似的组或聚类,这是由它们与局部质心的距离决定的。

K-means 是一种广泛使用的聚类算法。它创建了“k”个相似的数据点聚类。不属于这些组的数据实例可能会被标记为异常。其他聚类算法,如层次聚类和数据库扫描,也可以用来检测异常值。

K-means 算法的工作方式如下:

  1. 指定簇的数量 K
  2. 通过首先改组数据集,然后为质心随机选择 K 个数据点来初始化质心,而无需替换。
  3. 计算质心和数据点之间的距离。
  4. 继续迭代,直到质心没有变化。也就是说,数据点到聚类的分配没有改变。

履行

  1. 初始化随机质心

你从三个(我们决定 K 为 3)随机点(以(x,y)的形式)开始这个过程。这些点被称为质心,这只是一个用来表示中心的花哨名称。我们先把这三个点命名为 C1、C3 ,这样你以后就可以参考了。

K-Means 中的步骤 1:随机质心

2。计算质心和数据点之间的距离

接下来,测量数据点与这三个随机选择的点之间的距离。一个非常流行的距离测量函数的选择,在本例中,是

简而言之,如果 2D 空间上有 n 个点(如上图所示),并且它们的坐标由(x_i,y_i)表示,那么该空间上任意两点( (x1,y1)(x2,y2) )之间的欧几里德距离由下式给出:

假设 C1、C2、C3 的坐标分别为— (-1,4)(-0.2,1.5)(2,2.5) 。现在让我们写几行 Python 代码,它将计算数据点和这些随机选择的质心之间的欧几里德距离。我们从初始化质心开始。

# Initialize the centroids
c1 = (-1, 4)
c2 = (-0.2, 1.5)
c3 = (2, 2.5)

接下来,我们编写一个小的辅助函数来计算数据点和质心之间的欧几里德距离。

# A helper function to calculate the Euclidean distance between the data points and the centroidsdef calculate_distance(centroid, X, Y):
    distances = []

    # Unpack the x and y coordinates of the centroid
    c_x, c_y = centroid

    # Iterate over the data points and calculate the distance using the           # given formula
    for x, y in list(zip(X, Y)):
        root_diff_x = (x - c_x) ** 2
        root_diff_y = (y - c_y) ** 2
        distance = np.sqrt(root_diff_x + root_diff_y)
        distances.append(distance)

    return distances

我们现在可以将该函数应用于数据点,并相应地分配结果。

# Calculate the distance and assign them to the DataFrame accordingly
data['C1_Distance'] = calculate_distance(c1, data.X_value, data.Y_value)
data['C2_Distance'] = calculate_distance(c2, data.X_value, data.Y_value)
data['C3_Distance'] = calculate_distance(c3, data.X_value, data.Y_value)# Preview the data
print(data.head())

3。比较、分配、平均和重复

这基本上是 K-Means 聚类算法的最后一步。一旦你有了数据点和质心之间的距离,你就可以比较这些距离并取最小的一个。特定数据点到质心的距离最小,该质心被指定为该特定数据点的聚类。

让我们以编程的方式来做这件事。

# Get the minimum distance centroids
    data['Cluster'] = data[['C1_Distance', 'C2_Distance', 'C3_Distance']].apply(np.argmin, axis =1)

# Map the centroids accordingly and rename them
    data['Cluster'] = data['Cluster'].map({'C1_Distance': 'C1', 'C2_Distance': 'C2', 'C3_Distance': 'C3'})

# Get a preview of the data
    print(data.head(10))

现在最有趣的部分来了,*通过确定数据点坐标的平均值来更新质心*(这些数据点现在应该属于某个质心)。因此得名K-意为。平均值计算看起来是这样的:

K-Means 中的均值更新(n 表示属于一个聚类的数据点的数量)

下面几行代码可以帮您做到这一点:

# Calculate the coordinates of the new centroid from cluster 1
x_new_centroid1 = data[data['Cluster']=='C1']['X_value'].mean()
y_new_centroid1 = data[data['Cluster']=='C1']['Y_value'].mean()# Calculate the coordinates of the new centroid from cluster 2
x_new_centroid2 = data[data['Cluster']=='C3']['X_value'].mean()
y_new_centroid2 = data[data['Cluster']=='C3']['Y_value'].mean()# Print the coordinates of the new centroids
print('Centroid 1 ({}, {})'.format(x_new_centroid1, y_new_centroid1))
print('Centroid 2 ({}, {})'.format(x_new_centroid2, y_new_centroid2))

重复这个过程,直到质心的坐标不再更新。

缺点

K-means 算法是一种流行的算法,被广泛应用于图像压缩、文档分类等领域。K-mean 的目标是将数据点分组到不同的非重叠子组中。当集群具有一种球形形状时,它做得非常好。然而,当团簇的几何形状偏离球形时,它会受到影响。此外,它也不会从数据中学习聚类数,而是需要预先定义。

隔离森林

隔离森林是一种无监督学习算法,属于集成决策树家族。这种方法不同于所有以前的方法。所有以前的方法都是试图找到数据的正常区域,然后将这个定义区域之外的任何东西识别为异常值或异常值。这种方法的工作原理不同。它通过给每个数据点分配一个分数来明确隔离异常,而不是描绘和构建正常点和区域。它利用了这样一个事实,即异常是少数数据点,并且它们具有与正常情况下非常不同的属性值。

隔离森林通过随机选择一个特征,然后随机选择所选特征的最大值和最小值之间的分割值来“隔离”观察值。

由于递归分割可以用树结构表示,分离样本所需的分裂次数等于从根节点到终止节点的路径长度。

这种路径长度在这种随机树的森林中平均,是常态的度量和我们的决策函数。

随机分区会为异常产生明显更短的路径。因此,当随机树的森林共同产生特定样本的较短路径长度时,它们极有可能是异常。

这种算法在处理非常高维的数据集时非常有效,并且被证明是一种非常有效的异常检测方法。

这篇论文涵盖了隔离林如何工作的全部细节。

履行

**import** **numpy** **as** **np**
**import** **matplotlib.pyplot** **as** **plt**
**from** **sklearn.ensemble** **import** [IsolationForest](https://scikit-learn.org/stable/modules/generated/sklearn.ensemble.IsolationForest.html#sklearn.ensemble.IsolationForest)rng = np.random.RandomState(42)*# Generate train data*
X = 0.3 * rng.randn(100, 2)
X_train = [np.r_](https://docs.scipy.org/doc/numpy/reference/generated/numpy.r_.html#numpy.r_)[X + 2, X - 2]
*# Generate some regular novel observations*
X = 0.3 * rng.randn(20, 2)
X_test = [np.r_](https://docs.scipy.org/doc/numpy/reference/generated/numpy.r_.html#numpy.r_)[X + 2, X - 2]
*# Generate some abnormal novel observations*
X_outliers = rng.uniform(low=-4, high=4, size=(20, 2))*# fit the model*
clf = [IsolationForest](https://scikit-learn.org/stable/modules/generated/sklearn.ensemble.IsolationForest.html#sklearn.ensemble.IsolationForest)(max_samples=100, random_state=rng)
clf.fit(X_train)
y_pred_train = clf.predict(X_train)
y_pred_test = clf.predict(X_test)
y_pred_outliers = clf.predict(X_outliers)*# plot the line, the samples, and the nearest vectors to the plane*
xx, yy = [np.meshgrid](https://docs.scipy.org/doc/numpy/reference/generated/numpy.meshgrid.html#numpy.meshgrid)([np.linspace](https://docs.scipy.org/doc/numpy/reference/generated/numpy.linspace.html#numpy.linspace)(-5, 5, 50), [np.linspace](https://docs.scipy.org/doc/numpy/reference/generated/numpy.linspace.html#numpy.linspace)(-5, 5, 50))
Z = clf.decision_function([np.c_](https://docs.scipy.org/doc/numpy/reference/generated/numpy.c_.html#numpy.c_)[xx.ravel(), yy.ravel()])
Z = Z.reshape(xx.shape)[plt.title](https://matplotlib.org/api/_as_gen/matplotlib.pyplot.title.html#matplotlib.pyplot.title)("IsolationForest")
[plt.contourf](https://matplotlib.org/api/_as_gen/matplotlib.pyplot.contourf.html#matplotlib.pyplot.contourf)(xx, yy, Z, cmap=plt.cm.Blues_r)b1 = [plt.scatter](https://matplotlib.org/api/_as_gen/matplotlib.pyplot.scatter.html#matplotlib.pyplot.scatter)(X_train[:, 0], X_train[:, 1], c='white',
                 s=20, edgecolor='k')
b2 = [plt.scatter](https://matplotlib.org/api/_as_gen/matplotlib.pyplot.scatter.html#matplotlib.pyplot.scatter)(X_test[:, 0], X_test[:, 1], c='green',
                 s=20, edgecolor='k')
c = [plt.scatter](https://matplotlib.org/api/_as_gen/matplotlib.pyplot.scatter.html#matplotlib.pyplot.scatter)(X_outliers[:, 0], X_outliers[:, 1], c='red',
                s=20, edgecolor='k')
[plt.axis](https://matplotlib.org/api/_as_gen/matplotlib.pyplot.axis.html#matplotlib.pyplot.axis)('tight')
[plt.xlim](https://matplotlib.org/api/_as_gen/matplotlib.pyplot.xlim.html#matplotlib.pyplot.xlim)((-5, 5))
[plt.ylim](https://matplotlib.org/api/_as_gen/matplotlib.pyplot.ylim.html#matplotlib.pyplot.ylim)((-5, 5))
[plt.legend](https://matplotlib.org/api/_as_gen/matplotlib.pyplot.legend.html#matplotlib.pyplot.legend)([b1, b2, c],
           ["training observations",
            "new regular observations", "new abnormal observations"],
           loc="upper left")
[plt.show](https://matplotlib.org/api/_as_gen/matplotlib.pyplot.show.html#matplotlib.pyplot.show)()

资料来源:scikit-learn.org

结论

这篇文章概述了检测数据异常的不同技术。它的范围从使用简单的统计方法,如标准差,到无监督的学习算法,如隔离森林。每种方法都有其优点和缺点。例如,四分位间距(IQR)不适用于季节模式,K 均值聚类擅长将数据分组到不同的非重叠子组中。当集群具有一种球形形状时,它做得非常好。隔离森林提供了一种反向方法来检测异常。它利用了这样一个事实,即异常是少数数据点,并且它们具有与正常情况下非常不同的属性值。作为一个好的实践者,了解算法/方法背后的假设是很好的,这样你就会对每种方法的优缺点有一个很好的想法。这将有助于您决定何时以及在何种情况下使用每种方法。

起锚!更多你希望知道的 Python 正则表达式概念

原文:https://towardsdatascience.com/anchors-away-more-python-regular-expressions-you-wish-you-knew-8a7780ac54e9?source=collection_archive---------32-----------------------

教程| Python |正则表达式(Regex)

使用 Python 中的高级正则表达式工具处理文本的秘密

作者图片

所以你已经知道了 Python 中正则表达式的基础。像如何使用字符集、元字符、量词和捕获组这样的东西是基本的构建模块,但是你是一个超级用户,永远不会满足于仅仅是基础。你的文本争论问题比你希望用这些工具解决的要复杂得多。幸运的是,Python 中有更多的正则表达式概念需要学习。这是更多文本辩论工具的锚!

不确定基本的?查看我关于 Python 中正则表达式(regex)构建块的文章。

[## Python 正则表达式的简明介绍

正则表达式是数据科学家对付非结构化文本最强大的武器

towardsdatascience.com](/a-gentle-introduction-to-regular-expressions-with-python-4f3fce46dcb4)

(正文)起锚了!

在我们开始 SS 正则表达式之前,我们需要讨论锚。更具体地说,文本锚。文本锚表示在字符串的开头或结尾查找匹配。在 Python 中,有两种类型的锚点:

  • ^:匹配字符串开头的以下正则表达式
  • $:匹配字符串末尾的前一个正则表达式

提醒一下,要在 Python 中使用 regex,需要导入re模块。在尝试新的正则表达式主题(比如锚点)时,re.findall()函数特别有用。它将返回一个包含字符串中匹配项实际值的向量的列表。开始之前,请确保加载了re模块。

import re

起锚,^起锚

要起航,我们必须在旅行开始时起锚。当处理文本数据时,您可能需要匹配一个正则表达式模式,但前提是它出现在字符串中的第一项。为此,我们还使用了一个锚点,具体来说就是^

为了演示,我们的目标是找到单词“the”,但前提是它出现在字符串的开头。

anchor = 'The ship set sail on the ocean'
anchor_n = 'Ships set sail on the ocean to go places'

anchor开始,当我们使用^锚查找“the”时,我们只返回了它的一个实例。

anchor01 = re.findall('^[Tt]he', anchor)
print(anchor01)['The']

我们知道这是第一个实例,因为字符串开头的“the”是大写的。现在用anchor_n,不返回任何结果。正则表达式通常匹配句子中的“The ”,但是使用^定位符时,它只检查句子的开头。

anchor02 = re.findall('^[Tt]he', anchor_n)
print(anchor02)[]

抛锚,$

在我们的正则表达式之旅结束时,我们需要放下锚。有时,只有当正则表达式出现在字符串末尾时,才需要匹配它。这是通过$锚完成的。

让我们再来看一下anchor字符串,这次是在字符串的末尾寻找“海洋”。我们会有一个结果,“海洋。”

anchor03 = re.findall('ocean$', anchor)
print(anchor03)['ocean']

同样,如果我们查看anchor_n,这次使用$或结束锚,我们将得不到匹配,即使“海洋”出现在字符串中。如果它不在绳子的末端,$号锚就不会把它捡起来。

anchor04 = re.findall('ocean$', anchor_n)
print(anchor04)[]

正则表达式否定(避免冰山)

安妮·斯普拉特在 Unsplash 上的照片

现在我们知道了如何在字符串的开头和结尾匹配字符串(提升和降低 SS 正则表达式的锚点),我们可以继续下一个概念:告诉 regex 匹配什么不匹配

想象一下,你是一艘大船的船长,这是一艘船的处女航。让我们称这艘船为泰坦尼克号。作为这艘船的船长,你可能更关注于而不是撞上冰山,而不是其他任何具体的事情。

在字符串中,您可能希望指定某些模式来避免。要做到这一点,使用否定。这些将匹配除了您指定的内容之外的任何内容。在 Python 中有两种主要的方法来处理它们:

  • 大写元字符:元字符匹配一组字符。一个大写的元字符通常会匹配除该字符集之外的所有内容
  • ^和字符集:使用一个^和一个字符集将匹配除了在字符集中指定的以外的所有内容

大写元字符

在我上一篇关于 Python 中正则表达式的文章中,我们介绍了三种不同的元字符:\s\w\d。作为复习,它们匹配字符串中的一组字符:\s匹配空白字符(空格、制表符和换行符),\w匹配字母数字字符(字母和数字),而\d匹配任何数字(数字)。

当您大写这些元字符中的任何一个时,它将匹配除了正常匹配之外的所有内容。要查看它们的运行情况,让我们创建一个包含空格、数字、字母和标点符号的新字符串:

negation = 'I sail my 3 ships on the 7 seas.'

现在,当我们看一看我们的大写元字符时,我们将看到它们的输出是如何变化的。

  • 这将匹配除空格之外的任何内容。我们在输出中看到来自negation字符串的所有字母、数字和标点符号
negation01 = re.findall('\S', negation)
print(negation01)['I', 's', 'a', 'i', 'l', 'm', 'y', '3', 's', 'h', 'i', 'p', 's', 'o', 'n', 't', 'h', 'e', '7', 's', 'e', 'a', 's', '.']
  • \W:这将匹配除了字母和数字以外的任何内容。我们这里的输出是每个空格和句尾的句号
negation02 = re.findall('\W', negation)
print(negation02)[' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '.']
  • \D:这将匹配除数字以外的任何内容。您可能已经猜到,输出中有字母、空格和标点符号
negation03 = re.findall('\D', negation)
print(negation03)['I', ' ', 's', 'a', 'i', 'l', ' ', 'm', 'y', ' ', ' ', 's', 'h', 'i', 'p', 's', ' ', 'o', 'n', ' ', 't', 'h', 'e', ' ', ' ', 's', 'e', 'a', 's', '.']

^和字符集

一些用例需要比元字符更灵活的否定。让我们看一个例子:

  • 匹配给定字符串中的所有辅音

我们来分解一下这个问题。字母表里有 21 个辅音,我们想要全部。它们是不连续的,所以我们不能仅仅使用字符集中的一系列字母来得到它们。我们确实知道元音和辅音是互斥的,而且只有 5 个元音。如果我们能找到所有的元音,我们应该能通过否定得到所有的辅音。让我们从复习如何用字符集查找元音开始。

negation04 = re.findall('[AEIOUaeiou]', negation)['I', 'a', 'i', 'i', 'o', 'e', 'e', 'a']

括号内的字符定义了字符集。在这种情况下,任何元音都匹配。要获得除元音以外的所有内容,我们可以使用^。这将否定字符集内的所有内容。

negation05 = re.findall('[^AEIOUaeiou]', negation)[' ', 's', 'l', ' ', 'm', 'y', ' ', '3', ' ', 's', 'h', 'p', 's', ' ', 'n', ' ', 't', 'h', ' ', '7', ' ', 's', 's', '.']

…但那不是辅音。还有一点工作要做。我们的结果中有空格、数字和标点符号。由于捕获组中的所有内容都被否定,所以我们只需在结果中匹配所有不想要的内容。\s负责空格,\d负责数字,\.负责句点。\.不是元字符,是逃期。

negation06 = re.findall('[^AEIOUaeiou\s\d\.]', negation)['s', 'l', 'm', 'y', 's', 'h', 'p', 's', 'n', 't', 'h', 's', 's']

在所有的字符之后,我们需要以辅音结尾,也许这并不比键入 21 个不同的辅音更快,但它确实演示了我们如何将所有的字符加在一起,并使用正则表达式来得到我们想要的东西。

环顾四周(观赏野生动物)

马丁·韦特斯坦在 Unsplash 上拍摄的照片

出海时,我们可能会留意一些东西。看到一些野生动物将会是一次更有趣的旅行。我们寻找野生动物,这样我们就可以找到一个靠近它的地方来停泊我们的船。作为一名经验丰富的船长,我们并不真正关心野生动物本身,只关心附近的安全空间,以便驾驶船只。

类似于我们想通过野生动物寻找一个停车的地方,我们可能想在一些文本中寻找我们真正想要的信息。如果您现在还没有弄明白,正则表达式可以轻松地处理这个问题。为此,我们使用了一种叫做环视的方法。Look arounds 在字符串中搜索特定的匹配项,然后返回它之前或之后的某个值。这给我们留下了两种主要的环视方式:向前看和向后看。

让我们用下面的字符串作为例子来说明这两者是如何工作的。

lookaround = 'A penguin costs 2.99, a whale costs 5.99, I only have 3.50 left.'

对于我们想从这个字符串中提取什么信息,我们有两个场景。

  1. 每只动物的成本是多少?
  2. 卖什么动物?

起初,你可能会想为什么我们需要环顾四周才能做到这一点。你可以简单地寻找一个数字,句号,和另外两个数字。大概是这样的:

lookaround01 = re.findall('\d\.\d{2}', lookaround)

这是一个不错的开始,但是这一行代码的输出会给出三种价格。

print(lookaround01)[[1]]
[1] "2.99" "5.99" "3.50"

只有两种动物价格,最后一种是不相关的。环顾四周将有助于我们删除最后一个,并把一切都很好地纳入一个数据框架。

向后看

查看lookaround字符串,我们看到每种动物的价格前面都有单词“costs”我们将在后面用一个来看看。这将匹配我们通常匹配的任何内容,但前提是它之前有其他内容的匹配。前瞻的通用公式是"(?<=if preceded by this)match_this"。在我们的例子中,这将翻译如下(\s被添加以说明单词和数字之间的空间):

# Look behind
look_behind = re.findall('(?<=costs)\s\d\.\d{2}', lookaround)

现在我们有了每只动物的价格,不包括我在绳子上剩下的钱。

print(look_behind)[' 2.99', ' 5.99']

如果你仔细看,你会注意到琴弦上有空隙。现在不要担心这些,当我们建立一个数据框架来保存我们的价目表时,我们会处理这些问题。

向前看

现在,我们已经从字符串中获得了动物的价格,我们希望为价格列表数据框获取动物的名称。

为此,我们现在需要匹配单词“成本”前面的单词。为此,我们将使用前瞻。前瞻的基本公式如下:"match this(?=if followed by this)"。要从我们的字符串中获取动物名称,应该是这样的:

# Look Ahead
look_ahead = re.findall('\w+\s(?=costs)', lookaround)

就这样,我们抓住了单词 costs 之前的每个单词。在我们的字符串中是所有动物的名字。

print(look_ahead)['penguin ', 'whale ']

创建动物价格数据框

为了创建动物价格的数据框架,我们已经拥有了大部分我们需要的东西。我们只是用来自animalsprices的向量作为列创建一个数据帧。当然,要在 Python 中做到这一点,我们需要首先导入pandas模块作为pd

import pandas as pdanimal_prices = {'animals': look_ahead, 'prices': look_behind}
animal_prices_df = pd.DataFrame(animal_prices)

如果你忘了,当我们使用re.findall()时,它返回一个字符串列表。这可以很容易地用于创建字典和数据框。

我们的下一步是解决这些多余的空间。我们将使用.strip()方法。它移除字符串开头或结尾的空格。我们将使用列表理解在一行代码中为每一列完成这项工作。

animal_prices_df['animals'] = [animal.strip() for animal in animal_prices_df['animals']]
animal_prices_df['prices'] = [price.strip() for price in animal_prices_df['prices']]

生成的数据框如下所示:

 animals prices
0  penguin   2.99
1    whale   5.99

结论和进一步学习

就这样,你在 SS 正则表达式上的巡航结束了。你学到了:

  1. 如何匹配出现在字符串开头或结尾的字符串
  2. 如何用否定来寻找除了某物以外的任何东西
  3. 如何使用 look arounds 来匹配后面或前面的事物

以下是与 Python 中的正则表达式相关的一些其他资源,可能对您有所帮助:

  • 官方 [re](https://docs.python.org/3/library/re.html) 文档:虽然文档看起来令人生畏,但学习如何阅读它只会在你编程时对你有所帮助
  • w3schools 参考资料:庞大的编码和脚本语言参考资料知识库,包括 python。他们的许多例子都可以通过点击“自己尝试”按钮直接在浏览器上运行
  • Datacamp 课程(付费链接):一个致力于数据科学、机器学习和数据可视化的在线学习社区。查看他们的课程“Python 中的正则表达式”网站上每门课程的第一章都是免费的!

[## 通过我的推荐链接加入 Medium-Drew Seewald

作为一个媒体会员,你的会员费的一部分会给你阅读的作家,你可以完全接触到每一个故事…

realdrewdata.medium.com](https://realdrewdata.medium.com/membership)

起锚!更多你希望知道的 R 正则表达式概念

原文:https://towardsdatascience.com/anchors-away-more-regex-concepts-in-r-f00fe7f07d52?source=collection_archive---------25-----------------------

教程| R |正则表达式(Regex)

在 R 中使用高级正则表达式工具处理文本的秘密

所以你已经知道了 r 中正则表达式的基本知识,比如如何使用字符集、元字符、量词和捕获组。这些是基本的构建模块,但是你是一个超级用户,永远不会满足于仅仅是基本的。你的文本争论问题比你希望用这些工具解决的要复杂得多。幸运的是,R 中有更多的正则表达式概念需要学习。这是更多文本争论善良的锚!

不确定基本的?请查看我在 r。

[## R 正则表达式的简明介绍

正则表达式是数据科学家对付非结构化文本最强大的武器。他们曾经是野餐…

towardsdatascience.com](/a-gentle-introduction-to-regular-expressions-with-r-df5e897ca432)

(正文)起锚了!

在我们开始 SS 正则表达式之前,我们需要讨论锚。更具体地说,文本锚。文本锚表示在字符串的开头或结尾查找匹配。在 R 中,有两种类型的锚:

  • ^:匹配字符串开头的以下正则表达式
  • $:匹配字符串末尾的前一个正则表达式

提醒一下,要在 R 中使用 regex,您需要使用stringr包。在尝试新的正则表达式主题(比如锚点)时,str_extract_all()函数特别有用。它将返回一个包含字符串中匹配项实际值的向量的列表。确保在开始之前加载stringr包。

library(stringr)

起锚,^起锚

要起航,我们必须在旅行开始时起锚。当处理文本数据时,您可能需要匹配一个正则表达式模式,但前提是它出现在字符串中的第一项。为此,我们使用^锚。

为了演示,我们的目标是找到单词“the”,但前提是它出现在字符串的开头。这里有几个示例字符串供您试用。

anchor <- "The ship sets sail on the ocean"
anchor_n <- "Ships set sail on the ocean to go places"

anchor字符串开始,当我们使用^锚查找“the”时,我们只返回了它的一个实例。

str_extract_all(anchor, "^[Tt]he")[[1]]
[1] "The"

我们知道这是第一个实例,因为字符串开头的“the”是大写的。现在用anchor_n,不返回任何结果。正则表达式通常匹配句子中的“The ”,但是使用^定位符时,它只检查句子的开头。

str_extract_all(anchor_n, "^[Tt]he")[[1]]
character(0)

抛锚,$

在我们的正则表达式巡航结束时,我们需要放下锚。有时,只有当正则表达式出现在字符串末尾时,才需要匹配它。这是通过$锚完成的。

让我们再来看一下anchor字符串,这次是在字符串的末尾寻找“海洋”。我们会有一个结果,“海洋。”

str_extract_all(anchor, "ocean$")[[1]]
[1] "ocean"

同样,如果我们查看anchor_n,这次使用$或结束锚,我们将得不到匹配,即使“海洋”出现在字符串中。如果它不在绳子的末端,$号锚就不会把它捡起来。

str_extract_all(anchor_n, "ocean$")[[1]]
character(0)

正则表达式否定(避免冰山)

安妮·斯普拉特在 Unsplash 上的照片

现在我们知道了如何匹配字符串开头和结尾的模式(提升和降低 SS 正则表达式的锚),我们可以继续下一个概念:告诉 regex 匹配什么不匹配

想象一下,你是一艘大船的船长,这艘船正在进行它的处女航。让我们称这艘船为泰坦尼克号。作为那艘船的船长,你可能对而不是撞上冰山非常感兴趣。

在字符串中,您可能希望指定某些模式来避免。要做到这一点,使用否定。这些将匹配除了您指定的内容之外的任何内容。在 R 中有两种主要的方法来处理它们:

  • 大写元字符:元字符匹配一组特定的字符。一个大写的元字符通常会匹配除该字符集之外的所有内容
  • ^和字符集:使用一个^和一个字符集将匹配除了在字符集中指定的以外的所有内容

大写元字符

在我上一篇关于 R 中正则表达式的文章中,我们介绍了三种不同的元字符:\\s\\w\\d。作为复习,它们匹配字符串中的一组字符:\\s匹配空白字符(空格、制表符和换行符),\\w匹配字母数字字符(字母和数字),而\\d匹配任何数字(数字)。

当您大写这些元字符中的任何一个时,它将匹配除了正常匹配之外的所有内容。要查看它们的运行情况,让我们创建一个包含空格、数字、字母和标点符号的新字符串:

negation <- "I sail my 3 ships on the 7 seas."

当我们看一看我们的大写元字符时,我们会看到它们与小写元字符的操作方式有所不同。

  • 这将匹配除空格之外的任何内容。我们在输出中看到来自negation字符串的所有字母、数字和标点符号
str_extract_all(negation, "\\S")[[1]]
[1] "I" "s" "a" "i" "l" "m" "y" "3" "s" "h" "i" "p" "s" "o" "n" "t" "h" "e" "7" "s" "e" "a" "s" "."
  • \\W:这将匹配除了字母和数字以外的任何内容。我们这里的输出是每个空格和句尾的句号
str_extract_all(negation, "\\W")[[1]]
[1] " " " " " " " " " " " " " " " " "."
  • \\D:这将匹配除数字以外的任何内容。您可能已经猜到,输出中有字母、空格和标点符号
str_extract_all(negation, "\\D")[[1]]
 [1] "I" " " "s" "a" "i" "l" " " "m" "y" " " " " "s" "h" "i" "p" "s" " " "o" "n" " " "t" "h" "e" " " " " "s" "e" "a" "s" "."

^和字符集

一些用例需要比元字符更灵活的否定。让我们看一个示例场景:

  • 匹配给定字符串中的所有辅音

我们来分解一下这个问题。字母表里有 21 个辅音,我们想要全部。它们是不连续的,所以我们不能仅仅用一个字符集中的一系列字母来得到它们。我们确实知道元音和辅音是互斥的,而且只有 5 个元音。如果我们可以找到所有的元音,我们应该能够否定它得到所有的辅音。让我们从复习如何用字符集查找元音开始。

str_extract_all(negation, "[AEIOUaeiou]")[[1]]
[1] "I" "a" "i" "i" "o" "e" "e" "a"

括号内的字符定义了字符集。在这种情况下,每个元音都匹配。为了获得除元音以外的所有内容,我们可以使用字符集内的^。这会否定里面的一切。

str_extract_all(negation, "[^AEIOUaeiou]")[[1]]
[1] " " "s" "l" " " "m" "y" " " "3" " " "s" "h" "p" "s" " " "n" " " "t" "h" " " "7" " " "s" "s" "."

…但那不是辅音。还有一点工作要做。我们的结果中有空格、数字和标点符号。由于捕获组中的所有内容都被否定,所以我们只需在结果中匹配所有不想要的内容。\\s负责空格,\\d负责数字,\\.负责句点。

注意:\\.不是元字符,是转义句号。

str_extract_all(negation, "[^AEIOUaeiou\\s\\d\\.]")[[1]]
[1] "s" "l" "m" "y" "s" "h" "p" "s" "n" "t" "h" "s" "s"

在添加了所有需要的字符后,我们最终只需要辅音字母,也许这并不比输入 21 个不同的辅音字母更快,但它确实演示了我们如何将所有字符相加,并使用正则表达式来获得我们想要的结果。

环顾四周(观赏野生动物)

马丁·韦特斯坦在 Unsplash 上拍摄的照片

出海时,我们可能会留意一些东西。看到一些野生动物将会是一次更有趣的旅行。我们寻找野生动物,这样我们就可以找到一个靠近它的地方来停泊我们的船。作为一名经验丰富的船长,我们并不真正关心野生动物本身,只关心附近的安全空间,以便驾驶船只。

类似于我们想通过野生动物寻找一个停车的地方,我们可能想在一些字符串周围寻找我们真正想要的信息。与大多数任务一样,正则表达式使这变得容易得多。为此,我们使用了一种叫做环视的东西。Look arounds 在字符串中搜索特定的匹配项,然后返回它之前或之后的某个值。这给我们留下了两种主要的环视方式:向前看和向后看。

让我们用下面的字符串作为例子来说明这两者是如何工作的。

lookaround <- "A penguin costs 2.99, a whale costs 5.99, I only have 3.50 left."

对于我们想从这个字符串中提取什么信息,我们有两个场景。

  1. 每只动物的成本是多少?
  2. 卖什么动物?

起初,你可能会想为什么我们需要环顾四周才能做到这一点。你可以简单地寻找一个数字,句号,和另外两个数字。大概是这样的:

str_extract_all(lookaround, "\\d\\.\\d{2}")

这是一个不错的开始,但是这一行代码的输出会给出三种价格。

[[1]]
[1] "2.99" "5.99" "3.50"

只有两种动物价格,最后一种是不相关的,所以我们想排除它。环顾四周将有助于我们删除最后一个,并把一切都很好地纳入一个数据框架。

向后看

查看lookaround字符串,我们看到每种动物的价格前面都有单词“costs”我们将在后面使用一个外观。这将匹配我们通常匹配的任何内容,但前提是它之前有其他内容的匹配。前瞻的通用公式是"(?<=if preceded by this)match_this"。在我们的例子中,这将翻译如下(\\s被添加以说明单词和数字之间的空间):

# Look behind
prices <- str_extract_all(lookaround, "(?<=costs)\\s\\d\\.\\d{2}")
prices

现在我们有了每只动物的价格,不包括我在绳子上剩下的钱。

[[1]]
[1] " 2.99" " 5.99"

如果你仔细看,你会注意到琴弦上有空隙。现在不要担心这些,当我们建立一个数据框架来保存我们的价目表时,我们会处理这些问题。

向前看

现在,我们已经从字符串中获得了动物的价格,我们希望为价格列表数据框获取动物的名称。

为此,我们现在需要匹配单词“costs”前面的单词。为此,我们将使用前瞻。前瞻的基本公式如下:"match this(?=if followed by this)"。要从我们的字符串中获取动物名称,应该是这样的:

# Look Aheads
animals <- str_extract_all(lookaround, "\\w+\\s(?=costs)")
animals

就这样,我们抓住了单词 costs 之前的每个单词。在我们的字符串中是所有动物的名字。

[[1]]
[1] "penguin " "whale "

创建动物价格数据框

为了创建动物价格的数据框架,我们已经拥有了大部分我们需要的东西。我们只是创建一个数据帧,用向量animalsprices作为列。

animal_prices <- data.frame(
    animals=animals[[1]],
    prices=prices[[1]],
    stringsAsFactors=FALSE
)

如果你忘了,当我们使用str_extract_all()时,它返回一个包含字符串向量的列表。为了访问字符串向量,我们使用了双括号符号(animals[[1]])。参数stringsAsFactors被设置为 false,否则 R 将把列创建为因子,而不是字符串。

我们的下一步是解决这些多余的空间。对于包含更多列的数据框,我们可以考虑使用purrr包中的map函数,但是由于我们的函数很小,我们可以手动完成这个过程。我们将使用str_trim()功能。默认情况下,它会删除字符串开头和结尾的空格。

animal_prices$animals <- str_trim(animal_prices$animals)
animal_prices$prices <- as.numeric(str_trim(animal_prices$prices))

添加了as.numeric()函数来将prices列的类型从字符更正为数字。生成的数据框如下所示:

 animals prices
1 penguin   2.99
2   whale   5.99

结论

就这样,你在 SS 正则表达式上的巡航结束了。你学到了:

  1. 如何匹配出现在字符串开头或结尾的模式
  2. 如何使用否定来匹配除指定内容之外的任何内容
  3. 如何使用 look arounds 来匹配后面或前面有您指定的其他内容的内容

下面是一些与 R 中的正则表达式相关的资源,您可能会觉得有帮助:

  • tidy verse 网站官方 [stringr](https://stringr.tidyverse.org/index.html) 页面:r studio的伙计们整理了资源帮助学习stringr之类的包。他们甚至包括一个[stringr](https://github.com/rstudio/cheatsheets/blob/master/strings.pdf) 备忘单,你可以打印出来参考。
  • R for Data Science :由 Hadley Wickham 编写,stringr package 的作者,这本书对于 R 中的一切都是很好的参考。甚至有一章介绍了 R 中的高级正则表达式。它可以在网上免费获得这里,或者你可以在这里购买硬拷贝(付费链接)。
  • Datacamp 课程(付费链接):一个致力于数据科学、机器学习和数据可视化的在线学习社区。查看他们的课程“用 r 中的 stringr 进行字符串操作”,Datacamp 上每个课程的第一章都是免费的!

[## 通过我的推荐链接加入 Medium-Drew Seewald

作为一个媒体会员,你的会员费的一部分会给你阅读的作家,你可以完全接触到每一个故事…

realdrewdata.medium.com](https://realdrewdata.medium.com/membership)

自行车上的古丝绸之路——游牧自行车人问题

原文:https://towardsdatascience.com/ancient-silk-road-on-bicycle-nomad-cyclist-problem-c0c6db8bb217?source=collection_archive---------48-----------------------

旅行商问题的分步指南及其在自行车旅游规划中的应用

丝绸之路是一个古老的路线网络,包括中国西部、中亚、西亚和南亚的几十个城市。过去,从一个城市到另一个城市的最短已知路径通常成为路线的一部分。例外是为了避免暴徒,恶劣的天气和耗尽食物和水的风险。

沿着古丝绸之路的一条路线——(与OpenStreetMap.org一起画的路线)

我的几个朋友计划明年骑自行车穿越古丝绸之路。这将是一次人力驱动的旅行,就像过去大多数人通过这些路线旅行一样。我的朋友不是职业自行车运动员。所以,在这数千公里的旅程中,每一桨都必须划向正确的方向。

我最初认为这是一个疯狂的想法,但后来我意识到这将是一生一次的旅行。所以,我决定尝试编程来计划这次旅行。

城市

这些骑自行车的人已经计划好了以下几个丝绸之路上不可错过的主要古城:

中国🇨🇳Xi(丝绸贸易的起源地)

中国🇨🇳兰州

中国🇨🇳敦煌

中国🇨🇳高昌

中国🇨🇳乌鲁木齐市

哈萨克斯坦阿拉木图🇰🇿

吉尔吉斯斯坦🇰🇬比什凯克

乌兹别克斯坦🇺🇿塔什干

塔吉克斯坦🇹🇯杜尚别

乌兹别克斯坦🇺🇿boy sun

乌兹别克斯坦🇺🇿撒马尔罕

乌兹别克斯坦布哈拉🇺🇿

土库曼斯坦🇹🇲梅尔夫

土库曼斯坦🇹🇲阿什哈巴德

伊朗🇮🇷德黑兰

伊朗🇮🇷大不里士

土耳其🇹🇷安卡拉

土耳其🇹🇷伊斯坦布尔

沿途有很多城市有丰富的历史遗迹,在古代贸易中有重要意义,但是不可能在人力自行车旅行中包括所有的城市。比如巴基斯坦的塔西拉,中国的喀什。

旅行推销员问题

为了找到穿过所有这些城市的最短路线,让我们使用经典的旅行商问题(以下简称 TSP)算法。TSP 是一个优化问题。TSP 说

“给定一个要游览的城市列表以及这些城市之间的距离,游览所有这些地点并返回我们出发地点的最短路径是什么”

在原始 TSP 中,我们计算的路线是一条封闭路线,并且我们的开始和结束位置必须相同。

旅行推销员问题——点代表城市,线代表道路——(TSP on Wikipedia)

但是作为骑自行车的人,我们感兴趣的是沿途的城市,而不是回到起点。TSP 有许多变体,让我们把 TSP 的这种变体称为“游牧自行车手问题”。

开放旅游旅行推销员问题(图片由作者提供)

距离矩阵

距离矩阵是制定几乎所有涉及路径的优化问题的起点。例如最短路径问题、旅行商问题、车辆路径问题和物流路径优化。

看看下面的距离矩阵:

丝绸之路距离矩阵—第一部分(图片由作者提供)

第 2 行第 1 列的单元格的值为 626。这是从兰州到西安的公里数。然而,从西安到兰州的距离是 627,由第一行第二列中的单元表示。

上表中每个像元的值是以公里为单位的道路长度,从左侧的城市到顶部标题中的城市。

我们将使用 2D 数组(或者 Python 中的 2D 列表)来存储距离矩阵值。

*# distance matrix* D **=** [[0, 627, 1724, 2396, 2531, 3179],
    [626, 0, 1096, 1768, 1903, 2551],
    [1723, 1095, 0, 929, 1064, 1713],
    [2395, 1766, 849, 0, 163, 811],
    [2531, 1903, 985, 163, 0, 656],
    [3186, 2557, 1640, 817, 664, 0]]

对于真实世界的位置,我们可以使用简单的 API 调用,使用谷歌地图距离矩阵 APIMapbox 的矩阵 API 轻松计算我们位置的距离矩阵。

计算距离矩阵

手动计算多个城市的距离矩阵变得很烦人。让我们写一些脚本来为我们计算距离矩阵。

要使用 Google Maps Matrix API 计算距离矩阵,我们只需提供地点的名称。但是我决定硬编码城市的经度,纬度坐标。我认为当不止一个地方有相似的名字时,坐标可以避免混淆。

getCities方法给出了城市+坐标的列表。

**def** **getCities**():
    *# locations to visit
*    cities **=** [
        {
            "name": "Xi'an, Shaanxi, China",
            "location": [34.341576, 108.939774]
        },
        {
            "name": "Lanzhou, China",
            "location": [36.061089, 103.834305]
        },

        ...

        {
            "name": "Istanbul, Turkey",
            "location": [41.022576, 28.961477]
        }
    ]
    **return** cities

计算距离矩阵

计算距离矩阵现在很容易。我们只需要调用一个 gmap API 端点,提供两个列表:起点和终点,我们希望计算它们之间的距离,并以 JSON 的形式返回我们的距离矩阵,该矩阵将被解析并转换为我们在上面看到的 2D 列表D

import requests, traceback

**def** **gmapMatrix**(origins, destinations):
    API_KEY **=** GET_GMAP_API_KEY() *# <<= gmap api key
*    URL **=**f"https://maps.googleapis.com/maps/api/distancematrix/json?units=metric&origins={origins}&destinations={destinations}&key={API_KEY}"

    **try**:
        r **=** requests.get(URL)
        jsn **=** r.json()

        **return** jsn
    **except** Exception:
        traceback.print_exc()
        **return** None

让我们找到我们的距离矩阵。好吧,没那么容易。原来谷歌的 Matrix APIs 有一些限制。

根据谷歌地图矩阵 API:

  1. 发送到距离矩阵 API 的每个查询都会生成元素,其中起点数量乘以目标数量等于元素数量
  2. 每个请求最多允许 25 个起点或 25 个目的地
  3. 每个服务器端请求最多允许 100 个元素

计算超过 100 个元素的距离矩阵

我们有大约 20 个城市。20*20 等于 400。我们超过了 100 个元素的限制。我们必须将我们的请求分成 100 个一批,获取距离并构建我们的20 x 20距离矩阵。

如果你熟悉深度学习中的卷积神经网络,你可能知道我们取一小部分矩阵,做一些运算,计算另一个矩阵的卷积技术。这个过程是一步一步的,每次只对矩阵的一小部分起作用。

一次查看矩阵的一小部分(卷积神经网络,维基百科)

别担心,我们处理 100 个元素限制的计算比卷积神经网络简单得多。

看看项目代码中的[convolveAndComputeDistMatrix](https://github.com/emadehsan/nomad-cyclist/blob/0f42ef8a06bdf2be0b7c0fdec32661d16efded38/distance_matrix.py#L26)方法。该方法遍历距离矩阵,仅挑选 100 个项目来寻找距离,并构造一个距离矩阵20 x 20...我们想要的距离矩阵。

履行

让我们把我们的游牧自行车手问题(TSP)公式化为一个线性规划问题。为了解决一个线性规划问题,我们通常需要 4 样东西

  • 决策变量
  • 限制
  • 目标
  • 解决者

解决者

(优化)解算器是一个软件或库,它采用以某种方式定义(建模)的优化问题,并试图找到该问题的最佳解决方案。我们的工作是对问题进行适当的建模,求解者承担繁重的工作。

我们将使用 OR-Tools,这是一个由 Google AI 团队开发的开源库,用于解决优化问题。

让我们看看如何为或工具初始化或工具的线性规划求解问题建模

from ortools.linear_solver import pywraplp

s **=** pywraplp.Solver('TSP', \
        pywraplp.Solver.GLOP_LINEAR_PROGRAMMING)

决策变量

简而言之,决策变量是包含线性规划算法输出的变量。我们的算法做出的决定!

对于 TSP,决策变量将是与距离矩阵大小相同的 2D 列表。它将在像元中包含1,其对应的道路必须被访问,因此是输出路径的一部分。以及0在不得通行的道路上。

可视化旅行商问题的决策变量(图片由作者提供)

根据 OR-Tools 线性求解器文档,我们使用IntVar定义一个整数决策变量。

如果在普通的 Python 代码中,我们将创建一个名为count的变量,它将保存某个任务完成的次数。我们可能会从 0 开始,并随着每个任务不断增加,直到程序结束,其中会包含一些我们感兴趣的有用值。

count **=** 0

如果我们想要定义相同的变量,但要由 OR-Tools 线性求解器使用,我们将按如下方式定义它(现在它将被称为决策变量)。

*# IntVar(lowerBound, upperBound, name)* count **=** s.IntVar(0, 100, 'count_var')

注意,我们也必须提供界限。这告诉规划求解找到一个从下限到上限的整数值。这有助于缩小可能的输出范围,求解器可以更快地找到解决方案。

现在我们定义 TSP 的决策变量。这将是一个由IntVar组成的 2d Python 列表。但是在这里,城市的边界将是从01,除了那些没有找到内部距离的城市,它们的边界将是从00

x **=** [[s.IntVar(0, 0 **if** D[i][j] **is** None **else** 1, '') \
        **for** j **in** range(n)] **for** i **in** range(n)]

限制

将约束视为必须遵守的限制或规则。根据 TSP,

"我们必须在一条封闭的道路上游览每一个城市一次."

首先我们将解决一个封闭旅游的 TSP 问题。然后我们将修改它,为我们的游牧骑行者提供一条开放的最短路径。因此,在我们的解决方案中,必须只有一条路通往城市,并且只有一条路通往城外。

**for** i **in** range(n):  
    *# in i-th row, only one element can be used in the tour
*    *# i.e. only 1 outgoing road from i-th city must be used
    # in the tour
*    s.Add(1 **==** sum(x[i][j] **for** j **in** range(n))) 

    *# in j-th column, only one element can be used in the tour
*    *# i.e. only 1 incoming road to j-th city must be used in tour
*    s.Add(1 **==** sum(x[j][i] **for** j **in** range(n))) 

    *# a city must not be assigned a path to itself in the tour
*    *# no direct road from i-th city to i-th city again must be taken
*    s.Add(0 **==** x[i][i])

以上约束是不够的。有时存在有问题的子区域,每个子区域覆盖不同城市的子集,但没有一个子区域覆盖所有城市。我们想在所有城市进行一次(最短的)旅行。

有问题的(断开的)子旅行团(图片由作者提供)

我们知道,任何由 6 个城市组成的封闭路径都有 6 条弧线(边/路)连接它们(如上图)。因此,我们告诉我们的求解程序,如果一个旅行是一个子旅行(即它覆盖的城市少于距离矩阵中的城市总数),连接这些城市的道路必须少于这个子旅行中的城市数。这将强制求解程序查找未闭合的子区域,最终成功找到连接所有城市的闭合(最短)旅行。

但是我们不能找到所有可能的次区域,并把它们包含在我们的约束中。避免这种子区域的方法是,在第一次运行我们的算法时,我们找到一个解决方案,如果它包含子区域,我们再次运行算法,这次我们告诉它保持这些子区域不闭合。我们不断重复这个过程,直到我们找到一个覆盖所有城市的最佳旅游路线,而不是小的不相连的子区域。

让我们对这个约束进行建模:

*# Subtours from previous run (if any)* **for** sub **in** Subtours:
    *# list containing total outgoing+incoming arcs to 
*    *# each city in this subtour
*    K **=** [ x[sub[i]][sub[j]] **+** x[sub[j]][sub[i]] \
         **for** i **in** range(len(sub)**-**1) **for** j **in** range(i**+**1,len(sub)) ]

    *# sum of arcs (roads used) between these cities must 
*    *# be less than number of cities in this subtour
*    s.Add(len(sub)**-**1 **>=** sum(K))

目标

我们整数规划算法的目标。在我们的例子中,我们希望尽可能缩短我们访问所有城市的距离。

*# minimize the total distance of the tour* s.Minimize(s.Sum(x[i][j]*****(0 **if** D[i][j] **is** None **else** D[i][j]) \
                   **for** i **in** range(n) **for** j **in** range(n)))

换句话说,找到一条穿过这些城市的最短路径,同时遵守上述约束。这是一条最短的路径。因为可能有多条长度相同的最短路径。

游牧骑自行车者问题

TSP 与开放路线将是我们的游牧自行车的问题。让我们现有的算法找到开放路线而不是封闭路线的一个简单技巧是在距离矩阵中添加一个虚拟城市。将这个虚拟城市到所有其他城市的距离设置为 0。

当求解程序找到最优解时,在封闭路径上的某个地方会有一个虚拟城市。我们简单地删除这个虚拟城市和它的进出弧线(道路),为我们的游牧自行车手提供一条开放的路线。

*# to make the loops run 1 more time then current size of
# our lists so we could add another row / column item* n1 **=** n **+** 1

*# if i or j are equal to n, that means we are in the last 
# row / column. just add a 0 element here* E **=** [[0 **if** n **in** (i,j) **else** D[i][j] \
    **for** j **in** range(n1)] **for** i **in** range(n1)]

由此产生的路线将是最短的单程和开放的旅游覆盖所有城市。

手动 v 计算路线比较

根据我们的求解程序,我们必须按照这个顺序在城市中循环

Shortest Path for our Cycling Tour:
    Cities:  Xi'an  Lanzhou  Dunhuang  Gaochang  Urumqi  Khorgas  Horgos  Almaty  Bishkek  Tashkent  Samarqand  Dushanbe  Baysun  Bukhara  Merv  Ashgabat  Tehran  Tabriz  Ankara  Istanbul
 City Index      0        1         2         3       4        5       6       7        8         9         12        10      11       13    14        15      16      17      18        19
   Distance      0      627      1096       929     163      656      -1     332      237       631        310       292     196      341   353       400     948     632    1463       451
 Cumulative      0      627      1723      2652    2815     3471    3470    3802     4039      4670       4980      5272    5468     5809  6162      6562    7510    8142    9605     10056

在上述输出不可读的情况下,相同输出的照片:

算法计算的游牧骑行旅行(图片由作者提供)

在决定了要参观的城市之后,我们对参观这些地方的顺序做了一个粗略的计划。在谷歌地图上,你不能在超过 10 个地方之间创建一条路线(我想)。

我们之前计划的中国丝绸之路城市路线的第一站,与我们的算法建议的路线相匹配:

丝绸之路的中国部分—(在谷歌地图上绘制)

我们计划的中东到欧洲之旅的最后一站…这也与算法输出相匹配:

穿越中东的丝绸之路—(在谷歌地图上绘制)

我们手动规划的中亚城市路线的第二段(中间):

中亚——丝绸之路——(在谷歌地图上绘制)

根据我们的算法,除了中亚的几个城市之外,最短路线与我们手动规划的路线的 90%匹配:

中亚——最短路径——丝绸之路——(谷歌地图)

当骑自行车数千公里时,即使少走几百公里也能让我们感觉如释重负。我们希望这是对旅行推销员问题和游牧自行车手问题的一个很好的指导。我们接下来要考虑的一个重要问题是海拔。

规划自己的路线

所有的项目代码都在 GitHub 上,上面有很多有用的评论。您可以随意定制它并提出拉取请求。

这里使用的(TSP)线性规划算法摘自 Serge Kruk 的实用 Python AI 项目一书。如果你想学习路线优化算法或者一般的优化算法,强烈推荐。

接下来,我们将增加考虑海拔和攀爬难度的选项,这对于计划游览的骑行者来说是非常重要的。

原载于 2020 年 10 月 19 日https://alternate . parts

使用 Chaquopy 实现概念自动化的 Android 应用程序

原文:https://towardsdatascience.com/android-app-for-notion-automation-using-chaquopy-863e72fa4ecd?source=collection_archive---------59-----------------------

使用 Android、Chaqopy 和 opinion-py 实现观念的自动化。

这个应用程序是如何工作的?

最近写了一篇文章,讲述了我如何使用 idea-py 自动将我的费用添加到 idea 的费用管理器数据库中。

在发现使用 idea-py 添加费用是多么容易之后,我继续创建了一个简单的 android 应用程序,它允许我输入一个简单的文本字符串,然后将数据迁移到 idea 的数据库中。我使用 chaquoy 在 android 中运行我的 python 脚本。这个项目在 git-hub 上是开源的,欢迎你来查看并让我知道你的想法。如果您希望合作,请阅读 github 上的自述文件。

因此,这个应用程序基本上做的,是采取一个字符串'费用,金额,评论,标签'并将其添加到概念的费用数据库。因为你可以在 github 上查看完整的代码,所以我只解释核心代码。

# -*- coding: utf-8 -*-
"""
Created on Sat May  3 15:22:31 2020

@author: Prathmesh D
"""

def addRecord(Expense,Amount,Comment,Category,v2,db):
    import datetime
    from notion.client import NotionClient
    Date = datetime.datetime.now()

    print(v2)
    print(db)
    # Obtain the `token_v2` value by inspecting your browser cookies on a logged-in session on Notion.so

    try:
        client = NotionClient(token_v2=v2)
        print("Client Connected..")
    except:
        s ="V2Error"
        return s

    try:
        cv = client.get_collection_view(db)
        print("Connected to db")
    except:
        s="dbError"
        return s

    try:
        row = cv.collection.add_row()

        row.Expense = Expense
        row.Amount = Amount
        row.Comment = Comment
        row.Category = Category
        row.Date=Date
        return "Success"
    except Exception as e:
        s = str(e)
	#Get the values from the database         
	filter_params = [{
            "property": "Expense",
        }]
        result = cv.build_query(filter=filter_params).execute()
        #Get size of db
	size = len(result)
        #Remove last entry
	result[size-1].remove()
        return s

基本脚本和我之前的帖子中的一样。唯一增加的是 catch 块,其中我删除了最新的记录,因为有一些错误,并将生成的错误返回给 android 应用程序。

在 android 应用程序中,我使用 AsyncTask 向 python 脚本发送请求,并返回结果。如果您不熟悉 AsyncTask,它有 4 个主要部分:

  1. onPreExecute() —在调用新线程之前准备数据。在 UI 线程上运行。
  2. doInBackground(Params…) —创建新线程并在后台运行的异步调用。这是我们将使用 chaqopy 调用脚本的地方。
  3. onProgressUpdate(Progress…) —该方法在 UI 线程上运行,您可以更新在 doInBackground()方法中完成的流程或操作。我还没有在我的应用程序中使用它,但是使用它的一种方法是显示在一次调用中添加多个条目时完成了多少个条目。
  4. onPostExecute(Result) —此函数在 doInBackground()完成执行后调用。它也运行在 UI 线程上。

以下是我如何使用这些方法的:

  1. onPreExecute()
@Override
protected void onPreExecute() {
    super.onPreExecute();

    editTextString[0] = getData.getText().toString();
    getData.setText("");

    d[0] = editTextString[0].split(Pattern.quote(","));

    amount[0] = Integer.parseInt(d[0][1]);

    date[0] = Calendar.getInstance().getTime();

}

2.doInBackround(参数…)

@Override
protected String doInBackground(String... params) {
    try{
	//Call to chaquopy library
        returned_value = viewSource(MainActivity.this, d[0], amount[0], date[0], TOKEN_V2, DB_URL);
	return returned_value;
    }catch (Exception e){
        return returned_value;
    }

}

2.1 viewSource(参数…)

Chaquopy 码

@Override
protected String doInBackground(String... params) {
    try{
	//Call to chaquopy library
        returned_value = viewSource(MainActivity.this, d[0], amount[0], date[0], TOKEN_V2, DB_URL);
	return returned_value;
    }catch (Exception e){
        return returned_value;
    }

}

3.onPostExecute(结果)

在这个方法中,我们主要检查用户是否正确输入了他的 v2_Token 和数据库 URL,我们检查 python 脚本的结果,并相应地在主 UI 线程上显示结果。

@Override
protected void onPostExecute(String result) {
    super.onPostExecute(result);
    SharedPreferences.Editor putEditor = getSharedPreferences(SAVED_PREFERENCES, MODE_PRIVATE).edit();
    if(result.equals("Success")) {
        Toast.makeText(MainActivity.this, result, Toast.LENGTH_SHORT).show();
    }
    else{

        if(result.equals("V2Error")){
            Toast.makeText(MainActivity.this, "Your V2 Token is wrong. Please Enter Details again!", Toast.LENGTH_LONG).show();
            putEditor.remove("Init");
            putEditor.commit();
            Intent intent = new Intent(MainActivity.this,userDetails.class);
            startActivityForResult(intent,2);

        }
        else if(result.equals("dbError")){
            Toast.makeText(MainActivity.this, "Your db URL is wrong. Please Enter Details again!", Toast.LENGTH_LONG).show();
            putEditor.remove("Init");
            putEditor.commit();
            Intent intent = new Intent(MainActivity.this,userDetails.class);
            startActivityForResult(intent,2);
        }
        else{
            String sourceString = "<b>" + result + "</b> " ;
            tv.setText(Html.fromHtml(sourceString, Html.FROM_HTML_MODE_LEGACY));
            Toast.makeText(MainActivity.this, "Record Deleted!\\nPlease add again.", Toast.LENGTH_LONG).show();
        }

    }

我希望这在某种程度上对你有所帮助。

下次见。干杯!

使用 PyTorch 和卷积神经网络的动物分类

原文:https://towardsdatascience.com/animal-classification-using-pytorch-and-convolutional-neural-networks-78f2c97ca160?source=collection_archive---------16-----------------------

作者图片

介绍

PyTorch 是脸书 AI 研究实验室(FAIR)开发的深度学习框架。由于其 C++和 CUDA 后端,称为 Tensors 的 N 维数组也可以在 GPU 中使用。

卷积神经网络(ConvNet/CNN) 是一种深度学习,它获取输入图像,并为各种特征分配重要性(权重和偏差),以帮助区分图像。

作者图片

神经网络大致分为 3 层:

  • 输入层
  • 隐藏层(可以由一个或多个这样的层组成)
  • 输出层

隐藏层可以进一步主要分为两层-

  • 卷积层:从给定的输入图像中提取特征。
  • 完全连通的密集图层:它为卷积图层中的要素分配重要性以生成输出。

卷积神经网络过程通常包括两个步骤

  • 前向传播:权重和偏差被随机初始化,这在最后生成输出。
  • 反向传播:权重和偏差在开始时随机初始化,并根据误差更新值。对于较新的输出,这些更新的值一次又一次地发生正向传播,以最小化误差。+

照片由 Fotis FotopoulosUnsplash 上拍摄

激活函数是分配给神经网络中每个神经元的数学方程,并根据其在图像中的重要性(权重)来确定是否应该激活它。

有两种类型的激活功能:

  • 线性激活函数:它获取输入,乘以每个神经元的权重,并创建与输入成比例的输出信号。然而,线性激活函数的问题是,它给出的输出是一个常数,因此不可能使用反向传播,因为它与输入没有关系。此外,它将所有层折叠成一个层,从而将神经网络变成一个单层网络。
  • 非线性激活函数:这些函数在不同层之间创建复杂的映射,有助于更好地学习数据建模。一些非线性激活函数是 sigmoid、tanh、softmax、ReLU(整流线性单元)、Leaky ReLU 等。

理论够了,开始吧。

开源代码库

如果您熟悉 Github,请查看我的代码和数据集存储库。

[## 使用 Pytorch 和卷积神经网络进行分类

使用 PyTorch 和 CNN 对老虎-鬣狗-猎豹进行分类这个项目是为我的中型博客制作的。一定要去看看…

github.com](https://github.com/Shubhankar07/Zoo-Classification-using-Pytorch-and-Convolutional-Neural-Networks)

克里斯里德在 Unsplash 上的照片

资料组

要训练一个模型,首要任务是找到一个数据集。对于该项目,使用的数据集是 Kaggle 原始数据集的修改版本:

原始数据集:【https://www.kaggle.com/c/swdl2020/overview

定制数据集:https://drive . Google . com/file/d/1 krqqs 2 hi 2 kagfgactdwqpquzo 6 rx 2 ve 6/view?usp =分享

数据集中有 3 个类对应于三种动物:老虎、鬣狗和猎豹。

作者图片

数据集已经分为两个文件夹,即培训和验证。训练文件夹包含 2700 幅图像,每幅 900 幅在不同的文件夹中,验证文件夹包含 300 幅图像,每幅 100 幅在对应于每只动物的不同文件夹中。

导入库

如果您尚未安装 PyTorch 库,请使用以下命令:

如果您在 Anaconda 上运行,那么一旦您进入虚拟环境,运行命令-

**conda install pytorch torchvision cudatoolkit=10.1 -c pytorch**

如果您想使用 pip 在本地安装它,以下命令将打开一个 wheel 文件供下载-

**pip install torch==1.5.0+cu101 torchvision==0.6.0+cu101 -f**

了解数据集

一旦我们导入了所需的库,让我们导入数据集并理解它。

正如我们所见,总共有 3 个类,每个代表一种特定的动物。 data_dir 是存储数据集的路径。如果在一个在线环境中工作,那么数据集要么需要上传到本地环境,要么保存到驱动器上,就像 Google Colab 的情况一样。如果您在本地环境中工作,请在此处插入本地数据集路径。

现在让我们把数据集转换成一个 N 维张量。

现在,可变数据集是一个包含所有用于训练的图像的张量。如上所述,数据集的大小为 2700。

我们现在将使用验证文件夹作为测试文件夹。测试数据集的大小是 300。

现在让我们检查数据集。

每个图像的形状为 3x400x400,图像尺寸为 400x400,3 表示颜色通道 RGB。

现在让我们显示数据集中的一些图像。

为训练准备数据集

我们现在将把训练集分成两部分进行训练和验证。

为此,我们将使用 random_split()函数。

让我们将批处理大小保持在 32 并加载数据。

我们来看看批次。

现在我们已经完成了准备工作,让我们来看看模型。

构建模型

在这里,我们使用交叉熵或对数损失作为损失函数。在多类分类的情况下,交叉熵公式为:

现在让我们检查一下 GPU 是否可用,如果可用就使用它。

现在已经选好 GPU 了。如果“cuda”不可用,那么如果您在本地工作,请检查您的设置。如果你在 Kaggle 上工作,那么确保没有其他 GPU 会话是活动的,并且你没有用完每月 30 小时的免费配额。

我们现在来定义卷积网络的各层。

我们已经定义了 3 个隐藏层。对于激活功能,我们使用 ReLU(校正线性单位)。

现在我们已经建立了一个基本模型,让我们尝试用各种学习率和时期来拟合它,并检查准确性。

现在让我们来分析这个模型。

让我们最后评估一下这个模型。

如你所见,准确率非常低,只有 50.9%左右。这是因为我们还没有给模型添加任何卷积层。

约书亚·索蒂诺在 Unsplash 上拍摄的照片

现在让我们使用 Resnet-18 构建卷积层。

Resnet 或残差网络是一种卷积神经网络,具有强大的表示能力,使其有可能训练多达数百甚至数千层,并仍然实现令人信服的性能。我们将使用 Resnet-18,它表示网络有 18 层深。

现在让我们定义卷积层。

我们现在将使用预训练的 Resnet-18 模型。

现在让我们看看输出形状。

检查 GPU 是否可用,并将其指定为设备。

确认设备类型为 GPU。

加载用于训练和测试的数据。

分配模型。

现在让我们检查初始损失和准确性。

最初的精确度是 35%。现在让我们设置超参数并开始训练模型。

Opt_func 代表优化器函数。它们通过响应损失函数的输出来更新模型,从而将损失函数和模型参数联系在一起。我们将要使用的函数是 Adam optimizer。Adam 是一种优化算法,可以用来代替经典的随机梯度下降过程,以基于训练数据迭代地更新网络权重。它实现了 AdaGrad(自适应梯度算法)和 RMSProp(均方根传播)的优点。

现在让我们来分析这个模型。

现在,让我们最后评估一下这个模型。

分析

正如我们所见,卷积层的应用有助于将精度提高到 89.5%。通过具有更大的训练数据集和进一步调整超参数,可以进一步提高精确度。

结论

我们已经成功地建立了一个卷积神经网络模型来对动物园的动物进行分类。类似的分类数据集相当多,可以通过了解来熟悉卷积神经网络、PyTorch 等概念。

Unsplash 上由 Tincho Franco 拍摄的照片

使用 Python 制作数据动画

原文:https://towardsdatascience.com/animate-data-using-python-dd558e9103d?source=collection_archive---------23-----------------------

仅使用 MatPlotLib 查看实时数据

本指南的目标是向您展示如何实时更新图表。这可以用于各种应用,如可视化来自传感器的实时数据或跟踪股票价格。

在本教程中,数据将是静态的,但可以很容易地用于动态数据。当然,您需要在本地机器或虚拟环境中安装 MatPlotlibPandas

照片由卢克·切瑟Unsplash 上拍摄

进口

FuncAnimation 是我们将用来持续更新图形的方法,为其提供实时效果。

我们将在本教程中使用 MatPlotLib 来生成我们的图形,并使用 panda 的库来读取 CSV 文件。

初始化子情节

我们将创建一个支线剧情,使我们更容易一遍又一遍地利用同一个情节。

创建动画方法

animation(i)方法是我们将用来一遍又一遍地画线的方法。这个方法会被 MatPlotLib 库中的FuncAnimation方法反复调用,直到你决定终止程序。

在这个例子中,我将使用一个从互联网上下载的静态文件。该文件为我们提供了苹果公司 2014 年的股价。

下面是我们的虚拟数据的样子:

现在,为了模拟实时数据,而不是一次性绘制整个图表,我将绘制每一行。这给了我们虚幻的真实数据。

记住animation(i)函数将在每次迭代 时被 调用。每调用一次i就加一。这一行,AAPL_STOCK[0:i]['AAPL_x'],将返回对应于我们正在进行的迭代的行。

例如,在迭代 1 中,我们将返回数据集中的第一行。在第 n 次迭代中,我们将返回数据集中的第 n 行(及其之前的所有行)。

要绘制到图形的线,我们只需要调用ax.plot(x,y)。我们也调用ax.clear()来清除之前生成的行。我们这样做是为了在每次迭代后不要在彼此上面画线。

为什么在循环中导入数据集?

您可能已经注意到,我们在循环中读取 CSV。我们需要这样做的原因是,如果新数据被添加到 CSV 中,我们可以读取它并将其绘制到我们的图表中。

如果我想使用文本文件呢?

如果你使用一个文本文件,你可以在每次迭代中给 x 和 y 追加值。例如:

运行动画

要运行动画,我们只需要使用下面的。

animation = FuncAnimation(fig, func=animation, interval=1000)plt.show()

这里,**FuncAnimation**将连续运行我们的动画程序。**interval**用于确定更新之间的延迟。目前,它被设置为 1000 毫秒,但它可以根据你的需要或快或慢。

这里是所有的代码:

截屏由作者拍摄

当新的价值加入其中时,它真的能工作吗?

因此,在这里,我只是从我们上面使用的 CSV 中删除了一些数据。

然后,一旦图形更新到数据集的末尾,我就复制一些新数据并保存文件。然后,该图表用我刚刚添加的所有新数据进行了更新。在这种情况下,它更新得相当快,但只是因为间隔时间被设置为100 毫秒

截屏由作者拍摄

如果你喜欢这个指南,你可以看看下面写的其他一些很酷的文章!如果你有任何问题,请留下你的评论,我会尽力帮助你。

[## 这个故事完全是由一个人工智能写的

Grover 可以产生假新闻的人工智能模型

towardsdatascience.com](/this-story-was-completely-written-by-an-ai-cb31ee22feae) [## 数据可视化:动画条形图!

Excel 和 After Effects 中的数据可视化和动画

towardsdatascience.com](/data-visualization-animate-bar-graphs-df9ca03a09f2) [## 这个建议可以拯救数百万人

降低你的胆固醇,这简直是要了你的命

medium.com](https://medium.com/in-fitness-and-in-health/this-advice-could-save-millions-of-people-6e318db330ad) [## 科学证据:作者应该知道什么

并非所有的证据都是平等的。

medium.com](https://medium.com/2-minute-madness/scientific-evidence-what-writers-ought-to-know-724eb7145e09) [## 疫情期间,创新蓬勃发展

科技如何帮助人类对付致命的疫情病毒。

medium.com](https://medium.com/workhouse/innovation-is-thriving-during-the-pandemic-531fe6975a61)

通过 4 个简单的步骤用 Python 制作动画!

原文:https://towardsdatascience.com/animate-your-graphs-in-python-in-4-easy-steps-243dccad9a7?source=collection_archive---------21-----------------------

利用新冠肺炎数据实现数据可视化的创意

我们开始吧!资料来源:Nik Piepenbreier

在之前的一篇文章中,我们探索了如何用几行代码制作漂亮的 Matplotlib 图。你可以通过访问这个链接来查看这个帖子,作为如何将这些图表放在一起的快速复习。

我们将研究 COVID 数据,并绘制出自 2020 年 3 月 1 日以来每 100,000 人的比率。我们筛选出截至 2020 年 4 月 10 日受影响最大的五个州(按每 100,000 人的比率),以及华盛顿和加利福尼亚州,因为它们的早期病例数。

让我们看看我们在组装什么!

我们的最终产品。资料来源:Nik Piepenbreier

第一步:让我们载入数据

我们将开始分析,加载我们分析所需的库,并生成一些数据帧来存储我们的数据:

我们的第一行代码:导入库和数据!资料来源:Nik Piepenbreier

让我们更详细地分解我们的代码:

在第 1 节中,我们导入了几个库。我们使用 Pandas 来保存数据帧中的数据以及生成基本的绘图。我们使用 matplotlib 来生成实际的图形。我们使用 glob 获取文件名,然后创建 GIF。最后,我们使用 moviepy 来生成 GIF。

如果没有安装这些库,可以使用 pip 来安装。默认情况下会安装 Matplotlib,因此我们不将它包含在下面的列表中:

pip install pandas
pip install glob
pip install moviepy

在第 2 节的中,我们生成两个数据帧。我们从纽约时报 GitHub 获取数据,这个 GitHub 按州收集新冠肺炎病例的数据到一个名为 df 的数据框架中。我们还从人口普查网站获取各州的人口数据。

最后,在第 3 节中,我们将人口数据框架中的数据合并到主数据框架 df 中。如果你想了解更多这方面的知识,看看这个教程

我们还通过将病例数除以该州人口并乘以 100,000,得出每 100,000 人中的新冠肺炎病例率。这被分配给一个叫做 rate 的变量。这类似于我们的另一篇关于用 Matplotlib 漂亮地可视化新冠肺炎数据的文章:

[## 用 Python 漂亮地可视化新冠肺炎数据(不到 5 分钟!!)

让 Matplotlib 不那么痛苦!

towardsdatascience.com](/visualizing-covid-19-data-beautifully-in-python-in-5-minutes-or-less-affc361b2c6a)

第二步:准备我们的数据

我们的数据现在包括所有州的数据。这将产生一个非常混乱的视觉效果。因此,我们希望将我们的分析限制在更小、更易管理的状态数量上。我们将在 2020 年 4 月 10 日选出人均收入最高的州。我们还将把华盛顿和加利福尼亚包括进来,因为他们有早期的高病例数:

为制图准备数据。资料来源:Nik Piepenbreier

第 4 节中,我们生成了一个我们感兴趣的州列表。我们创建一个只包含昨天数据的新数据帧。然后,我们生成了当天人均收入最高的州的列表(按降序排列,只选择前五名)。最后,我们追加华盛顿和加利福尼亚。

然后,在第 5 节中,我们将我们的原始数据帧 df 限制为仅包括所标识的州。我们将数据范围限制为仅包括 2020 年 3 月 1 日以后的数据(因为在此日期之前病例报告率极低/偏低)。最后,我们透视数据集,使其可以绘制成图表。

在第 6 节的中,我们重置了多索引数据帧的索引,以允许绘图。我们重置了索引并删除了日期列。这似乎有点争议,但我发现将数据显示为“# of days from March 1,2020”更容易理解,特别是考虑到我们希望在数据中包含的运动。

我们的四个简单步骤!资料来源:Nik Piepenbreier

第三步:绘制数据图表

我们现在已经准备好了绘制图表的数据。下面的代码迭代创建许多 png 图像,我们将把这些图像拼接在一起,创建我们的 GIF:

绘制我们的数据。资料来源:Nik Piepenbreier

在第 7 节中,我们完成了几件事:

  • 我们将样式设置为匹配 538 主题,
  • 我们测量数据帧的长度,并将其赋给一个名为 length 的变量。我们给这个值加 10。这使得默认情况下输出文件名更容易排序(而不是从 1 开始)。
  • 我们使用 for 循环为每个日期生成单独的可视化效果。如果你想学习更多关于 for-loops 的知识,看看这个指南,它会教你你需要知道的一切。
  • 如果您一直在跟进,请确保更改最后一行,以反映存储图形的文件夹的路径。要使 GIF 正常工作,请将其创建为不包含任何其他内容的新文件夹。

步骤 4:创建我们的可视化动画

现在我们到了激动人心的时刻!您现在已经创建了一个装满 png 的文件夹,我们正准备将它们转换成 GIF。

生成我们的 GIF。资料来源:Nik Piepenbreier

在第 8 节中,我们主要做两件事:

  • 我们使用 glob 生成所有 png 的列表。为此,在代码的第 4 行,更新 PNG 文件夹的路径,确保在代码中保留/*。这将从该文件夹中取出所有文件。
  • 我们使用 moviepy 来生成 GIF。GIF 将以每秒 6 帧的速度运行,并将存储在您的用户目录中。如果找不到它,请在计算机上搜索 COVID.gif。

庆祝我们的最终产品!

让我们检查一下我们的最终作品,然后花点时间整理一下自己。在很短的时间内,我们创造了一个美丽的动画,非常容易地展示了新冠肺炎的怪异和令人羞愧的传播。

让我们庆祝一下!资料来源:Nik Piepenbreier

结论:用 Python 创建动画数据可视化

感谢您抽出时间!资料来源:Nik Piepenbreier

非常感谢你阅读这篇文章!在这篇文章中,我们学习了如何将数据帧合并在一起,用 for 循环创建多个可视化效果,并将这些可视化效果转换成 GIF。

我希望你能学到一些新东西,找到一些创造性的方法来展示你的数据!

用人工智能让你自己成为一个迪斯尼角色

原文:https://towardsdatascience.com/animating-yourself-as-a-disney-character-with-ai-78af337d4081?source=collection_archive---------14-----------------------

先睹为快数字艺术的未来

【作者生成的图片】【CC 许可下来源于维基百科的名人图片: Scarlet J.Benedict C 。,将 S 。、 Emma W.Elon M. 【奥巴马视频来源于 VoxCeleb 数据集,CC BY 4.0】

上周,我在网上冲浪,无意中发现了贾斯汀·平克尼写的一篇有趣的文章。有什么好玩的?有人用这个想法做了一个很酷的应用!多伦·阿德勒(Doron Adler)用迪士尼人物对 StyleGAN2 模型进行了微调,然后将这些层与真实人脸模型(FFHQ)混合,并根据真实人脸生成迪士尼人物。

根据 CC 许可,图片来源于维基百科:斯嘉丽·约翰逊本尼迪克特·康伯巴奇威尔·史密斯。作者生成的动画图像。

然后,贾斯汀·平克尼接着在上发布了这个模型,以此来统一。你只需上传你的照片,就能立刻得到你的迪士尼角色。试试吧!

但是,在这篇文章中,我们将学习如何以编程的方式来实现这一点,并制作角色动画!

埃隆·马斯克的动画角色(为 GIF 优化而压缩)[图片由作者生成][埃隆·马斯克图片来源于 Flickr 由特斯拉车主俱乐部比利时,CC 由 2.0 许可][奥巴马视频来源于 VoxCeleb 数据集,CC 由 4.0]

目录

  1. 开始
  2. StyleGAN
  3. 图层交换的意义
  4. 用一阶运动制作动画
  5. 辅导的

如果您不想学习理论并直接学习编码,您可以跳到教程。

生成对抗网络

这种角色生成背后的核心机制是一个称为生成性对抗网络(GAN)的概念,由于其生成性应用,该概念目前在社区中非常流行。甘是什么?这基本上是两个网络试图相互竞争,发电机和鉴别器。生成器试图欺骗鉴别器,使其相信其生成的图像是真实的,而鉴别器试图在真实图像和假(生成的)图像之间进行分类。

甘建筑【来源:作者用 StyleGAN2 生成动漫人物】

首先通过向鉴别器显示来自数据集的真实图像和随机噪声(来自未训练的生成器的图像)来训练鉴别器。由于数据分布非常不同,鉴别器将能够容易地进行区分。

初始鉴别器训练[图片由作者提供][面部图像由thispersondoesnotexist.com生成]

免责声明:由于我试图尽可能简化,该图可能无法准确反映 GAN 的情况。

然后,我们将切换到训练发电机,同时冻结鉴别器。生成器将学习如何基于鉴别器的输出(真的或假的)生成更好的图像,直到鉴别器不能再正确鉴别为止。然后,我们切换回训练鉴别器,循环继续,两者都变得更好,直到生成器达到生成非常真实的图像的点,您可以停止训练。

GAN 培训概述。生成器将学习生成更好的图像,鉴别器将学习更好地分类,因为假图像开始看起来非常相似。最终,它会达到一个点,图像非常相似,而鉴别器只有一半的时间是正确的。[图片由作者提供][面部图片由thispersondoesnotexist.com生成]

StyleGAN

2018 年,NVIDIA 发表了一篇突破性的论文,该论文管理生成高质量的图像(1024x1024),题为“基于风格的生成式对抗性网络生成器架构”。其中一个新颖之处是,它解开了潜在的空间,使我们能够在不同的水平上控制属性。例如,较低层将能够控制姿势和头部形状,而较高层控制诸如照明或纹理的高级特征。

通过引入额外的映射网络来完成解缠结,该映射网络将输入(从正态分布采样的噪声/随机向量)映射到分离的向量 w 并将其馈送到层的不同级别。因此,z 输入的每个部分控制不同级别的特征

StyleGAN 生成器架构【来源:作者用 StyleGAN2 生成动漫角色】

因此,如果我们改变较低层(4x4,8x8)的输入,我们将会有高级特征的变化,例如头型、发型和姿势。

粗糙层次细节的变化(头型、发型、姿势、眼镜)[来源:
分析和改善 StyleGAN 的图像质量

另一方面,如果您更改较高层(512x512,1024x1024)的输入,我们将在更精细的功能方面有所变化,如照明、肤色和头发颜色。

精细层次细节(头发颜色)的变化【来源:
分析和改善 StyleGAN 的图像质量

我们可以尝试通过分析激活图来进一步可视化解缠结,并像本文所做的那样对激活进行聚类。

使用球形 K-均值聚类的 StyleGAN2 层分析显示了 StyleGAN 语义的解开。每种颜色代表一个不同的集群[ 来源:时尚编辑:揭示甘斯的地方语义

颜色代表一个集群,你可以把它看作是图像的可控部分。在最后一层,你可以看到照明的不同部分表现为不同的集群。在中间层中,诸如眼睛、鼻子或嘴的面部特征被表示为不同的聚类,这意味着这是面部特征的变化被控制的地方。最后,在前几层中,头部的不同部分被表示为不同的簇,这证明它控制着人的形状、姿势和发型。

你可以在这里观看这篇论文的演示视频。

* [## 时尚编辑:揭示 GANs - Crossminds 的本地语义

作者:Edo Collins,Raja Bala,Bob Price,Sabine Süsstrunk 描述:虽然 GAN 图像合成的质量已经…

crossminds.ai](https://crossminds.ai/video/5f6e7419d81cf36f1a8e31e0/)

除了解开,StyleGAN 还做了其他几个改进,如渐进增长架构。虽然他们在 StyleGAN2 中换成了类似 MSG-GAN 的架构。关于最新的更新,你可以阅读这篇文章或者观看下面的 StyleGAN2 演示视频。

[## StyleGAN - Crossminds 图像质量的分析与改进

作者:Tero Karras,Samuli Laine,Miika Aittala,Janne Hellsten,Jaakko Lehtinen,Timo Aila

crossminds.ai](https://crossminds.ai/video/5f6e71a6d81cf36f1a8e30ee/)

我也用这个网站来跟踪今年关于 CVPR 和 ECCV 的新论文。所以,还是挺有用的。

StyleGAN 网络混合

StyleGAN 独特的“解开缠绕”功能使我们能够混合不同的模型,并从一张脸上生成迪士尼角色。如果前几层控制面部特征,最后几层控制纹理,如果我们把最后几层和另一个模特的层互换会怎么样?

例如,如果我们将人脸模型的权重用于前几层,将绘画模型的权重用于层的其余部分,它将生成具有绘画风格的人脸!此外,它不仅可以复制第二个模型的纹理,而且还可以复制不同模型的面部特征风格,如迪士尼人物的眼睛或嘴巴。

FFHQ 模型和 MetFaces 模型在各层和 16x16 层的网络融合[图像由作者生成][使用的人脸是生成的,人不存在]

一阶运动模型

在我们创造了迪斯尼角色之后,为什么不把它带到另一个层次,并制作出动画呢?一篇名为“图像动画的一阶运动模型”的有趣论文为我们提供了这样的能力。基本上,它试图使用关键点从驾驶视频中学习运动,并试图变形输入图像来实现运动。

一阶运动模型概述 [来源:图像动画一阶运动模型,CC BY 4.0]

时装模特的一阶运动模型。请注意,它能够制作模型背面的动画。【来源:图像动画一阶运动模型,CC BY 4.0】

辅导的

现在我们已经对这个概念有了一点了解,让我们开始写代码吧!幸运的是,贾斯汀·平克尼提供了他的卡通化模型,并为它创建了一个实验室。我做了另一个 Colab 笔记本,基本上是他的代码,并添加了从 Aliaksandr Siarohin 的笔记本修改而来的动画代码。

这里有 Colab 笔记本供你跟随!

首先,确保你用的是 GPU 运行时和 Tensorflow 1。

%tensorflow_version 1.x

注意: %

接下来,我们克隆回购并创建我们将使用的文件夹。

!git clone [https://github.com/justinpinkney/stylegan2](https://github.com/justinpinkney/stylegan2)
%cd stylegan2
!nvcc test_nvcc.cu -o test_nvcc -run
!mkdir raw
!mkdir aligned
!mkdir generate

注: '!'用于在 Colab 中运行 shell 命令,如果您在本地执行此操作,只需在您的 shell/控制台上运行该命令。

接下来,上传你的图像到 raw 文件夹,我们将使用一个脚本来裁剪面部和调整图像的大小,因此你的图像不必是全脸。但为了获得更好的效果,请确保您的面部分辨率至少为 256x256。

在这个例子中,我们将使用 Elon Musk 图像作为例子。

!wget [https://drive.google.com/uc?id=1ZwjotR2QWSS8jaJ12Xj00tXfM0V_nD3c](https://drive.google.com/uc?id=1ZwjotR2QWSS8jaJ12Xj00tXfM0V_nD3c) -O raw/example.jpg

埃隆·马斯克图片【来源:Flickr by 特斯拉车主俱乐部比利时,CC BY 2.0】

然后,我们将加载由多伦的阿德勒和正常的 FFHQ 人脸模型的真实人脸和迪士尼人物的混合模型。

为什么我们还需要加载正常的真实人脸(FFHQ)模型?请记住,StyleGAN 模型仅采用潜在向量 z,并基于潜在向量生成人脸。它不像图像到图像转换模型那样获取图像并转换图像。

那么我们如何生成一张我们想要的脸呢?StyleGAN2 介绍了一种投射到潜在空间的方法。基本上,我们可以尝试使用梯度下降为我们想要的图像找到匹配的潜在向量。

但在我们试图找到匹配的潜在向量之前,我们需要首先裁剪和对齐图像。

!python align_images.py raw aligned

该脚本将源图像目录和输出目录作为输入,并将正确地裁剪和对齐我们的脸。

被裁剪和对齐的脸[来源:Flickr,作者:比利时特斯拉车主俱乐部

最后,我们将图像投影到潜在空间。

!python project_images.py --num-steps 500 aligned generated

该脚本将获取aligned目录中的图像,并在generated文件夹中创建保存为.npy文件的潜在向量。

现在我们有了潜在向量,我们可以尝试使用我们的混合迪士尼模型来生成人脸。

生成的图像保存在generated文件夹中。我们可以显示笔记本里面的图像。

埃隆·马斯克卡通化[图片由作者生成]

瞧啊。我们有一个迪士尼化的埃隆·马斯克,但我们还没有完成。让我们来制作动画吧!

首先,我们在一阶模型上克隆 Aliaksanr 的回购。

!git clone https://github.com/AliaksandrSiarohin/first-order-model

然后,我们将设置一个路径,这样我们就不必在一阶模型目录下进行 python 导入,或者你可以直接cd到这个目录。

然后,在加载关键点和视频生成器模型之前,我们需要先下载预先训练好的权重。该文件相当大~700 MB,您可能需要手动下载,因为 Google 不允许使用 wget 下载大文件。

!wget "https://drive.google.com/uc?export=download&id=1jmcn19-c3p8mf39aYNXUhdMqzqDYZhQ_" -O vox-cpk.pth.tar

使用刚才下载的重量加载一阶模型。

接下来,我们需要一个驾驶视频,我们将从那里获得动画。您可以使用示例视频或上传自己的视频。如果您上传视频,请确保相应地更改文件路径。

!wget https://drive.google.com/uc?id=1LjDoFmeP0hZQSsUmnou0UbQJJzQ8rMLR -O src_video.mp4

最后,我们可以生成动画!

[作者生成的图像]来自 [VoxCeleb 数据集的中间视频]

耶!我们终于让我们的角色有了生气。祝贺你,如果你设法达到这一点🎉

下一步是什么?

我们还有很多东西可以做实验。如果我们混合其他模型,如绘画的模型,或者我们也可以反向混合迪士尼人物和绘画,我们基于迪士尼人物或绘画生成一个真实的脸。我们也可以尝试加入 Deepfake,用我们的迪士尼角色替换迪士尼电影中的面孔。

涂脂抹粉的脸的动画【图片由作者生成】【名人图片来源于 CC 许可下的维基百科图片: Scarlet J.Benedict C 。,将 S 。,艾玛 W.埃隆 M. ]

如果你喜欢我的作品,看看我的其他文章!

[## 使用 StyleGAN2 生成动画角色

了解如何生成这个很酷的动画人脸插值

towardsdatascience.com](/generating-anime-characters-with-stylegan2-6f8ae59e237b) [## 如何使用自定义数据集训练 StyleGAN2-ADA

了解如何训练人工智能生成您想要的图像

towardsdatascience.com](/how-to-train-stylegan2-ada-with-custom-dataset-dc268ff70544)

也可以在 Linkedin 上和我联系。

[## Muhammad Fathy Rashad -计算机视觉和深度学习 R & D 实习生- ViTrox 公司…

在我 16 岁的时候,我以最年轻的学生的身份开始了我的学业,并出版了 2 款总安装量超过 1.5K 的手机游戏…

www.linkedin.com](https://www.linkedin.com/in/mfathyrashad/)

参考

[1]t .卡拉斯、s .莱恩和 t .艾拉(2019 年)。一种基于风格的生成对抗网络生成器体系结构。在IEEE 计算机视觉和模式识别会议论文集(第 4401–4410 页)。

[2]t . Karras,Laine,s .,Aittala,m .,Hellsten,j .,Lehtinen,j .,& Aila,T. (2020 年)。stylegan 图像质量的分析与改进。在IEEE/CVF 计算机视觉和模式识别会议论文集(第 8110–8119 页)。

[3]Siarohin,a .,Lathuilière,s .,Tulyakov,s .,Ricci,e .,& Sebe,N. (2019 年)。图像动画的一阶运动模型。在神经信息处理系统的进展(第 7137-7147 页)。

[4]柯林斯,e .,巴拉,r .,普莱斯,b .,& Susstrunk,S. (2020 年)。风格编辑:揭示 GANs 的本地语义。在IEEE/CVF 计算机视觉和模式识别会议论文集(第 5771–5780 页)。

https://www.justinpinkney.com/stylegan-network-blending/*

用 Python 制作多元线性回归动画

原文:https://towardsdatascience.com/animations-of-multiple-linear-regression-with-python-73010a4d7b11?source=collection_archive---------32-----------------------

在这篇文章中,我们的目标是扩展我们的能力,可视化梯度下降到多元线性回归。这是《渐变下降动画》的后续文章:1。简单线性回归”。正如我们之前所做的,我们的目标是建立一个模型,使用批量梯度下降将模型拟合到我们的训练数据,同时存储每个时期的参数值。之后,我们可以使用我们存储的数据,通过 Python 的赛璐珞模块来创建动画。

这是我在 7 月 20 日上传的一篇关于同一主题的文章的修订版。主要改进包括封面照片和一些动画。

建立模型

多元线性回归是简单线性回归的扩展版本,其中多个预测变量 X 用于预测单个因变量 Y 。对于 n 个预测变量,这可以用数学方法表示为

随着

b 代表我们回归平面的 y 截距(“偏差”)。我们的目标是找到使训练数据点和超平面之间的均方距离最小化的超平面。梯度下降使我们能够确定我们的模型参数 θ 的最佳值,包括我们的权重 w 和我们的偏差项 b ,以最小化观测数据点 y 和我们用回归模型预测的数据点【ŷ).在训练期间,我们的目标是根据以下公式更新我们的参数值,直到我们达到收敛:

其中∇ J(θ) 表示我们的成本函数 J 相对于我们的模型参数 θ的梯度。学习率用 α 表示。每个权重的偏导数和偏差项与我们的简单线性回归模型中的相同。然而,这一次,我们想要建立一个(多元)线性回归模型,它对预测变量的数量是灵活的,并引入一个权重矩阵来同时调整所有权重。此外,我们在拟合过程中直接将参数值存储在数组中,这在计算上比我们在上一篇文章中使用 for 循环和列表要快。我决定将权重的初始参数值设置为 3,将偏差的初始参数值设置为-1。在 Python 中,我们导入一些库并建立我们的模型:

之后,我们打算让我们的模型适应一些任意的训练数据。虽然我们的模型理论上可以处理任何数量的预测变量,但我选择了一个具有两个预测变量的训练数据集。我们有意使用一个特别小的学习率, α =0.001,以避免动画中过大的步长。由于本文关注的是动画而不是统计推断,我们简单地忽略了线性回归的假设(例如,不存在多重共线性等。)目前来说。

为了确保我们拟合的参数收敛到它们的真实值,我们使用 sklearn 的先天线性回归模型来验证我们的结果:

现在,我们终于可以创作出我们的第一部动画了。像以前一样,我们希望从可视化值开始,我们的成本函数和参数在绘制 3-D 中的相应回归平面时相对于时期呈现。如前所述,我们打算只绘制选定时期的值,因为最大的步长通常在梯度下降开始时观察到。在每个 for 循环之后,我们对我们的绘图进行快照。通过相机的动画功能,我们可以将快照变成动画。为了获得所需的回归平面,我们通过 numpy.meshgrid 引入一个坐标网格(M1,M2),并定义一个函数“pred_meshgrid()”来计算在某个时期与模型参数相关的相应 z 值。下面动画中看到的虚连接线可以通过训练数据点和预测点之间的线图获得。通过返回最终的参数值(参见注释掉的代码!)我们在我们的动画中获得,我们确保我们粗略地可视化了模型收敛,尽管没有使用我们在拟合过程中存储的参数值的全部范围。

尽管很简单,但分别绘制每个时期的参数值实际上是可视化具有两个以上模型参数的梯度下降的最具信息量和最现实的方式。这是因为我们只能以这种方式同时见证成本和所有模型参数的变化。

固定截距模型:

当创建更复杂的渐变下降动画时,特别是在 3d 中,我们必须关注两个模型参数,同时保持第三个参数“固定”。我们通常对多重线性回归模型的权重比偏差项更感兴趣。为了直观显示成本如何随着我们调整后的权重(w₀、w₁)稳步下降,我们必须建立一个新的线性回归模型,其中偏差项 b 是固定的。这可以通过将 b 的初始值设置为预定义值 b_fixed 并删除新模型中更新 b 的代码部分来轻松完成。 b_fixed 可以取任何值。在这种情况下,我们只是将它设置为前一个模型收敛到的 y 截距值:

在用我们的新模型积累新数据后,我们再次为下面的动画创建另一个坐标网格(N1,N2)。这个坐标网格使我们能够在给定的数字范围内,为每一对可能的 w₀和 w₁绘制成本图。

等值线图表

等高线图允许我们通过等高线和填充等高线在二维平面上可视化三维表面。在我们的例子中,我们希望将 w₀和 w₁分别绘制在 x 轴和 y 轴上,成本 J 作为等高线。我们可以用 Matplotlib 的轮廓函数标记图形中的特定轮廓级别。因为我们知道我们的最终成本大约是 76,我们可以设置我们的最后轮廓水平为 80。

当 y 轴截距允许变化时,我们看到了参数值和成本随后期的显著变化,这是我们不想在回归和参数图中遗漏的。然而,随着 b 被固定,大部分“动作”似乎被限制在前 400 个纪元。因此,我们将我们打算在下面的动画中可视化的时期限制为 400,这在计算上花费较少,并且产生更吸引人的动画。为了证实这种印象,我们可以将最终参数值(100,000 个时期后返回的固定截距模型)与 400 个时期后获得的参数值进行比较(参见下面注释掉的代码!).因为参数值和成本值通常是匹配的,尽管限制了被可视化的时期的数量,我们还是可视化了模型收敛,这是公平的。

动画回归平面和等高线图()

表面图

对于最后一个图,我们希望包含与等高线图相同的时期。然而,这一次,我们希望使用曲面图在三维空间中可视化梯度下降。

动画回归平面和曲面图()

理论上,在 x-y 平面上绘制梯度下降的轨迹——就像我们绘制等高线图一样——对应于梯度下降的“真实”轨迹。与上面的曲面图相反,梯度下降实际上根本不涉及 z 方向的移动,因为只有参数可以自由变化。更深入的解释见此处。最后,我想指出用赛璐珞制作动画是非常耗时的。尤其是涉及曲面出图的动画可能需要 40 分钟才能返回所需的结果。尽管如此,我还是更喜欢赛璐珞,因为它简单明了。一如既往,创造性的投入和建设性的批评是赞赏!

我希望这篇文章对你有所帮助。如果有任何问题或者你发现了任何错误,请随时发表评论。在本系列的下一篇文章中,我将致力于以逻辑回归为例的梯度下降动画。完整的笔记本可以在我的 GitHub 上找到。

感谢您的关注!

参考文献:

[## 深度学习中的优化介绍:梯度下降

图片来源:奥莱利媒体深度学习,在很大程度上,真的是关于解决大规模讨厌的优化…

blog.paperspace.com](https://blog.paperspace.com/intro-to-optimization-in-deep-learning-gradient-descent/amp/) [## 线性回归

在统计学中,线性回归是一种建模标量响应(或变量)之间关系的线性方法

en.wikipedia.org](https://en.wikipedia.org/wiki/Linear_regression#Simple_and_multiple_linear_regression)

附录:

缩小版(特色图片)

神经网络转换数据的动画

原文:https://towardsdatascience.com/animations-of-neural-networks-transforming-data-42005e8fffd9?source=collection_archive---------22-----------------------

我们可以对神经网络为什么以及如何工作有更好的直觉

谈到分类算法,对它们如何工作的解释可以是直观的:

  • 逻辑回归SVM 找到一个超平面将空间“切割”成两个。(但是这两种算法的途径不一样,所以最终的超平面也不一样。)
  • 如果数据不是线性可分的,使用内核技巧SVM 将数据转换到一个更高维度的空间,然后将其“切割”成两个。
  • 决策树将数据分组到超矩形中,超矩形将包含一个类的大部分。
  • K 最近邻分析新观测值的邻居来预测该观测值的类别。

那么神经网络呢?

隐藏层的变换

我们举个简单的数据集例子:两个类,两个特征 x1 和 x2,数据不线性可分

import numpy as npnp.random.seed(1)x1=np.concatenate((np.random.normal(0.5,0.1,100),
np.random.normal(0.2,0.05,50),
np.random.normal(0.8,0.05,50))).reshape(-1,1)x2=np.concatenate((np.random.normal(0.5,0.2,100),
np.random.normal(0.5,0.2,50),
np.random.normal(0.5,0.2,50))).reshape(-1,1)X=np.hstack((x1,x2))y=np.concatenate((np.repeat(0,100),np.repeat(1,100)))

可视化如下,数据也是标准化的。

from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
X=scaler.fit_transform(X)plt.scatter(X[:, 0], X[:, 1], c=y,s=20)
plt.axis('equal')

对于该数据集,神经网络最简单的合适结构是具有两个神经元的一个隐藏层的结构。如果你看不出为什么,那就继续,你会直观地理解。

我们也可以认为激活函数是 sigmoid 函数

from sklearn.neural_network import MLPClassifierclf = MLPClassifier(solver=’lbfgs’,hidden_layer_sizes=(2,),
activation=”logistic”,max_iter=1000)clf.fit(X, y)

初始数据通过隐藏层进行转换。由于隐藏层有两个神经元,我们可以在 2D 平面上可视化这两个神经元的输出。

因此,初始 2D 数据集被转换到另一个 2D 空间。

我们可以用相应的权重计算隐藏层 A1 的值:

A1=1/(1+np.exp(-(X@clf.coefs_[0]+clf.intercepts_[0])))

然后我们可以将 A1 层可视化:

fig, ax = plt.subplots(figsize=(5,5))
ax.scatter(A1[:, 0], A1[:, 1], c=y,s=30)
ax.set_xlim(-0.1, 1.1)
ax.set_ylim(-0.1, 1.1)

现在,如何将初始数据转换成这些值?我们可以将这个过程动画化:

  • 一开始,我们有原始数据集(记住,它不是线性可分的)
  • 最后,我们将在(0,1)方块中得到转换后的数据,因为 sigmoid 函数的输出介于 0 和 1 之间。你看到数据的形式了吗?是的,这就是奇迹发生的时候…

输出神经元的性质

我们可以看到最后,数据集变成线性可分!对于二元分类,输出神经元是一个逻辑回归。并且很容易找到线性决策边界。在这种情况下,超平面分隔符将是一条直线。

因此,神经网络背后的直觉是,隐藏层将非线性可分离的初始数据转换到它们几乎线性可分离的空间。

神经网络的整体结构

结合隐藏层和输出层,我们有一个表面,我们可以在下面可视化。它是一个曲面,因为输入有两个变量,输出是概率,所以我们可以用 z 轴来表示。

现在我们可以看到,这两个神经元“切割”了两个类之间的两个边界。

稍微复杂一点的数据

现在让我们考虑下面的数据集。一个类在另一个类里面,有一个循环的形式。在使用神经网络之前,哪些算法可以解决这个问题?

  • 二次判别分析是合适的候选,因为两类协方差矩阵不同,中心相同。决策边界将是一个几乎完美的圆,以区分黄色点和紫色点。
  • 带 RBF (径向基函数)的 SVM 也会很完美。
  • 一个神经网络怎么样?两个神经元意味着你只“切”两次,这里我们至少需要三个。

最终神经网络表示

我们可以首先看到下面的最终表面,决策边界实际上是一种三角形。

隐藏层的变换

初始的 2D 空间被转换成具有 3 个神经元的 3D 空间。我们可以让这种转变生动起来。

  • 一开始,数据集在一个平面上。我们将认为 z 轴的值为零。
  • 最后,数据集被映射到(0,1)立方体中,因为 sigmoid 函数的输出介于 0 和 1 之间。

现在你能看到数据集在这个立方体中几乎是线性可分的吗?让我们通过旋转立方体来创建另一个动画。现在你看到了,用一架飞机,我们可以很容易地把黄色的点和紫色的点分开?

为了理解算法是如何工作的,我喜欢用简单的数据创建可视化。如果你也觉得它们有帮助,请评论。

这些情节首先是用 python 创建的,然后我用 gifmaker 制作动画。

人工智能电子动画——带来难以想象的结果

原文:https://towardsdatascience.com/animatronics-with-artificial-intelligence-brings-unimaginable-results-407983acf39?source=collection_archive---------24-----------------------

当前创新背景下电子动画的重要性——人工智能

来自 Unsplash 的照片

动画+电子=动画电子学

小时候,我总是喜欢有机器人的电影。尤其是那些穿得像人类的。我一直在想,如果我们可以用电子动画制作出有生命的物体,并赋予它们人工智能的能力,让它们像人或动物一样思考和行动,那不是很酷吗?

在这篇文章中,我将概述 Animatronics,并讨论它与人工智能的合作,以使机器人看起来更好,并提供良好的用户体验。

遇见一个由制作的电子动画特效

什么是电子动画?

动画电子学是动画和电子学的混合体。它可以预先定制(编程)或远程控制。动画可能执行开发,或者他们可能是不可思议的适应性。这是利用链接拉动小工具或驱动器,以加快人类或生物的繁殖或携带类似的属性,在任何情况下,无生命的东西。

这个概念最初是为迪士尼工作室开发的,用于 1954 年的电影。

我知道这很难接受。因此,简单地说,Animatronic 是一个展示人类或动物特征的自动化人体模型。一些特征包括用腿行走、眼球运动和像蛇一样爬行。某些动作和特征是使用伺服电机开发的(以控制运动、角位置)。

照片由埃里克·库尔Unsplash 拍摄

电子动作融合了生物(包括恐龙)、植物,甚至传说中的动物。一个旨在令人信服地模仿人类的机器人被更具体地称为机器人。

这是一个多学科领域,融合了生命系统、机器人、机电一体化和木偶戏带来的确切活动。它们覆盖着由坚硬而敏感的塑料材料制成的身体外壳和多功能皮肤,并以色调、头发和羽毛等微妙之处和各种部件完成,使人物逐渐变得可感知和真实。

电子动画的开发和设计(使用 Canva 设计)

但是它的许多应用被限制在娱乐领域。当与人工智能集成时,该概念可用于卫生、教育和军事行业。

[## 迪士尼正在开发与公园游客互动的人工智能电子动画

电视节目《幻想工程的故事》和凯文·p·拉弗蒂的书《魔法之旅》展示了学术研究的进展…

www.newscientist.com](https://www.newscientist.com/article/mg24632881-000-disney-is-working-on-ai-animatronics-that-interact-with-park-guests/)

在这个领域有大量的工作在进行,但许多人对电子动画知之甚少。让我们来看几个可以与 Animatronics 成为好伙伴的应用或行业。

与人工智能的集成

电子动画和人工智能可以解决许多问题。电子动画与人工智能的结合产生了机器人,就像人们所熟知的模仿人类行为的机器人一样。我们拥有所有我们需要的设备,从电子动物中挑选外观,从人工智能中挑选像生物一样的思维能力。

照片由 MaximalfocusUnsplash

我们正在适应机器人。我们有一种方法可以把生物的外表和行为赋予机器。我们正在使机器人人性化。

迪士尼协会将使用动画电子学和人为思维来建立他们的一个角色,在现实中:帕斯卡,电影中的角色之一。

* [## 照片:可爱的 Pascal Animatronic 正在为即将到来的东京迪士尼海洋“纠结”船开发…

东方置地有限公司于 2018 年 6 月宣布,已与华特·迪士尼公司达成协议…

wdwnt.com](https://wdwnt.com/2019/09/photos-adorable-pascal-animatronic-under-development-for-upcoming-tokyo-disneysea-tangled-boat-ride-attraction-revealed/)

安奇·科兹莫

科兹莫是一个有特殊头脑的天才小家伙。他会逼你玩,让你持续震惊。他是一个真实的机器人,就像你最近在电影中看到的那样,有一个独一无二的角色,你和他在一起的时间越长,他就越有进步。

安奇·科兹莫(来源— 安奇)

科兹莫是你的伙伴在一个疯狂的乐趣比例。他的能力包括面部识别、物体检测、与环境的互动以及随着访问应用程序的激励而不断发展的智能,以提供更好的方式来处理学习和娱乐。

视频由数字梦想实验室

这个著名的机器人被用来帮助孩子们开始有趣地学习复杂的东西。

SPOT —波士顿动力公司

波士顿动力公司是一家美国结构和应用自治安排协会,成立于 1992 年,是麻省理工学院的副业。这是对日本混合软银集团的一种肯定的强化。

它在多功能机器人方面处于世界领先地位,毫无疑问,它解决了最困难的机械自治难题。波士顿动力公司有一个非凡的和迅速创造的特殊的计划者和专家的聚会,他们可靠地得到先进的论证推导和严肃的结构。

波士顿动力公司的视频

SPOT、****T5【移动机器人】专为传感、检测和远程操作而设计。波士顿动力公司宣布引入 Spot 的交易,这是一种可以轻松爬楼梯和穿越恶劣地区的敏捷机器人。此次发布标志着该组织首次可以购买波士顿动力公司的机器人,也代表了波士顿动力公司的首次在线交易。

想象一下,如果有合适的外表,就像真正的动物一样,那该有多酷。

劳斯莱斯——虫子和蛇机器人

你有没有想象过如果你拥有一些动物的力量或能力会发生什么?

我们确实有超级英雄,他们拥有并使用一些动物或昆虫的能力,比如蜘蛛侠和蚁人。

照片由像素中的 cottonbro 拍摄

想象一下一个系统的综合能力,它由一个小虫子或蟑螂一样的元件组成,可以在蛇形机器人的帮助下进入(人类不能进入的)地方,以利用其爬行的本性。这听起来好像我在谈论一部以蛇和蟑螂为英雄的电影。但是罗尔斯·罗伊斯公司提出了同样的系统来修理飞机引擎。

在与哈佛大学和诺丁汉大学的合作中,劳斯莱斯正在努力开发比预期小 10 毫米的名为 SWARM 的社群机器人,该机器人可以选择通过小型摄像机的方法为人类导演提供发动机内部的现场视频。

劳斯莱斯的视频

为了让 SWARM 到达引擎,这些小机器人将乘坐 FLARE——两到三个内窥镜蛇形机器人,它们可以在一个巨大设备的专业和角落里滑行,并将 SWARM 存储在检查点。该协会同样计划让 FLARE 进行内部修复。

很久以前,在电影工业中,通过电子动画,人们就开始使用生物的能力。

哥鲁达无人机

哥鲁达无人机(来源— ROBIRD )

一部受欢迎的印度军事动作电影, Uri: The Surgical Strike,于 2019 年 1 月 11 日上映。在影片中,军方将其用于间谍目的。这是一个多产、电控、无人驾驶的航空情报收集框架,用于情报、监视、目标跟踪&获取。

电子动画——“活动但不活动的东西。”*

感谢萨迪亚哈桑的校对和建议。

* [## 开发人员-cosmos/Animatronics-初学者指南

动画电子学是动画和电子学的交叉。电子动物是一个机械化的木偶。可能是…

github.com](https://github.com/developers-cosmos/Animatronics-BeginnersGuide)

结论

我们已经看到了一些创新,这些创新类似于开发电子动画的原因。当与人工智能合作时,这可能会解决许多问题,并使世界变得更加现代和令人困惑。我们开始与动物真正对话的那一天似乎不远了。

如果你有什么建议,我很乐意听听。我很快会带着另一个有趣的话题回来。在那之前,呆在家里,保持安全,继续探索!

如果想联系,LinkedIn上联系我。*

用神经网络预测银行客户流失

原文:https://towardsdatascience.com/ann-classification-banking-customer-leave-or-stay-1cba16441185?source=collection_archive---------9-----------------------

基于 Keras 的人工神经网络构建在银行客户去留预测中的直观演示

来自 pixabay 的 Img 通过链接

本文旨在解释如何创建一个人工神经网络(ANN ),以使用银行客户的原始数据来预测银行客户是否会离开。本文分为以下六个部分。

  1. 问题陈述
  2. 数据处理
  3. 模型结构
  4. 模型编译
  5. 模型拟合
  6. 模型预测法

现在让我们开始旅程🏃‍♂️🏃‍♀️!

1。问题陈述

一家银行让我们预测客户是否有可能离开银行。给出了 6 个月的客户银行数据。该银行计划利用你的调查结果,与即将离开的客户重新建立联系。

2。数据处理

首先,使用 read_csv() 导入带有 Pandas 的数据,如下所示。

dataset = pd.read_csv(‘Churn_Modelling.csv’)

图 1 显示了数据片段。前 13 列是关于客户 ID、姓名、信用评分、地理位置、性别、年龄等的独立变量。最后一列是因变量决定客户是离开还是留下。

图 1 导入数据片段

现在,让我们开始数据处理。

1)特征选择

第一个问题:我们需要所有的自变量来拟合模型吗🤔答案是。例如,行号、客户 ID 或姓氏对客户的去留没有影响。但是客户的年龄可能有变化,因为年轻的客户可能会离开银行。根据这个逻辑,我们可以判断哪些独立变量会影响结果。但是请记住,只有神经网络才能判断哪些特征对结果有很大影响。

所以自变量 X 是:

X = dataset.iloc[:, 3: 13].values

因变量 y 为:

y = dataset.iloc[:, 13].values

2)分类编码

神经网络只取数值进行学习。

因此,分类变量,如地理和性别需要编码成数字变量。这里我们用fit _ transform()的方法label encoderfromsk learn。我们为每一列创建两个标签编码器。

from sklearn.preprocessing import LabelEncoder
labelencoder_X_1 = LabelEncoder()
X[:, 1] = labelencoder_X_1.fit_transform(X[:, 1])

注意上面,我们输入索引 1,因为 X 中地理列的索引是 1。编码后,国家德语变为 1,法国为 0,西班牙为 2。用同样的方法,对性别列进行如下编码。

labelencoder_X_2 = LabelEncoder()
X[:, 2] = labelencoder_X_2.fit_transform(X[:, 2])

现在,男性是 1,女性变成 0(希望亲爱的女士不会觉得被冒犯,因为这纯粹是随机的😊).

3)一键编码

注意上面的编码后,德语变成了 1,法国是 0,西班牙是 2。然而,国与国之间没有关系秩序。即西班牙不比德国高,法国不比西班牙低。所以我们需要为分类变量创建 虚拟变量 来去除分类编码引入的数值关系。这里只需要为地理列做这件事,因为性别列只有 2 个类别。

from sklearn.preprocessing import OneHotEncoder
onehotencoder = OneHotEncoder(categorical_features = [1])
X = onehotencoder.fit_transform(X).toarray()

图 2 是编码数据。请注意,前 3 列是针对德国、法国和西班牙国家的一次性编码虚拟变量。

图 2 编码数据片段

为了避免伪数据陷阱,我们删除了第 1 列,因为具有 0 和 1 的两列足以编码 3 个国家。

X = X[:, 1:]

4)数据分割

接下来,将数据分为训练集和测试集,测试集占 20%。我们使用 random_state 来确保每次分割保持不变。

from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y,test_size = 0.2, random_state = 0)

5)特征缩放

缩放特征是为了避免密集的计算,也是为了避免一个变量支配其他变量。对于二元分类问题,不需要缩放因变量。但是对于回归,我们需要缩放因变量。

正常的方法包括标准化和规范化,如图 3 所示。这里我们拿标准化来说。

图 3 特征缩放方法(作者创建的 Img)

from sklearn.preprocessing import StandardScaler
sc = StandardScaler()
X_train = sc.fit_transform(X_train)
X_test = sc.transform(X_test)

注意,我们使用来自训练集的标度集来转换测试集。这一步,数据处理就完成了。图 4 是您应该得到的测试数据。

图 4 最终测试数据片段

3。模型构建

这里我们使用 Keras 建立一个序列模型。第一步是初始化顺序模型。

classifier = Sequential()

对于这个问题,模型由两个密集层构成。

#add input layer and first hidden layer
classifier.add(Dense(output_dim = 6, init = ‘uniform’, activation = ‘relu’, input_dim = 11))#add 2nd hidden layer
classifier.add(Dense(output_dim = 6, init = ‘uniform’, activation = ‘relu’))

注意 Output_dim 对于隐藏层来说是一种艺术的选择,看你的经验了。作为提示,基于实验,选择它作为输入层节点数和输出层节点数的平均值。一个更明智的方法是使用参数调整,使用类似 k-fold 交叉验证的技术,用不同的参数测试不同的模型。这里,输入维度是 11 个特征,输出维度是 1,因此输出维度是 6。这里我们使用 均匀 分布来随机化 0 和 1 之间的权重。使用 ReLU 函数进行隐藏层激活,根据我的实验,这是最好的。请随意尝试你的。

下面添加输出层。

classifier.add(Dense(output_dim = 1, init = ‘uniform’, activation = ‘sigmoid’))

对于输出层,我们使用 Sigmoid 函数来获得客户离开或留在银行的概率。如果处理多分类,使用 Softmax 功能。

4。模型编译

模型编译是在网络上应用随机梯度下降(SGD)

classifier.compile(optimizer = ‘Adam’, loss =’binary_crossentropy’, metrics = [‘accuracy’])

这里我们使用 Adam (一种 SGD )作为优化器,寻找使神经网络最强大的优化权重。优化器所依据的损失函数是 二元交叉熵 。我们用来评估模型性能的指标是 准确性

5。模型拟合

由于我们使用 SGD ,批量被设置为 10,表明神经网络在 10 次观察后更新其权重。Epoch 是通过网络的一轮完整数据流。这里我们选 100。

classifier.fit(X_train, y_train, batch_size = 10, epochs = 100)

现在是拟合模型的时候了。大约 35 个历元后,模型精度收敛到 0.837 。不错吧,✨✨?

6。模型预测

随着模型的拟合,我们在测试数据上测试模型。使用阈值 0.5,将数据转换为真(离开)和假(停留)数据。

y_pred = classifier.predict(X_test)
y_pred = (y_pred > 0.5)

然后我们使用 混淆 _ 矩阵 在测试集上考察模型性能。

from sklearn.metrics import confusion_matrix
cm = confusion_matrix(y_test, y_pred

精度为 0.859,高于训练精度,暗示过拟合。

有了上面的模型,银行可以测试新客户,得到离开或留下的概率。然后,银行可以抽取 10%可能性最高的客户,对客户数据进行深度挖掘,了解他们为什么有可能离开。这就是人工神经网络的目的。

现在最后一个问题:如何对新客户数据进行预测🤔?例如,图 5 中的客户数据:

图 5 新客户数据

正如你所想象的,我们需要遵循同样的数据处理。首先,对变量进行编码。例如,在虚拟变量中,地理位置法国被编码为(0,0),性别男性为 1。按照这个方法,我们产生了下面的数组。

new_customer = [[0, 0, 600, 1, 40, 3, 60000, 2, 1, 1, 50000]]

好了,一个家庭作业问题:为什么我们使用 [[]] 来创建数组🤔?

下一步是将变量调整到与训练集相同的范围。

new_customer = sc.transform(sc.transform(new_customer))

数据准备就绪后,通过以下方式进行预测:

new_prediction = classifier.predict(new_customer)
new_prediction = (new_prediction > 0.5)

预测结果为假,可能性为 0.418% ,表明该客户不太可能离开银行👍👍。

太好了!仅此而已。如果你需要一些额外的,请访问我的 Github 回购(仅供参考,回购是积极维护的)💕💕。

神经网络优化的模型评估和参数调整

原文:https://towardsdatascience.com/ann-classification-model-evaluation-and-parameter-tuning-9174fd5ad0c2?source=collection_archive---------30-----------------------

使用 Keras 的交叉验证和网格搜索的分步走查

Img 改编自 pixabay 通过链接

在之前的文章中,我们构建了一个人工神经网络来解决一个二元分类问题。如果你还记得的话,留做家庭作业的一个问题是为什么我们对新客户数据使用[[]]。 答案是我们需要将客户数据放入一个水平向量中,而不是垂直向量中,因为输入数据中的所有观察值都是行而不是列。希望你没弄错😎。**

本文主要关注模型优化的交叉验证和网格搜索,分为两个部分:

  1. 模型评估
  2. 参数调谐
  3. 摘要

现在让我们开始旅程🏃‍♀️🏃‍♂️.

  1. 模型评估

你可能想知道为什么我们要花精力在模型评估上🤔问题是如果我们重新运行 ANN,每次模型不仅在训练集和测试集上产生不同的精度。因此,在一次测试中评估模型性能并不是最恰当的方式。

典型的方法是使用 K-fold 交叉验证。图 1 展示了它是如何工作的。

图 1 K 倍交叉验证图(作者创建的 Img)

我们将训练集分成 K 个折叠(例如,K=10)。然后在 9 个褶皱上训练模型,在最后剩下的褶皱上测试。10 折,我们用 9 个训练集和 1 个测试集做 10 个不同的组合,训练/测试模型 10 次。之后,我们取 10 次评估的平均值,并计算标准偏差。这样,我们可以确定模型属于哪个类别,如图 2 所示。

图 2 偏差-方差权衡图(Img 由作者创建)

为了实现 K-fold 交叉验证,我们在Keras:Keras classifier中使用了一个 scikit_learn 包装器。具体来说,我们使用 Keras 构建模型,使用 scikit_learn 进行交叉验证。首先要为模型架构构建一个函数,因为该函数是 Keras 包装器的必需参数。正如你在下面注意到的,这和我们之前建立的人工神经网络结构是一样的。

from keras.wrappers.scikit_learn import KerasClassifier
from sklearn.model_selection import cross_val_score
from keras.models import Sequential
from keras.layers import Densedef build_classifier():
 classifier = Sequential()
 classifier.add(Dense(units = 6, kernel_initializer = ‘uniform’, activation = ‘relu’, input_dim = 11))
 classifier.add(Dense(units = 6, kernel_initializer = ‘uniform’, activation = ‘relu’))
 classifier.add(Dense(units = 1, kernel_initializer = ‘uniform’, activation = ‘sigmoid’))
 classifier.compile(optimizer = ‘adam’, loss = ‘binary_crossentropy’, metrics = [‘accuracy’])
 return classifier

通过使用上述函数构建的分类器,我们创建了一个 KerasClassifier 对象。下面我们指定批量大小为 10,需要训练的模型的时期数为 100。

classifier = KerasClassifier(build_fn = build_classifier, batch_size = 10, epochs = 100)

现在,让我们使用cross _ val _ score()方法对分类器应用 K-fold 交叉验证。这个函数返回一个训练精度列表。参数 cv 是我们用于交叉验证的折叠数。这里,分类器将在 10 个不同的训练集上进行训练,这些训练集是从初始训练集中分离出来的。

accuracies = cross_val_score(estimator = classifier, X = X_train, y = y_train, cv = 10)
mean = accuracies.mean()
std = accuracies.std()

在这里,交叉验证可能需要一段时间。最终,我们得到了如图 2 所示的准确度为 10 的评估。平均准确度为 0.843,标准偏差为 1.60%🤪。

图 2 交叉验证的准确性

2.参数调谐

有了可靠的模型准确性,让我们尝试使用两种技术来提高它。

2.1 辍学正规化

我们没有提到的一个技术是退出正则化。这是与高方差相关的过拟合的解决方案。辍学是如何进行的?在每次训练迭代中,一些神经元被随机禁用,以防止它们相互依赖。通过覆盖这些神经元,神经网络每次都会保留不同的神经元配置,帮助神经网络学习数据的独立相关性。这可以防止神经元过度学习。

让我们使用 Keras 来实现它。基本上,我们在每个隐藏层后添加一个退出层。注意 p =0.1 意味着每次迭代将禁用 10%的神经元。

from keras.layers import Dropout
classifier.add(Dense(output_dim = 6, init = ‘uniform’, activation = ‘relu’, input_dim = 11))
#add dropout layer
**classifier.add(Dropout(p =0.1))** classifier.add(Dense(output_dim = 6, init = ‘uniform’, activation = ‘relu’))
#add dropout layer
**classifier.add(Dropout(p =0.1))** #add output layer
classifier.add(Dense(output_dim = 1, init = ‘uniform’, activation = ‘sigmoid’))

2.2 参数调整

神经网络有一些超参数,如时期数、批量大小和学习速率。参数调整就是找到模型的最佳参数。这里我们使用网格搜索来测试参数的不同组合。

为了实现它,我们在Keras:Keras classifier中使用一个 scikit_learn 包装器来包装神经网络。然后创建一个网格搜索对象,并在包装的分类器上应用参数调整。首先,如下构建分类器函数。

def build_classifier(optimizer):
 classifier = Sequential()
 classifier.add(Dense(units = 6, kernel_initializer = ‘uniform’, activation = ‘relu’, input_dim = 11))
 classifier.add(Dense(units = 6, kernel_initializer = ‘uniform’, activation = ‘relu’))
 classifier.add(Dense(units = 1, kernel_initializer = ‘uniform’, activation = ‘sigmoid’))
 classifier.compile(optimizer = optimizer, loss = ‘binary_crossentropy’, metrics = [‘accuracy’])
 return classifierclassifier = KerasClassifier(build_fn = build_classifier)

上面注意,没有指定历元数和批量大小,因为它们是我们计划调优的参数,将在下面的网格搜索对象中指定。

让我们为参数创建一个字典,其中包含我们希望模型尝试的值。

parameters = {‘batch_size’: [25, 32], ‘nb_epoch’: [100, 500], ‘optimizer’: [‘adam’, ‘rmsprop’]}

如果你注意到上面,我们有一个参数优化器用于 build_classifier() 函数。该参数提供了一种调优优化器的方法。为了实现网格搜索,我们首先用分类器和参数创建一个 GridSearchCV 类的对象。

grid_search = GridSearchCV(estimator=classifier, param_grid =parameters, scoring = ‘accuracy, cv = 10’)

最后,让我们在运行网格搜索以找到最佳参数的同时,在训练集上拟合 ANN。

grid_search = grid_search.fit(X_train, y_train)

我们最感兴趣的是产生最高精度的最佳参数。所以有了下文:

best_parameters = grid_search.best_params_
best_accuracy = grid_search.best_score_

最后,几个小时后,我们的精确度提高到了 0.849 ✨✨.最佳参数是批处理大小 25、纪元编号 500 和优化器 Adam,如图 3 所示。

图 3 参数调整的精度

3.总结

综上所述,通过交叉验证,我们发现该模型的精度约为 0.843。通过使用随机丢弃正则化和网格搜索,我们将模型精度提高到 0.849。通常,网格搜索可以分两步进行,第一步是找到大致范围,另一小步是细化最佳范围。

太好了!仅此而已!✨✨If 你需要一些额外的,访问我的 Github 页面。(仅供参考,回购得到积极维护💕💕)

使用 NVIDIA Clara 在 TrainingData.io 上自动检测胸部 CT 中的新冠肺炎感染(第二部分)

原文:https://towardsdatascience.com/annotate-data-train-ai-for-covid-19-detection-in-chest-ct-using-nvidia-clara-on-trainingdata-io-e25df2bfd0aa?source=collection_archive---------49-----------------------

注释数据并训练您自己的分割模型,用于检测胸部 CT 中的新冠肺炎。请注意training data . io 上生成的 ggo/consolidation 仅用于研究目的,不用于临床诊断。

2020 年 3 月,为了帮助数据科学家研究新冠肺炎诊断工具,TrainingData.io 提供了一个免费的协作工作区,预装了开源数据集,包括胸部 X 射线和胸部 CT 图像。我们要感谢用户的贡献。我们从用户身上学到了很多。现在,TrainingData.io 推出了拖放式用户体验,将人工智能的力量带给世界各地抗击新冠肺炎感染的每一位数据科学家、医院和诊所。

患者胸部 CT 显示的新冠肺炎感染。

在与新冠肺炎的斗争中,医务人员有四种不同的诊断数据可供他们使用:a)RT-聚合酶链式反应测试结果,b)抗体测试结果,c)胸部 x 光成像,以及 d)胸部 CT 成像。RT-聚合酶链式反应测试结果和抗体检测结果正被用作检测新冠肺炎的第一步。胸部 x 光和胸部 CT 成像被用于通过被诊断患有新冠肺炎的患者的肺部观察疾病的进展。

无症状病例中的肺损伤:研究人员研究了无症状感染的临床模式,并在自然医学发表。他们发现,无症状的新冠肺炎病例会对肺部造成长期损害。在一项这样的研究中,发现无症状患者的胸部 CT 检查显示“条纹状阴影”,并且在某些情况下显示“毛玻璃阴影”,这是肺部炎症的迹象。

由于受这种病毒影响的人群规模庞大,研究无症状病例肺损伤的能力依赖于在胸部 ct 检查中自动检测和可视化新冠肺炎感染的软件工具的容易获得性。

胸部 CT 数据中的新冠肺炎磨玻璃影和实变

当新冠肺炎患者体内的病毒扩散时,肺部被称为肺泡的小气囊中就会积聚液体。这种液体的存在导致肺部发炎。肺部炎症的增长可以在 x 光和 CT 成像中观察到。肺部炎症表现为磨玻璃样阴影(GGOs ),随后是磨玻璃样实变。

医务人员必须使用一些标准来决定是给病人使用氧气治疗或呼吸机系统,还是让康复中的病人脱离呼吸机系统。在 CT 成像中可视化 GGOs 实变模式在帮助医务人员做出正确决策方面起着重要作用。

新冠肺炎培训数据的 GGOs 整合细分. io

从在 PCR 测试中被诊断患有新冠肺炎的患者的胸部 CT 数据集开始,数据科学家需要创建肺部的分割掩模和毛玻璃阴影(新冠肺炎感染)的分割掩模。TrainingData.io 提供了一个隐私保护的并行分布式框架,用于将注释工作外包给多个放射科医生。CT 检查中的所有切片都可以在像素级别进行精确注释,并在 TrainingData.io 提供的 3D 注释客户端中进行可视化。

GGOs 合并的半自动人工智能辅助分割

TrainingData.io 提供了一个分割 ML 模型,为输入胸部 CT 检查生成毛玻璃阴影/实变。该模型可用于初始分割的种子。放射科医生可以稍后修复自动分割的结果。

使用 TrainingData.io 自动分割新冠肺炎的 GGOs 合并

从带注释的胸部 CT 数据集到 ML(分割)模型,只需点击几下鼠标

为什么区域医院或诊所需要 ML 模型来分割使用其数据集训练的 GGOs 合并?这个问题的答案在于病毒变异背后的科学。新冠肺炎是由一种叫做新型冠状病毒的病毒引起的。已经发现这种病毒变异速度很快,在所有受影响的国家都发现有很大比例的遗传多样性。病毒的不同变异可能以不同方式影响当地人口。

一旦 CT 数据集和分割掩模准备就绪,数据科学家就必须重新训练现有的机器学习模型,以更好地适应当地的人口统计数据。这可以通过在 TrainingData.io 网络应用程序中点击几下来实现。

注意:training data . io 上生成的 ggo/consolidation 仅用于研究目的,不用于临床诊断。

[1]https://www.nature.com/articles/s41591-020-0965-6.pdf

[2]https://www.webmd.com/lung/what-does-covid-do-to-your-lungs

本博客最早发表于 TrainingData.io 博客

系列第一篇:https://towards data science . com/新冠肺炎-成像-数据集-胸部-x 光-CT-用于注释-协作-5f6e076f5f22

使用在线注释工具注释您的图像!

原文:https://towardsdatascience.com/annotate-your-image-using-online-annotation-tool-52d0a742daff?source=collection_archive---------4-----------------------

图像标记变得简单

门的图像(由 Unsplash 上的 Robert Anasch 拍摄)

你刚刚得到了一个令人兴奋的项目,你必须建立一个程序来使用图像检测门是开着还是关着。你已经收集了几百张开门和关门的照片。现在,你需要注释和标记图像中的每扇门,以便你可以训练一个机器学习模型来检测开/关的门。

图像标注是为诸如对象检测、图像分类和图像分割等任务创建机器学习模型的重要步骤。

你在谷歌上搜索注释工具,找到了很多选项,但现在不知道该用哪种工具来注释你的图像。如果这是你,我将通过一个现有的开源注释工具,让你的决策过程变得简单一些。

不同的计算机视觉任务,每个任务都有注释类型

如果您想详细了解不同的图像注释类型:包围盒、多边形分割、语义分割、3D 长方体、关键点和界标、直线和样条线,在此阅读更多信息

让我们进入开源在线注释工具——make sense。

MakeSense 是一个开放源代码的注释工具,在 GPLv3 许可下可以免费使用。它不需要任何高级安装,只需要一个网络浏览器来运行它。

  • 开源
  • 免费
  • 基于网络的

用户界面简单易用。您只需上传您想要注释的图像,注释图像并导出标签。MakeSense 支持多种标注:包围盒、多边形和点标注。您可以导出不同格式的标签,包括 YOLO,VOC XML,VGG JSON 和 CSV。

根据该网站的说法,MakeSense 不存储图像,因为它们不会将图像发送到任何地方。

以下是使用 MakeSense 注释工具的分步指南。

  1. 前往 www.makesense.ai

MakeSense 主页

2.点击右下角的框进入注释页面,你会看到下面的页面,你可以上传你想注释的图片。

MakeSense 图像选择页面

3.选择并上传图像后,点击“物体检测”按钮。

MakeSense 上传图像页面

4.因为您没有加载任何标签,所以会要求您为项目创建标签名称列表。
要添加新标签,点击消息框左上角的+号,在“插入标签”文本框中输入标签。对所有标签重复上述步骤。
添加完所有标签后,选择“开始项目”

MakeSense 创建标签页

5.在这里,您可以选择使用预先训练好的模型来帮助您进行标记,或者自己进行标记。对于这篇文章,让我们用“我自己去”并手动完成所有的标记。

MakeSense 手动注释选项

6.您将在左栏看到已上传的图像,在右栏看到注释,在中间栏看到当前选择进行注释的图像。

在注释栏中,您可以选择不同的注释类型:边界框、点、多边形。对于这篇文章,让我们使用边界框注释,并在图像中注释开门和关门。

MakeSense 图像标签

要注释对象,只需将鼠标悬停在所选图像中的对象上,单击并拖动鼠标,即可创建一个所需大小的矩形框。

创建边界框后,会在右侧的边界框列下添加一个新条目。单击为对象选择所需的标签。

7.对所有图像中的所有对象重复注释步骤。

8.当您注释完所有图像后,就可以导出标签了。要导出,点击页面右上角的“导出标签”按钮,选择您想要的输出格式,然后点击“导出”

MakeSense 出口标签

通过这些简单的步骤,您现在已经注释了您的数据集,并准备好训练您的机器学习模型。

使用该工具时,您需要记住以下几点:

  1. MakeSense 是一个在线网络工具。这意味着,您需要将您的所有图像加载到 web 门户来对其进行注释。
  2. MakeSense 不提供上传 zip 文件的选项。您将不得不通过选择您想要注释的所有图像来上传。
  3. MakeSense 并没有提供一种保存注释项目的方法。这意味着,如果您有意或无意地刷新您的 web 浏览器,您的所有注释进度都将消失,您将不得不从头开始(上传所有图像)。
  4. 您不能与您的团队协作处理同一个注释项目。
  5. 你可以使用人工智能来帮助加速你的图像注释任务。您可以选择两个选项:COCO SSD 对象检测模型用于包围盒注释,POSE-NET 姿势估计用于关键点注释。请注意,要使用 AI,您需要在开始项目之前选择此选项。一旦你选择不使用人工智能模式的帮助,你不能改变它。

考虑到以上几点,当您有多达几百个图像要注释时,比如在开/关门的例子中,MakeSense 是一个很好的选择。由于不需要设置或安装,当您有一个小数据集时,该工具会变得非常方便,您可以一次标注。你可以上传开放门户的图片,添加注释并导出标签。
如果一幅图像包含两扇门,并且您使用边界框注释,平均来说,您可以在 1 分钟内注释 10 幅图像。在一个小时内,您可以使用此工具注释大约 600 张图像。

如果您有一个大型数据集,比如说 10,000 张图像,我建议您查看其他离线注释工具(如果您单独工作),或者在线注释工具(如果您在团队中工作),它们可以选择暂停和恢复注释任务。另一种选择是将图像标注任务外包给其他公司,这些公司专门从事这项工作。

在这篇文章中,我们讨论了如何使用名为 MakeSense 的开源在线注释工具来注释图像数据,何时使用 MakeSense 以及一些限制。

您使用哪种图像注释工具? 在下面留下你的想法作为评论。

原载于【www.xailient.com/blog】。**

找一个预先训练好的人脸检测模型点击这里下载。

查看这篇文章了解更多关于创建一个健壮的对象检测模型的细节。

关于作者

Sabina Pokhrel 在 Xailient 工作,这是一家计算机视觉初创公司,已经建立了世界上最快的边缘优化物体探测器。

宣布 PyCaret 2.0

原文:https://towardsdatascience.com/announcing-pycaret-2-0-39c11014540e?source=collection_archive---------3-----------------------

https://www.pycaret.org

我们很高兴今天宣布 PyCaret 的第二次发布。

PyCaret 是一个开源的、Python 中的低代码机器学习库,可以自动化机器学习工作流。它是一个端到端的机器学习和模型管理工具,可以加快机器学习实验周期,让你更有效率。

与其他开源机器学习库相比,PyCaret 是一个替代的低代码库,可以用来替换数百行代码,只需要几个单词。这使得实验快速有效。

请参见 PyCaret 2.0 的详细发行说明

为什么使用 PyCaret?

PyCaret 2.0 功能

正在安装 PyCaret 2.0

安装 PyCaret 非常容易,只需要几分钟。我们强烈建议使用虚拟环境来避免与其他库的潜在冲突。参见下面的示例代码,创建一个 conda 环境 并在该 conda 环境中安装 pycaret:

**# create a conda environment** 
conda create --name yourenvname python=3.6 **# activate environment** 
conda activate yourenvname **# install pycaret** 
pip install **pycaret==2.0** **# create notebook kernel linked with the conda environment python -m** ipykernel install --user --name yourenvname --display-name "display-name"

如果你使用的是 Azure 笔记本或者 Google Colab,运行下面的代码来安装 PyCaret。

!pip install **pycaret==2.0**

当您使用 pip 安装 PyCaret 时,所有硬依赖项都会自动安装。点击此处查看依赖关系的完整列表。

👉PyCaret 2.0 入门

PyCaret 中任何机器学习实验的第一步都是通过导入相关模块来设置环境,并通过传递数据帧和目标变量的名称来初始化设置函数。参见示例代码:

样本输出:

输出被截断

所有预处理转换都在设置功能中应用。 PyCaret 提供超过 20 种不同的预处理转换,可以在设置函数中定义。点击这里了解更多关于 PyCaret 的预处理能力。

https://www.pycaret.org/preprocessing

👉对比车型

这是我们建议任何监督机器学习任务的第一步。此函数使用默认超参数训练模型库中的所有模型,并使用交叉验证评估性能指标。它返回训练好的模型对象类。使用的评估指标包括:

  • 用于分类:准确度、AUC、召回率、精确度、F1、Kappa、MCC
  • 用于回归: MAE、MSE、RMSE、R2、RMSLE、MAPE

以下是使用 compare_models 函数的几种方法:

样本输出:

compare_models 函数的输出示例

👉创建模型

创建模型功能使用默认超参数训练模型,并使用交叉验证评估性能指标。这个函数是 PyCaret 中几乎所有其他函数基础。它返回训练好的模型对象类。以下是使用该功能的几种方法:

样本输出:

create_model 函数的示例输出

要了解更多关于创建模型功能的信息,点击此处

👉调整模型

调整模型函数调整作为估计量传递的模型的超参数。它使用随机网格搜索和预定义的完全可定制的调谐网格。以下是使用该功能的几种方法:

要了解更多关于调谐模式功能的信息,点击此处

👉集合模型

对于合奏基础学习者来说,可用的功能很少。 ensemble_modelblend_modelsstack_models 就是其中的三个。以下是使用该功能的几种方法:

要了解更多关于 PyCaret 中集合模型的信息,请点击此处

👉预测模型

顾名思义,这个函数用于推断/预测。你可以这样使用它:

👉绘图模型

绘图模型函数用于评估训练好的机器学习模型的性能。这里有一个例子:

plot_model 函数的输出示例

点击此处了解 PyCaret 中不同可视化的更多信息。

或者,您可以使用 evaluate_model 功能,通过笔记本内的用户界面查看图

PyCaret 中的 evaluate_model 函数

👉实用函数

PyCaret 2.0 包括几个新的 util 函数,在使用 PyCaret 管理您的机器学习实验时非常方便。其中一些如下所示:

要查看 PyCaret 2.0 中实现的所有新功能,请参见发行说明

👉实验记录

PyCaret 2.0 嵌入了 MLflow 跟踪组件作为后端 API 和 UI,用于在运行机器学习代码时记录参数、代码版本、指标和输出文件,并用于以后可视化结果。以下是你如何在 PyCaret 中记录你的实验。

输出(在本地主机上:5000)

https://本地主机:5000

👉将所有这些放在一起—创建您自己的 AutoML 软件

使用所有函数,让我们创建一个简单的命令行软件,它将使用默认参数训练多个模型,调整顶级候选模型的超参数,尝试不同的集成技术,并返回/保存最佳模型。下面是命令行脚本:

该脚本将动态选择并保存最佳模型。在短短的几行代码中,你已经开发了你自己的 Auto ML 软件,它有一个完全成熟的登录系统,甚至还有一个呈现漂亮排行榜的用户界面。

使用 Python 中的轻量级工作流自动化库,您可以实现的目标是无限的。如果你觉得这有用,请不要忘记给我们 github 回购⭐️,如果你喜欢 PyCaret。

想了解更多关于 PyCaret 的信息,请在 LinkedIn 和 Youtube 上关注我们。

重要链接

【PyCaret 2.0 发行说明
用户指南/文档 Github
安装 PyCaret
笔记本教程
投稿于 PyCaret

想了解某个特定模块?

单击下面的链接查看文档和工作示例。

分类
回归 聚类
异常检测 自然语言处理
关联规则挖掘

宣布 PyCaret 1.0.0

原文:https://towardsdatascience.com/announcing-pycaret-an-open-source-low-code-machine-learning-library-in-python-4a1f1aad8d46?source=collection_archive---------3-----------------------

Python 中一个开源的低代码机器学习库。

我们很兴奋地宣布 PyCaret ,这是一个用 Python 编写的开源机器学习库,用于在低代码环境中训练和部署有监督和无监督的机器学习模型。PyCaret 允许您从选择笔记本电脑环境开始,在几秒钟内从准备数据到部署模型。

与其他开源机器学习库相比,PyCaret 是一个替代的低代码库,可以用来替换数百行代码,只需要几个单词。这使得实验快速有效。PyCaret 本质上是几个机器学习库和框架的 Python 包装器,比如 scikit-learnXGBoostMicrosoft LightGBMspaCy 等等。

py caret简单 好用。PyCaret 中执行的所有操作都顺序存储在一个完全为部署编排的管道中。无论是输入缺失值、转换分类数据、特征工程还是超参数调整,PyCaret 都能实现自动化。要了解 PyCaret 的更多信息,请观看这段 1 分钟的视频。

PyCaret 1.0.0 发布 Python 中的开源低代码机器学习库

PyCaret 入门

PyCaret 版本 1.0.0 的第一个稳定版本可以使用 pip 安装。使用命令行界面或笔记本环境,运行下面的代码单元来安装 PyCaret。

pip install pycaret

如果你使用的是 Azure 笔记本Google Colab ,运行下面的代码单元来安装 PyCaret。

!pip install pycaret

安装 PyCaret 时,所有依赖项都会自动安装。点击此处查看完整的依赖关系列表。

没有比这更容易的了👇

📘循序渐进的教程

1.获取数据

在本分步指南中,我们将使用“糖尿病”数据集,目标是根据血压、胰岛素水平、年龄等几个因素预测患者结果(二进制 1 或 0)。数据集可以在 PyCaret 的 github 库上获得。直接从存储库中导入数据集的最简单方法是使用 pycaret.datasets 模块中的 get_data 函数。

from **pycaret.datasets** import **get_data**
diabetes = **get_data**('diabetes')

get_data 的输出

💡PyCaret 可以直接处理 pandas dataframe。

2.设置环境

PyCaret 中任何机器学习实验的第一步都是通过导入所需的模块并初始化 setup ()来设置环境。本例中使用的模块是py caret . class ification

模块导入后, setup() 通过定义数据帧(【糖尿病】)和目标变量(【类变量】)进行初始化。

from **pycaret.classification** import ***** exp1 = **setup**(diabetes, target = 'Class variable')

所有预处理步骤都在设置()中应用。py caret 有超过 20 个特征为机器学习准备数据,它根据设置功能中定义的参数创建一个转换管道。它会自动编排一个管道中的所有依赖项,这样您就不必手动管理测试或不可见数据集上转换的顺序执行。PyCaret 的管道可以轻松地跨环境传输,以便大规模运行或轻松部署到生产中。下面是 PyCaret 第一次发布时提供的预处理特性。

PyCaret 的预处理功能

💡初始化 setup()时,会自动执行机器学习必需的数据预处理步骤,例如缺失值插补、分类变量编码、标签编码(将是或否转换为 1 或 0)以及训练-测试-分割。点击这里了解更多关于 PyCaret 的预处理能力。

3.比较模型

这是监督机器学习实验中推荐的第一步(分类回归)。此函数训练模型库中的所有模型,并使用 k 倍交叉验证(默认为 10 倍)比较常用评估指标。使用的评估指标包括:

  • 用于分类:准确度、AUC、召回率、精确度、F1、Kappa
  • 用于回归: MAE,MSE,RMSE,R2,RMSLE,MAPE
**compare_models**()

compare_models()函数的输出

💡默认情况下,使用 10 重交叉验证来评估指标。可以通过改变 折叠 参数的值来改变。

💡默认情况下,表格按“精确度”(从高到低)值排序。可以通过改变 排序 参数的值来改变。

4.创建模型

在 PyCaret 的任何模块中创建一个模型就像编写 create_model 一样简单。它只有一个参数,即作为字符串输入传递的型号名称。该函数返回一个表,其中包含 k 倍交叉验证的分数和一个训练好的模型对象。

adaboost = **create_model**('ada')

变量“adaboost”存储由 create_model 函数返回的训练模型对象,它是一个 scikit-learn 估计器。被训练对象的原始属性可以通过使用句点( .)复变。参见下面的例子。

训练模型对象的属性

💡PyCaret 拥有超过 60 种开源的现成算法。点击这里查看 PyCaret 中可用的评估者/模型的完整列表。

5.调整模型

tune_model 函数用于自动调整机器学习模型的超参数。 PyCaret 在预定义的搜索空间内使用随机网格搜索。该函数返回一个表,其中包含 k 倍交叉验证的分数和一个训练好的模型对象。

tuned_adaboost = tune_model('ada')

💡无监督模块 pycaret.nlppycaret.clusteringpycaret.anomaly 中的 tune_model 函数可以与有监督模块配合使用。例如,PyCaret 的 NLP 模块可用于调整主题数量参数,方法是评估监督 ML 模型的目标/成本函数,如“准确度”或“R2”。

6.集合模型

ensemble_model 函数用于集合训练好的模型它只需要一个参数,即一个训练好的模型对象。这个函数返回一个表,其中包含 k 倍交叉验证的分数和一个训练好的模型对象。

# creating a decision tree model
dt = **create_model**('dt')# ensembling a trained dt model
dt_bagged = **ensemble_model**(dt)

💡默认情况下,集合使用“Bagging”方法,可以通过使用 ensemble_model 函数中的 方法 参数将其更改为“Boosting”。

💡PyCaret 还提供了 blend_modelsstack_models 功能来集成多个训练模型。

7.绘图模型

可以使用 plot_model 函数对经过训练的机器学习模型进行性能评估和诊断。它在 plot_model 函数中将一个训练好的模型对象和绘图类型作为字符串输入。

# create a model
adaboost = **create_model**('ada')# AUC plot
**plot_model**(adaboost, plot = 'auc')# Decision Boundary
**plot_model**(adaboost, plot = 'boundary')# Precision Recall Curve
**plot_model**(adaboost, plot = 'pr')# Validation Curve
**plot_model**(adaboost, plot = 'vc')

单击此处了解 PyCaret 中不同可视化的更多信息。

或者,您可以使用 evaluate_model 函数,通过笔记本内的用户界面查看图

**evaluate_model**(adaboost)

💡py caret . NLP模块中的 plot_model 函数可以用来可视化文本语料库语义主题模型。点击这里了解更多信息。

8.解释模型

当数据中的关系是非线性的时,这在现实生活中是常见的情况,我们总是看到基于树的模型比简单的高斯模型做得更好。然而,这是以失去可解释性为代价的,因为基于树的模型不像线性模型那样提供简单的系数。PyCaret 使用 interpret_model 函数实现SHAP(SHapley Additive exPlanations)。

# create a model
xgboost = **create_model**('xgboost')# summary plot
**interpret_model**(xgboost)# correlation plot
**interpret_model**(xgboost, plot = 'correlation')

测试数据集中特定数据点(也称为原因参数)的解释可使用“原因”图进行评估。在下面的例子中,我们检查测试数据集中的第一个实例。

**interpret_model**(xgboost, plot = 'reason', observation = 0) 

9.预测模型

到目前为止,我们看到的结果仅基于训练数据集的 k-fold 交叉验证(默认为 70%)。为了在测试/保留数据集上查看模型的预测和性能,使用了 predict_model 函数。

# create a model
rf = **create_model**('rf')# predict test / hold-out dataset
rf_holdout_pred **= predict_model**(rf)

predict_model 函数也用于预测未知数据集。现在,我们将使用我们用于训练的相同数据集作为新的看不见的数据集的代理。在实践中, predict_model 函数将被迭代使用,每次使用一个新的看不见的数据集。

predictions = **predict_model**(rf, data = diabetes)

💡predict_model 函数还可以预测使用 stack_modelscreate_stacknet 函数创建的一系列模型。

💡predict_model 函数也可以使用 deploy_model 函数直接从 AWS S3 上托管的模型进行预测。

10.部署模型

利用已定型模型对未知数据集生成预测的一种方法是,在模型已定型的笔记本/ IDE 中使用 predict_model 函数。然而,对未知数据集进行预测是一个迭代过程;根据使用情况,进行预测的频率可以从实时预测到批量预测。PyCaret 的 deploy_model 函数允许从笔记本环境部署整个管道,包括云上的训练模型。

**deploy_model**(model = rf, model_name = 'rf_aws', platform = 'aws', 
             authentication =  {'bucket'  : 'pycaret-test'})

11.保存模型/保存实验

一旦训练完成,包含所有预处理转换和训练的模型对象的整个管道可以保存为二进制 pickle 文件。

# creating model
adaboost = **create_model**('ada')# saving model **save_model**(adaboost, model_name = 'ada_for_deployment')

您还可以将包含所有中间输出的整个实验保存为一个二进制文件。

**save_experiment**(experiment_name = 'my_first_experiment')

💡您可以使用 PyCaret 所有模块中可用的 load_modelload_experiment 函数加载已保存的模型和已保存的实验。

12.下一个教程

在下一个教程中,我们将展示如何在 Power BI 中使用经过训练的机器学习模型,以在真实的生产环境中生成批量预测。

请参见我们的初级笔记本,了解这些模块:

回归
聚类
异常检测
自然语言处理
关联规则挖掘

开发管道中有什么?

我们正在积极改进 PyCaret。我们未来的开发管道包括一个新的时间序列预测模块,与 TensorFlow 的集成,以及对 PyCaret 可扩展性的重大改进。如果您想分享您的反馈并帮助我们进一步改进,您可以在网站上填写此表格,或者在我们的 GitHubLinkedIn 页面上发表评论。

想了解某个特定模块?

从第一个版本 1.0.0 开始,PyCaret 有以下模块可供使用。单击下面的链接查看文档和工作示例。

分类
回归 聚类
异常检测 自然语言处理
关联规则挖掘

重要链接

用户指南/文档
Github 资源库
安装 PyCaret
笔记本教程
贡献于 PyCaret

如果你喜欢 PyCaret,给我们 github 回购⭐️。

想了解更多关于 PyCaret 的信息,请在 LinkedIn 和 Youtube 上关注我们。

生物传感器数据中的异常检测和校正

原文:https://towardsdatascience.com/anomaly-detection-and-correction-in-biosensor-data-a1a4c371bbb?source=collection_archive---------21-----------------------

机器学习|信号处理|可穿戴技术

Owen r . Bidder-Insight 健康数据科学研究员

目前监测血压的方法是有缺陷的

三分之一的美国成年人患有高血压,即高血压。高血压会增加心脏病发作和中风的风险,而心脏病和中风本身就是美国的主要死亡原因。仅在 2017 年,美国就有超过 50 万例死亡病例将高血压列为加重因素,美国疾病预防控制中心估计,高血压每年在医疗保健成本、处方药和因误工和病假导致的生产力损失方面给美国经济造成超过 559 亿美元的损失。不幸的是,高达 10%的患者还表现出“隐性”高血压,即在诊所通过袖带方法测量的血压明显低于患者日常生活时的血压。这种低估的后果可能是可怕的;不正确的预测或者更糟的是,完全漏诊。

显然,允许人们在诊所外监测血压的方法对于抗击这种“无声杀手”至关重要。然而,大多数当前的方法在某些方面是有缺陷的。在手术过程中,使用导管通过静脉监测血压,但这增加了感染的风险,因此几乎只在重症监护中使用。由于这个原因,它也不适合日常测量。基于袖带的血压测量是非侵入性的,因此感染的风险很小。然而,它们通常由训练有素的医疗保健专业人员在门诊期间进行管理,因此它们也不允许我们在患者的日常生活中监测他们的血压——这是我们之前确定的对捕捉隐性高血压病例至关重要的。

血压在一天中会有波动,所以看医生时的一次测量只能提供一个快照(鸣谢:omronhealthcare.com)

PyrAmes 通过其新颖的传感器设计解决了这一问题

在我担任 Insight 数据科学项目的健康数据科学研究员期间,我有机会为一家公司提供咨询,该公司试图解决现有血压监测方法的缺陷。PyrAmes 开发了一种放置在风险上的传感器,使其成为静脉监测的非侵入性替代方法。与基于袖带的方法不同,它还可以连续监测血压,使其成为手术和长期监测患者的理想解决方案。

PyrAmes 传感器的早期原型(左)和 4 个信号通道记录的数据示例(右)。

PyrAmes 面临的一个挑战是运动可能在信号数据中产生运动伪影。该传感器依靠身体压力的微小变化来监控动脉波形,由此可以将各种指标(峰值高度、宽度、曲线下面积)校准为平均动脉压(MAP),这是我们感兴趣的血压指标。受运动伪影影响的数据必须被识别并从分析中排除,但这意味着用户无法在体育活动中监测他们的血压。考虑到这一点,皮拉梅斯要求我考虑以下几点:

  1. 开发一个简单的异常检测模型,该模型可以准确地标记低延迟的信号质量,以便可以即时执行运动伪影检测。
  2. PyrAmes 不是排除所有受运动伪影影响的数据,而是希望开发一种信号处理方法,通过这种方法可以恢复有用的信息,提供用户健康的更完整图像,并增加设备的价值。

开发低延迟模型

目前,PyrAmes 使用深度学习方法在后处理中执行异常检测。该模型提供了范围从-5 到+5 的分数,在任何极端的值表示良好的信号质量,接近 0 的分数表示差的信号质量。虽然深度学习模型可以提供高精度,但它们往往是计算密集型的。这使得它们不适合可穿戴设备,因为可穿戴设备可能需要将有限的硬件用于异常检测以外的任务。我开始尝试开发一个模型,可以重现这些信号质量分数,但使用一种计算量较小的方法,以便可以在运行中进行异常检测。

PyrAmes 提供的数据包括来自传感器的 4 个通道,用于检测动脉波形和一个记录三维动态运动的加速度计。加速度计应该对设备的运动非常敏感,但是不包含关于动脉波形的信息。任何重复的运动,比如运动节奏产生的运动,都会在加速度计中形成明显的信号。所提供的数据是在 660 小时的总持续时间内在 2 个不同的参与者身上的 5 次设备部署中获得的。参与者进行各种典型的活动,如走路、坐着、在电脑前工作等。传感器以 125 赫兹的频率记录(也就是每小时 450,000 条记录!),产生超过 4 Gb 的总数据集。

特征工程

在异常检测中,简化建模过程的一个策略是将我们的高分辨率数据转换为定义长度的仓,称为“时期”。每个时期代表一个 8 秒的数据窗口,这个窗口在时间序列上每次下移 2 秒,这样我们的时期就重叠了。对于每个时期,我计算了分为三类的汇总统计数据;

—关于信号和加速度计通道的广泛统计数据(如最小值、最大值、方差等)。)

—每对通道的相关系数,包括信号通道对和信号与加速度计的相关性。

—两个频率范围内的功率谱密度,0 至 3 Hz 用于捕捉典型的心率节奏(通常在每分钟 60 至 180 次之间),10 至 50 Hz 用于捕捉更高频率的噪声。

总的来说,这产生了 44 个特征,我可以用它们来为每个时期的平均质量分数(从-5 到+5)建模。

建模程序

对于这个任务,我选择尝试随机森林(RF)回归模型。我的基本原理是,RF 模型往往计算成本低,对过拟合具有鲁棒性,并且能够很好地处理大量的特征。最后一点是随机森林中每棵树的制作方式的产物,每棵树都使用了可用特征的随机子集。RF 还提供了相对变量重要性的便捷测量方法,让我们有更多机会进行特性选择,并进一步优化可穿戴设备的使用。

每个 8 秒的时期总共设计了 44 个特征。这些特征大致分为三类。

对于模型拟合程序,我选择排除每个部署数据 20%的分层样本进行验证。剩余的 80%进行了分层的 10 倍交叉验证。我使用这种方法是因为我想确保我们的最终模型可以推广到未来后续部署中收集的新数据。从模型拟合中排除每个部署的 20%的数据模拟了遇到新数据的过程。

随机森林准确检测运动伪影

我使用两个指标评估了异常检测模型,均方根误差(RMSE)和决定系数(R)。该模型在 10 倍和验证集中产生的误差很小,RMSE 分别为 0.24 和 0.52。为了验证该结果,如果我们的目标质量分数可以从-5 到+5(范围为 10),深度学习模型(以验证集为例)给出的分数平均低 0.5 分是可以接受的。在验证集中,我们的 R 值是 0.89,表示由工程特征解释的质量分数的变化量,对于计算开销如此低的模型来说,这是一个非常积极的结果。

从噪声中恢复信号

因为随机森林在检测运动伪影方面表现非常好,而不是迭代模型以获得边际性能收益,所以我开始通过处理 PyrAmes 设定的下一个任务来增加项目的价值,从受运动伪影影响的数据中恢复可用的指标。为了做到这一点,我借鉴了我在学术界的经验,并从文献中寻找灵感。PyrAmes 用来计算血压的最重要的指标之一是心率。幸运的是,我能够找到 Islam 等人的一篇论文(你可以在这里阅读),其中详细介绍了一种从类似的生物传感器(PPG 的光电容积描记器)恢复心率的方法。该方法被称为具有复合运动伪影参考生成的修改的谱减法方案,或简称为 SPECMAR,对于每个历元具有以下步骤:

—使用低带通滤波器去除每个通道原始数据中的高频噪声

—对滤波后的数据进行快速傅立叶变换,然后在频域中表示每个历元

—加速度计频谱中每个频率的幅度可以通过频谱减法从信号频谱的相应频率中去除。这是根据 Islam 等人指定的重量来完成的。

—心率数据中的自相关用于执行运动伪影参考生成。这里,我们使用来自前一时段的频域中的峰值来改进所考虑的时段中的峰值场。然后,我们可以忽略任何会导致心率突变的频率。

SPECMAR 方法的简单直观总结

在 SPECMAR 的验证试验中,该方法产生的心率估计值与使用 ECG 获得的地面实况高度相关(r=0.99),并且在 29 次不同记录中的平均绝对误差仅为 2.09 BPM。PyrAmes 提供的数据不包括来自 ECG 等来源的经过验证的基本事实,因此我无法完全验证将 SPECMAR 应用于 PyrAmes 传感器时产生的估计值。然而,该方法在 89%的测试时期产生了合理的心率估计(在 50 和 200 BPM 之间)。与以前排除任何不符合必要质量分数的数据的做法相比,这使得每个记录中可使用的数据量增加了 66%。增加可用数据量对用户有切实的影响,让他们更全面地了解自己的健康状况,增加 PyrAmes 传感器的价值。

未来方向

心率只是 PyrAmes 用来得出血压的指标之一。理论上,我们应该能够使用傅里叶逆变换将频域中的干净频谱转换回时域中的可用波形。不幸的是,在我分配给这个咨询项目的时间里,我无法调整这最后一步。然而,这确实为 PyrAmes 的工程师们继续研究提供了一个有用的起点。一旦这一阶段完善,他们应该能够消除信号中的所有运动伪影。一段时间以来,运动伪影一直是生物传感器行业的一个重要问题,这项技术代表了该领域技术水平的重大飞跃。

最后

在我与 PyrAmes 进行咨询的短暂时间里,我能够以两种重要的方式推进他们的产品开发;

首先,我能够优化他们用于可穿戴设备的异常检测算法。这允许实时的低延迟运动伪影检测,并且降低了该过程的硬件要求。降低 PyrAmes 传感器的硬件要求使他们的产品更加通用,并与目前市场上更多的可穿戴设备兼容。

其次,我能够利用最近开发的信号处理方法,从受运动伪影影响的数据中恢复心率估计值。这一过程将可用数据量增加了 66%。鉴于 PyrAmes 开发其传感器的部分动机是为其用户提供比目前不频繁的门诊更好的血压记录,开发对用户日常活动稳定的系统与他们的商业模式和他们与市场上其他生物传感器竞争的能力相关。

动态图中的异常检测

原文:https://towardsdatascience.com/anomaly-detection-in-dynamic-graphs-be3ec7ff7a5c?source=collection_archive---------38-----------------------

MIDAS:基于微簇的边缘流异常检测器

动机:

拒绝服务攻击(DoS 攻击)是一种网络攻击,旨在关闭网络,使其目标用户无法访问。2018 年 2 月 Github 上最近的一个重大(来源)。

简单的可视化展示一个 DoS 攻击- 来源

在这里,我们可以看到一个异常行为,在很短的时间内在动态图中创建了突然的可疑边。

什么是动态图?

节点是实体的视觉表示,而边是节点之间关系的视觉表示,表示为连接它们的线。图是由节点和边组成的非线性数据结构。经过一系列更新的图称为动态图。大多数现实世界的问题都属于这一类,包括交易网络,所有的即时通讯网络,包括 twitter,脸书等等。

问题:

在所有这些情况下都可以发现异常行为,在本文中,我们专注于检测微集群异常,如在 DoS 攻击的情况下突然到达可疑相似边缘的组,甚至是重大事件期间的推文等。

迈达斯:

基于微聚类的边缘流异常检测器是一种方法

(I)检测微团簇异常,同时提供关于其假阳性概率的理论保证。

(ii)由于它是在线的,因此在恒定时间和恒定存储器中处理每个边沿,并且处理数据的速度比最先进的方法快 162-644 倍。

(iii)它提供了比最先进的方法高 42%-48%的准确性(就 AUC 而言)。

方法:

我们将输入一个时间演化图,证明异常分数输出到边缘。

假设:

假设与时间戳小于 10 相比,时间戳为 10 时有大量的边(~1000)。如下图所示。t=10 时的平均水平等于 t 的平均水平<10.

来源

考虑到这一假设,我们使用 Count-Min-Sketch 数据结构,该数据结构使用哈希函数将事件映射到频率,与哈希表不同,它使用可能发生冲突的子线性空间,这被认为是由于使用子线性空间而产生的成本,更多详细信息可在本文中找到

  • sᵤᵥ : u-v 边沿直到时间 t
  • aᵤᵥ :当前时间 tu-v 边沿
  • ŝᵤᵥ :大致总计数
  • âᵤᵥ :近似电流计数

异常分数由下式给出

快速准确:

与最近在动态图(如 SEDANSPOT)中发现边缘流异常的方法相比,由于需要几个子过程(随机游走、采样等),需要花费大量计算时间。对于 DARPA 数据集,SEDANSPOT 的运行时间为~1.2 min,其中作为 MIDAS <的计算时间为 0.4 sec。

与 DARPA 数据集上的 SEDANSPOT 相比,MIDAS 具有更好的准确度和精密度。

DARPA 数据集的 ROC-来源

您可以通过此链接论文找到 MIDAS 的源代码。

实施:

C++编译器:

按照链接使用 MinGW 发行版安装最新的 C++17 编译器。将默认路径 C:\MinGW\bin 添加到系统环境变量%path%中。

Git Bash:

添加系统环境变量路径后检查 g++版本,确保其≥ 9.2.0

使用以下命令克隆 MIDAS 存储库。

运行使编译并创建可执行文件。

让我们在 DARPA 数据集上运行 midas,您可以下载原始格式和使用原始格式转换的 MIDAS 格式(‘源’、‘目的地’和‘时间戳’),这些格式可以通过链接从数据集下载。

scores.txt 文件提供了每条边的异常得分,对于约 450 万个 IP 到 IP 通信,该文件在不到 1.3 秒的时间内生成。使用原始数据集中的“攻击”变量,我们可以在存储库中 auc.py 文件的帮助下找到 AUC 得分。

AUC 为~0.95!

结论:

Dos 攻击、检测银行交易中的异常、推特寻找一些特定事件等。现实世界中有许多问题都是时间演变图。应用这些技术来更好地了解和发现可疑活动,实时可能是一个很好的解决方案。

参考文献:

  • SEDANSPOT
  • 西达尔特·巴蒂亚,布莱恩·胡伊,姬敏·尹,吉荣·申和克里斯特斯·法鲁索斯。" MIDAS:基于微团簇的边缘流异常检测器."2020 年 AAAI 人工智能大会(AAAI)。https://arxiv.org/abs/1911.04464
  • CMS

基于 MIDAS 的动态图异常检测

原文:https://towardsdatascience.com/anomaly-detection-in-dynamic-graphs-using-midas-e4f8d0b1db45?source=collection_archive---------21-----------------------

一种有趣的网络安全建模方法

随着网络攻击范围的不断扩大,网络安全研究近年来变得至关重要。从遗传算法到深度学习的各种想法已经被用来识别可能的攻击场景,并通知网络管理员。

在这些方法的花哨细节下,大多数都有一个相似的核心思想:注意到怪异 模式。作为一个密切观察网络的人,凭直觉您会对来自某些地理位置的请求的突然增加或网络流量的突然捕获/重新路由感到忧虑。

从技术上讲,这些怪异图案被称为异常、不寻常的东西。在识别可能的攻击时,这些是最明显的要点。

异常检测对于研究人员和工业家来说都是一个重要的问题。在本文中,我将重点介绍如何使用图表来识别这种模式。我们需要做的就是使用这些数学结构对整个网络流量(是的,您当前连接的局域网或 WiFi)进行建模。

一些帮助您入门的链接:

  1. 一篇关于 MIDAS 应用和实现细节的有趣的 TDS 文章:https://towards data science . com/controlling-fake-news-using-graphs-and-statistics-31ed 116 a 986 f
  2. 源代码:https://github.com/bhatiasiddharth/MIDAS
  3. 西达尔特·巴蒂亚,布莱恩·胡伊,姬敏·尹,申基贞和克里斯特斯·法鲁索斯。" MIDAS:基于微团簇的边缘流异常检测器."2020 年 AAAI 人工智能大会(AAAI)。https://arxiv.org/abs/1911.04464

那么我们如何用图来建模网络呢?

图是一种简单的数学结构:它有几个点,称为节点或顶点,以及一些连接它们的线,称为边。逻辑上,边代表顶点之间的某种关系。

我们可以利用这个抽象的定义。

要将问题建模为图形,您可以将某些实体标识为顶点,然后在它们之间绘制边来表示某些关系。

在我们的例子中,我们将计算机节点(如笔记本电脑、台式机等计算机设备)建模为图的顶点,并将它们之间的连接建模为边。

因此,上图可以理解为:

  1. 网络中的 5 台计算机设备:P、Q、R、S、T
  2. 上图是一个有向图。这意味着对于每条边,我们定义一个源顶点和一个目的顶点;例如,考虑有向边 P → Q,这意味着从顶点(或者在我们的情况下,计算机设备 P)到另一个顶点(也称为计算机设备 Q)的有向边。

我们现在得到了模拟我们网络的完美材料。假设我们创建一个由特定网络中的所有节点组成的有向图,每当一个节点试图与另一个节点建立连接时,我们就连接一条有向边。基于这些连接/边缘中已建立的模式,可能会注意到一些奇怪的模式和异常。

但是网络不是很动态吗?他们不经常改变吗?

是的,他们是。这是任何网络模型都面临的一个大问题。

您一定见过任何设备都有可能在几秒钟内上线。到目前为止一切顺利。现在,想象一下在一个巨大的人口统计区域中同样的事情——几个设备正在进入,其他几个正在进行。我说的进出,并不是指你简单地走进一个网络。有很多事情正在进行,其中最重要的也是理解子网的一个很好的起点是网络上每个节点的动态 IP 配置。而且,人们总是在发送文件、图像、视频、文本、HTTP 请求等等;服务器通常发现很难处理过多的流量。

除此之外,了解以下这一点是很有用的:您的节点/计算机和另一个(比如说服务器)之间的一个连接使用一个非常常见的协议——TCP——可能来回需要多达 3-4 个事务才能成功地传递一个信息单元(称为数据包)。你通常在几分钟内发送和接收数千个数据包。成千上万的人在和你完全相同的时刻做着完全相同的事情。

如果你深入思考,所有这些意味着一件事:

互联网是一个非常繁忙的地方。一个遍布全球的虚拟网络。而且很容易藏在那里。

那么这对异常检测意味着什么呢?

意味着变得强硬。在如此密集的网络动态中,你可以想象在数百个顶点上会有多少条边(最初是数千条)。那么,我们实际上如何开始触及问题的表面呢?

这就是迈达斯的用武之地。它建立在它的前辈(其细节超出了本文的范围)的基础上,在某种意义上,它通过考虑网络的时间性质和通过考虑微簇而不是单个边缘来执行检测。

MIDAS-R(MIDAS 的近似变体)考虑了网络的时间特性

到现在为止,你一定已经对计算机网络强烈的动态特性有了一个概念——它们变化的速度和规模。

使用使用静态图的异常检测技术,我们立即看到一个问题:没有时间关系(时间=时间,或者网络如何随时间演变)。让我们更详细地探讨一下这个问题。

仅用于演示目的。来源:https://towards data science . com/large-graph-visualization-tools-and-approach-2b 8758 a1 CD 59

仔细看上面的图表。考虑一个你正在观察的网络。现在看起来像图(b)中的。一小时后,看起来像(a)。有哪些异常?如何判断是否有人攻击?

使用静态图,您可以在特定的时间间隔拍摄网络配置的快照。你看到的图形就像独立的现存实体。

很直观的,这个方法没有意义。为什么?因为网络配置不是独立存在的。(a)中发生的每件事都与过去某个时候发生的事情有某种联系,就像(b)中一样。这就是时间特征的全部概念。这就是使用动态图的 MIDAS(和其他类似算法)所做的。他们会问以下问题:

当前的情景与过去有什么联系?什么变了,什么没变?

从你开始问这些问题的那一刻起,你就意识到了与网络过去配置的重要联系——你可以利用这一点更好地发现一些奇怪的模式并得到提醒。

MIDAS 考虑微团簇(这是它的一个新颖特征)

考虑一种非常流行的网络攻击:DoS(或另一种变种 DDoS)。虽然细节不在讨论范围之内,但是这些攻击非常粗略地向服务器加载了如此多的请求,以至于服务器不可用于来自真正客户端的真正请求,实际上使整个系统崩溃。

如果我们试图用我们的动态有向图、来对此建模,我们会期望什么呢?

左 iamge 来源:https://www . research gate . net/publication/301453477 _ Dynamic _ Graph _ Visualization _ with _ Multiple _ Visual _ anatomies/figures?lo = 1;右图来源:https://www . research gate . net/publication/277649495 _ A _ class ification _ of _ pentagent _ arc-transitive _ bicirculants/figures?lo=1

观察到了什么?

让我们首先考虑第二幅图像,所有点(被认为是节点)正在接收来自不同节点的几条边。然而,在第一幅图像中,由 1 标记的节点正在从单个节点 0 接收几条边。

早期的算法没有区分这些。但是迈达斯问了一个有趣的问题:

为什么两个相似的节点之间有这么多平行的边?

我们的网络模型中的并行节点意味着一台计算机正在向另一台计算机发送几个独立的请求。说 1 是服务器, 0 是运行浏览器的家用电脑。这个浏览器可能会同时请求几个东西:一个 YouTube 视频、一篇维基百科文章、一篇媒体文章、一篇关于数据科学的文章,以及其他几个东西。这仍然是对实际发生的事情过于简单化。事实上,你在浏览器中输入的每个 URL 都会被映射到分布在世界各地的不同服务器上。不要把来自一个浏览器的所有请求都发送到一个服务器。但是为了理解,我们假设他们真的做了。

到目前为止一切顺利。现在,如果并行请求的数量开始攀升,该怎么办呢?如下图所示:

资料来源:迈达斯文件原件

作者的上图说明了这一点。在一段时间的正常活动之后,两个节点之间会突然爆发活动。回想一下我们关于图的时间特性的讨论:现在发生的事情也取决于过去。在这里,过去有大约 100–200 个连接,突然计数上升到大约 1000。

迈达斯说,在这种情况下,可能会出问题。仔细检查这个。

这是 微簇检测的整个核心: 在用于监控异常的几个参数中,包括监控突然出现的活动突发,这些突发共享在图形表示(空间局部性) 意义上邻近的几个节点或边 。这是迈达斯非常强大的原因。

MIDAS 与现有解决方案相比如何?

MIDAS(或 MIDAS-R)的性能优于现有的解决方案/算法。作者报道的一些有趣的亮点包括:

假阳性概率的理论保证

我将把详细的讨论留在本文范围之外。粗略地说,作者表明 MIDAS (MIDAS-R)可以给出二元决策(异常是否存在),最高可达用户定义的 阈值 ε 因此,根据你是否希望将 10%或 1%作为误报的最大概率, MIDAS 可以处理它。

注意:假阳性意味着算法将某个事件检测为假阳性,即事件是阴性,但算法却说是阳性。在目前的情况下,这将意味着没有异常,但迈达斯说有。这只是通过设置一个高概率而应用的额外一层谨慎。你可能更愿意检查一个正常的事件,而不是让一个异常事件溜走(由于概率太低)。

然而,这并不总是令人满意的,这在很大程度上取决于用例。考虑一种检测癌症肿瘤的算法。你希望门槛高吗?这意味着几个没有患癌症的人被报道患有癌症。另一方面,过低的阈值意味着一些癌症患者可能会逃避检测。这两种情况都是危险的;因此,选择阈值是基于问题用例的经验决策。

恒定存储和更新时间

考虑一个典型网络的可能场景:

来源:https://towards data science . com/getting-started-with-graph-analysis-in-python-pandas-and-networkx-5e 2d 2 f 82 f 18 e

真正的网络要比这密集得多。问题出现了:

你把这么大的一个网络模型存储在哪里?

为了理解更多困扰我们的内存和时间复杂性问题,举一个例子:

找出所有小于给定数 n 的质数?

一个非常简单的算法:

  1. 取任何小于 n 的数。
  2. 检查 q 是否为质数。(这本身是在步骤 3 之后详述的算法)
  3. 重复直到 q 到达 n-1

(步骤 2 展开)检查 q 是否质数:

  1. 考虑所有比 q. 小的数字 p
  2. p 除以 q 吗? :增加某个计数器。:重复。
  3. 在最末端(当 p 到达 q-1 时),查看计数器是否为 2(意味着有 2 个因子:1 和数字本身)。:数字是质数。:不是质数。

一个糟糕的算法,但它做到了。你现在对问题有感觉了:先把 n: 增加到几十,再增加到几千,再增加到几万;你将开始看到时差。

粗略地说,我们认为时间和空间复杂性是这样的,当输入大小增加时,时间和空间需求都增加很多,以至于在某个限制之后解决问题是不可行的。

将此移植到我们的网络图问题中,MIDAS 面临着将几千台计算机表示为节点/顶点和几万条边的挑战。你可以查找图的标准表示法(邻接表、矩阵等)。),你马上就会看到问题。与素数算法一样,在这种表示中,随着节点和它们之间的连接的数量增加,用于存储的空间需求和用于执行算法的时间需求急剧增加。

作者使用一种不常见的数据结构解决了这个问题:计数最小草图(CMS)。细节不在讨论范围内,但这消除了上述问题。

迈达斯(MIDAS-R)简直更好

引用作者所做实验的一些结果:

MIDAS 和 MIDAS-R 得分高于基准。资料来源:迈达斯文件原件。

实验证明,MIDAS 和 MIDAS-R 比基准测试更准确、更精确。资料来源:迈达斯文件原件

随着图形越来越密集,线性时间和空间关系。资料来源:迈达斯文件原件

结论

我认为基于微聚类,包括时间和空间局部性,动态图上的异常检测将会有很长的路要走,并在不久的将来找到有趣的应用。作为一个整体,作者对重要的安全事件检测进行了基准测试,并发现了以下内容:

MIDAS 捕捉重要事件,就像之前建立的基准一样。资料来源:迈达斯文件原件

在未来的几年里,我们将会看到越来越多的研究致力于开发越来越干净和新颖的异常检测方法,并继续在其他领域实现对怪异模式的搜索。

监控系统中的异常检测(上)

原文:https://towardsdatascience.com/anomaly-detection-in-monitored-systems-part-1-a6080da61c75?source=collection_archive---------49-----------------------

了解高斯分布及其属性如何帮助我们对监控数据执行异常检测。

监控系统中的异常检测(图片来自维基媒体

自上世纪中叶第一台计算机问世以来,监控解决方案就一直存在,并且一直试图回答一个简单的问题:“我的计算机内部发生了什么?”。就像老师可能要求学生解释他的思维过程一样,我们也想了解我们程序的内部运作。如果我们找出了它运行时的问题所在,我们就可以获得更好的见解,帮助我们纠正它的行为。

回答这个问题最简单的方法可能是将数据写入任何标准输出,比如计算机屏幕或日志文件。在预定义的时间间隔或在程序执行到某个点时进行定期日志记录,可以让我们将它的状态与我们预期的结果进行比较。如果输出与我们预期的不匹配,我们可以修改代码来纠正这种情况。

这些年来,计算机沿着许多不同的道路发展。你可能在一部老电影里看到的笨重的电脑,已经被笔记本电脑、手机、物联网设备、网络系统、工业电脑,你能想到的都有了;这些系统,从现在开始我将称之为“设备”,通常有许多我们不能监控给定设备内部的应用。由于我们仍然希望监控它们的行为,以验证它们是否按预期运行,因此我们需要找到一种新的方法,仅基于输出就能给出可靠的结果。

最初的方法是为特定设备编写定制的监控解决方案。例如,如果我们有一个组件向我们发送一栋办公楼的功耗,我们可以用给定的指令集编写一个程序来监控该设备的输出值。虽然这种解决方案首次出现时被认为是一种好的方法,但它们有两个主要缺点。第一个是我们必须在编写程序时知道程序允许的操作参数,而在很多情况下我们并不知道。第二个缺点是,即使我们知道这些参数,我们也需要编写一个定制的算法,其中包含一组固定的指令,用于检查设备的输出是否在允许的工作参数范围内。

除非您是专业的电工或工程师,否则您必须长时间监控您的设备,经历许多电源故障、电涌和短缺,然后才能区分正常功耗和异常功耗。即使这样,你的知识也只对一个用电范围的办公楼有用。

我们能克服这两个缺点吗?答案是肯定的,这就是事情变得有趣的地方:一种称为“多元分布的测量方法可以帮助我们推广我们的监控解决方案,只需要很少的数据来进行初始预测,并且可以在我们编写算法时无需指定任何阈值的情况下检测异常。

那么多元分布是什么呢?让我们从本文中的“分布”部分开始,然后在第二篇文章中学习如何使用它,并在最后一篇文章中解释“多元”部分。如果你在阅读方程时感到自信,并且了解基本统计学,你可以安全地跳到下一篇文章。

在统计学中,分布向我们展示了我们的数据可以取的所有可能的值,以及落入给定范围内的任何额外样本的概率。为了使事情变得简单,并且因为我不想太深入问题背后的数学,我将假设我们的数据是“正态分布”。如果你学习统计学,你应该知道它的意思,但是对于那些不知道它的人来说,知道正态分布给我们一些关于我们监控的输出的有用属性就足够了。

如果我们将被监控设备的输出视为数字(从现在开始我将称之为“数据”),并假设它是正态分布的,我们就可以开始对它们进行预测。如果你从高中就不记得什么函数了,知道函数会根据我们给它的数字返回给我们数字就够了。正态分布的数据可以用一个叫做高斯函数的函数来描述。它有一个比你的标准高中函数稍微复杂一点的公式,但是如果你曾经看过为智商测试展示的著名的“钟形曲线”,它们是完全相同的函数。

带有高斯函数公式的钟形曲线(i 作者图片)

这个函数具体描述了什么?原来它描述了很多东西。其中第一个叫做“概率”。概率告诉我们数据中的随机数有给定值的机会。结果是任何给定值的概率都是零,但是高斯函数可以告诉我们一个数在两个值之间的概率。

钟形曲线下方的较大区域表示获得该范围内的值的可能性较高(Iimage by author)

它通过计算曲线下的面积来实现,由这两个值来界定。在上面的例子中,由数字 3 和 4 限定的区域大于由数字 5 和 6 限定的区域。这意味着,如果我们从数据中测量一个随机数,并将其称为 x,那么 x 在 3 和 4 之间的概率比在 5 和 6 之间的概率更高。我们将这个概率表示为 p(x)

出现的第二个问题是关于高斯函数公式中的许多符号:它们是什么意思?除了数学运算,公式中我们还有四个字母: eπμσ 。前两个是众所周知的常数值,在数学中有许多应用。e 大约等于 2.71828,而π大约是 3.14159。可能需要解释的是后两种情况。

数学家们有一个奇怪的习惯,他们从英语术语中抽取第一个字母,并用发音相同的希腊字母来表示它们。这使得希腊字母μ (Mu)成为表示英语单词“意思是”的标准符号。如果我们用一个字母和一个数字的组合来标记数据中的每个输出,我们可以得到如下所示的一系列值:x1,x2,x3 …等等。我们对这个系列的一个衡量标准是平均值,它给出了我们收到的所有值的平均值:

平均值方程

其中 n 是我们已经测量平均值的值的数量。写这个公式的一种更简洁的方式是使用“适马符号”。希腊字母σ(大写适马)标记多个元素的总和。如果我们想对 x 的所有值求和,我们可以写

适马记谱法对斯佩德记谱法

这意味着我们迭代 1 到 n 之间的所有值;用 I 标记当前迭代的值,我们取 x 的第 I 个值,并把它加到所有先前求和的值上。我们现在可以用适马符号来定义我们均值:

平均值方程,使用西格玛符号

第二个值称为“标准偏差”,描述 x 的不同值偏离平均值的程度。标准差用希腊字母σ(小写适马)标记,其公式如下:

标准偏差方程,无贝塞尔校正

现在,我们可以将这四个值结合起来,得出高斯函数的等式:

高斯函数 g(x),表示具有均值μ和标准差σ的正态分布

高斯函数还有另外两个简单的特性:它取的最大值等于我们数据的平均值,曲线的高度和宽度与标准偏差成比例。这在下图中可以清楚地看到:紫色曲线的标准差是红色曲线的两倍,但它们的均值相同。

这两个高斯函数具有相同的平均值,但是不同的标准偏差(i 作者的图片)

这让我们对 x 可以取的值有了几何上的了解:给定范围的值越接近我们数据的平均值,x 包含在该范围内的概率就越高。如果该值离我们数据的平均值太远,我们可以将其归类为异常。

看看你是否能找出这个图中的问题。提示:单值的概率是多少?(作者本人法师)

简单回顾一下,我们已经了解到:

我们希望检测受监控系统中的异常,但这并不总是那么简单

通过将输出视为数据并查看其分布,可以检测出输出中的异常

我们数据中的均值、标准差和概率代表什么

高斯函数有很多有趣的特性,可以用来检测异常。

这都是在此期间。在下一篇文章中,我们将讨论如何在实践中使用高斯函数来检测异常,并实现一个简单的程序来做到这一点。

该系列的第二部分现已准备就绪:

https://medium . com/@ yonatanalon/anomaly-detection-in-monitored-systems-part-2-c 2108 c143 a6

监控系统中的异常检测(下)

原文:https://towardsdatascience.com/anomaly-detection-in-monitored-systems-part-2-c2108c143a6?source=collection_archive---------53-----------------------

了解高斯分布及其属性如何帮助我们对监控数据执行异常检测。

使用高斯分布进行异常检测(图片由作者提供)

在本系列的第一篇文章中,我们讨论了高斯函数的属性以及如何使用它们来检测监控数据中的异常。在这一部分中,我们将把这些知识付诸实践,并构建我们自己的异常检测程序。

概括地说,我们用上图中的高斯函数完成了上一篇文章,其中两点标记了给定数据样本 x 可能取的不同值。然后我们说明 x 离平均值越远,它代表异常的概率就越高。

绘制在高斯函数 g(x)上的两点 p(x1)和 p(x2)(图片由作者提供)

该语句中唯一的问题是,获得任何单个值的概率恰好为零。这是为什么呢?原来高斯函数是一组函数中的一员,这组函数被称为“概率密度函数,简称 PDF。无需深究 pdf 背后的数学,理解它们给我们一个“连续随机变量在给定的“范围”内产生一个值的概率就足够了。

现在让我们来解释一下:范围就是两个数字之间的长度,用两个数字相减来表示。两点 a 和 b 之间的距离的常用符号是(a,b)。在下列情况下,数字 x 被认为是“在”一个范围内

随机变量是我们输出数据的正式术语。虽然我们将每个值视为单个数据点,但是当我们查看我们的输出随着时间推移所取的所有值时,我们可以将我们的输出视为一个整体,就像一个随机变量一样。我们要做一个小的捷径——我们要假设,对于给定的输出取值范围,没有一个值是不可能出现的。这种能够取任何可能值的性质,我们称之为“连续的”。因此,表现出这种性质的随机变量称为连续随机变量。标准符号是大写的 x。

回到 PDF 和高斯函数,它们表达概率的方式是计算给定范围内曲线下的面积。这种面积的计算是用积分来完成的。如果你有微积分背景,那么你就知道积分和面积的联系。对于那些不熟悉微积分的人来说,把它们当作工具就足够了,它允许我们计算给定范围内函数所限定的面积。

使用经典几何计算图形下的面积(图片由作者提供)

通过检查一个常数直线函数,例如 f(x) = 1(上面用红色标出),您仍然可以深入了解曲线下的区域。我们可以用两种不同的方法计算函数下面的面积:使用积分和使用矩形面积公式。首先,我们计算矩形的高度和宽度:我们可以看到底边是范围(2,3),我们通过减去左边的右点来获得它的长度。这样我们得到 3–2 = 1,这使得矩形的宽度正好为 1。

接下来,我们将计算矩形左边缘的长度。如果你不知道如何去做,看看这个关于测量距离的维基百科页面。因为我希望你知道如何做到这一点,如果你一直在阅读我到这一点,你可以很容易地验证左边缘的长度(代表高度)也等于 1。

通过使用矩形的面积公式,我们可以计算出它的表面积:

矩形区域,按宽度和高度

这给了我们一个关于测量由函数限定的面积的几何观点。即,在对应于矩形宽度的范围和它的表面积之间存在关系。事实证明,这种关系对每个函数都成立,这就给我们带来了以下问题:如果我们将值域的定义扩展到单点 x,我们可以说任何点都是形状(x,x)的值域。这个范围的长度是用同样的方法得到的——从左边减去右边的点。这使得长度 x - x = 0。

但是由于矩形的宽度是该范围的长度,我们将得到 W = 0。将新值代入面积公式,我们现在得到:

矩形区域,按宽度和高度

这就是为什么 p(x)对于任何给定值都是零。如果是这种情况,我们如何使用随机变量 X 的属性来执行异常检测呢?事实证明这比听起来简单,我们将再次使用我们的几何直觉。

给定值 X 在点 T1 和 T2 之间的概率(图片由作者提供)

对于任何给定值 x,我们计算范围(μ,x)的长度,并用常数 t 标记该长度。然后,我们使用以下公式选择两个值 T1 和 T2:

其中 g 表示高斯函数。事实证明高斯函数有一个很好的性质,它是一个关于均值的对称函数。即对于 x 的每一个值,g(μ - x) = g(μ + x)。因为每两个点只有一条剖切它们的线,所以 g(x)对称的事实意味着通过 T1 和 T2 的线垂直于 x 轴。

高斯函数的另一个特性是,当不受 x 轴限制时,曲线下的面积等于 1。这可以用积分表示为:

表示高斯函数下方面积的无界积分

这意味着,如果我们计算由范围(T1,T2)界定的面积,我们可以通过从 1:

概率 p(x) w.r.t. μ和σ的表示

使用高斯函数下方的区域可视化我们的概率(图片由作者提供)

这些结果在几何上意味着什么?请记住,这里的关键参数是我们用 t 表示的长度:x 离平均值越远,范围 t 就变得越长。t 的尺寸每增加一点,T1 和 T2 之间的面积 A 就相应增加一点。A 的增加自然意味着 1-A 值的减少,这被视为钟形曲线边缘下面的蓝色“尾部”。

在这个图中,x 比前面的图离μ更远;代表该概率的区域确实更小(图片由作者提供)

现在我们已经有了结果,我们需要决定的是一个给定的值 x 需要异常到什么程度,我们才能把它归类为异常。这个值完全取决于您和您试图监控的设备的域。常见的值可能是 0.1%,1%,5%,但当然,你可以选择最适合你的值。

简单回顾一下,我们已经了解到:

随机变量是一种统计上表示我们输出数据的方式。

概率密度函数和高斯函数一样,给出了我们的随机变量在一个范围内产生值的概率。

高斯传递这种概率的方式是通过其曲线下的表面积,以目标范围为界。

这个系列的第三部分已经准备好了:

https://medium . com/@ yonatanalon/anomaly-detection-in-monitored-systems-part-3-52c 172 CFA 589

python 中的代码示例(版本 3.8.3):

使用高斯分布的单变量异常检测—源代码

监控系统中的异常检测(第三部分)

原文:https://towardsdatascience.com/anomaly-detection-in-monitored-systems-part-3-52c172cfa589?source=collection_archive---------45-----------------------

了解高斯分布及其属性如何帮助我们对监控数据执行异常检测。

检测高维数据中的异常(图片由作者提供)

在本系列的第二篇文章中,我们学习了更多关于高斯函数的知识,然后我们使用它的属性构建了一个工作异常检测系统。在这最后一部分中,我们将处理多变量的情况,正如在本系列的第 1 部分中所承诺的。与第 1 和第 2 部分不同,第 1 和第 2 部分不需要任何先前的知识,本文将需要至少线性代数的基础知识(特别是使用向量和矩阵)。

那么到底什么是多元分布呢?“多元”部分将一个维度中的分布推广到任意有限个维度。这使得多元分布在建模与给定设备的许多监控参数相关的数据时非常有用。我们将我们的定义扩展到更高维度的方法是通过一个叫做“随机向量”的东西。如果 Xk 是每个 1≤k≤n 的随机变量,则向量 X = (X1,X2,…,Xn)称为随机向量。除了上述性质外,如果随机变量的任何线性组合也是正态分布的随机变量,则认为随机向量是正态分布的。

我们如何解读这些读数,从而对受监控设备的异常行为进行分类?最简单的方法也许是将随机变量相乘。因为随机变量随着时间监控输出,所以我们可以将随机变量视为向量,其值只是给定字段中的普通数字。然后,我们可以将它们按元素相乘,得到所谓的乘积分布:

z 是随机向量 X 中所有随机变量的乘积分布

我们可以建立产品分布的平均值和标准偏差,并使用它们来计算样本相对于产品分布 Z 异常的概率:每当我们从被监控设备读取一组新值时,我们就构造一个向量 z = (z1,z2,…,zn),其中 zi 表示第 I 个被监控参数的最新值。然后,我们通过将独立值相乘来计算 z 成为异常的概率:

读取样本向量 z 的概率是读取其各个分量的概率的乘积

使用乘积分布的主要好处是,计算数字之间的乘积是执行成本最低的计算之一。但是使用产品分销也有几个缺点。想到的两个这样的缺点是,不能保证 Z 将是正态分布的,并且 Z 可能无法捕捉某些类型的异常。

来自监控计算机网络中的防火墙的示例数据。注意异常的红色 X(图片由作者提供)

假设我们正在监控一个计算机网络,并且正在从我们的一个防火墙收集数据。我们对这个防火墙的两个操作参数感兴趣:并发连接数和每秒传输的千字节数。我们开始监控防火墙,并将 X1 指定为连接的用户数量,将 X2 指定为传输的 kb/s 数量。

经过一段时间后,我们得到一个读数 z = (z1,z2 ),并在上图中将其标为红色的“X”标记。当我们查看图表时,我们可以清楚地看到 z 是一个异常值——它被绘制在远离主要读数“簇”的地方。但是如果我们沿着红色的虚线,我们发现 z 是一个正常的读数:z1 非常接近连接用户的平均读数,z2 也接近传输的 kb/s 的平均值。因此,如果我们将 p(z1)*p(z2)相乘来检查异常,结果将显示 z 不是异常。

但事实证明,我们可以使用另一种统计方法,它也可以捕捉我们数据中的这些异常。这种方法使用所谓的多元高斯分布,它是一维高斯函数的多维抽象。

在解释其含义之前,让我们先来看看这个公式:

多元高斯分布方程

因为我假设你在第一段接受了我的建议,我希望你知道这个公式中的大多数符号。如果你有足够的勇气在没有这些知识的情况下继续下去,你至少应该知道向量就像一行或一列数字,矩阵是数字的网格(或一行向量)。向量和矩阵都有自己特殊的加法和乘法规则,而行列式、求逆和转置则是我们对矩阵进行的更专门的运算。

然而,该公式中真正的精华是协方差矩阵:这个非常特殊的矩阵在统计学中有一些有用的属性,包括作为多元高斯的基础。协方差矩阵的公式如下所示:

随机向量与其自身的协方差矩阵

我们在此描述的协方差矩阵测量随机向量与其自身之间的关系,由矩阵的每个单元中的标量值表示。表达这种关系的一种有用的方式是通过协方差矩阵的近亲,称为相关矩阵。相关矩阵将协方差矩阵中的每个值除以随机变量 Xi 和 Xj 的标准偏差的乘积

从协方差矩阵导出相关矩阵

这种划分的实际结果是协方差矩阵的值现在被限制在-1 和+1 之间。Rij = 1 表示 Xi 和 Xj 之间的正线性关系——测量 Xi 的大值表明 Xj 的相同测量值增加。相反,Rij = 1 表示 Xi 和 Xj 之间的负线性关系——测量大值的 Xi 表示减小相同的 Xj 测量值。

随机向量元素之间的关系(图片来自维基媒体

介于-1 和 1 之间的值表示非线性关系,0 表示 Xi 和 Xj 线性无关。由于这种关系的性质,它立即意味着σ(和 R)是对称矩阵。即—σij =σJi 对于每 1≤i≤j≤n。

这些统计的非线性关系正是协方差矩阵如此强大的原因。因为它可以捕捉组成随机向量 X 的随机变量之间的所有这些关系,所以多元高斯可以检测我们在产品分布中未检测到的异常。它们可能接近于每个随机变量的平均值,但是因为它们位于多元分布之外,所以它们可以被归类为异常。

同一个分布被绘制了三次,不同的协方差矩阵塑造了每一次绘制(图片来自 Coursera

我们将使用的技术类似于我们在单变量情况下使用的技术:我们将使用一个平面(或更高维的类似物)来“分割”分布,然后计算多变量高斯和坐标平面之间的体积。

请注意绿色平面是如何“切割”高斯曲线的,就像我们用直线切割单变量分布一样(图片由作者提供)

因为我们在一个 n 维空间中工作,所以我们需要进行 n 次积分。这让我们计算:

样本向量落入由任意范围界定的多元高斯下方体积内的概率

但是解析地计算 n 个积分,特别是当 n 可能非常大(例如超过 10,000)时,计算量非常大。这就是为什么我要走捷径:高斯的“钟形曲线”意味着返回值越低(您可能已经注意到 G(X,μ,σ)返回一个标量值,而不管 n 的大小),高斯“尾部”下面的区域越小。

叠加两个具有不同ε值的高斯图显示了不同的值如何设置不同的异常阈值(图片由作者提供)

由于 G 返回的值与距离分布平均值给定距离的观察值的概率之间的关系,我可以将 p(x)与任意阈值进行比较,并将任何小于给定阈值的值归类为异常。这可能不是直接计算在我们的数据中观察到给定值的概率,但也许您可以证明这种分类的一种方法是注意到,因为高斯是一个连续函数,每个阈值ε ∈ (0,max(G))都有一个匹配值 t,因此 p(t) = ε。不是将异常分类为 p(x) < ε, we can think of our classification as marking any x for which p(x) < p(t) as an anomaly.

Plotting a density function for a multivariate distribution as a height map. Darker colours indicate a larger volume underneath a given slice (image by author)

概括的任何值,我们已经知道:

乘积分布是一种简单的多元异常分类方法,计算量也很小。

他们的主要缺点是无法对某些异常进行分类。

协方差矩阵(及其近亲,相关矩阵)可以用来度量随机向量 x 的随机变量之间的关系

这些关系允许多元高斯函数对产品分布遗漏的所有异常进行分类。

我们可以通过将异常与任意值ε ∈ (0,max(G))进行比较来对异常进行分类,因为执行高维积分在计算上是昂贵的。

python 中的代码示例(版本 3.8.3):

基于 VAR 的多元时间序列异常检测

原文:https://towardsdatascience.com/anomaly-detection-in-multivariate-time-series-with-var-2130f276e5e9?source=collection_archive---------11-----------------------

存在序列相关性时的系统监控

照片由 Unsplash 上的尼克·费因斯拍摄

异常检测是机器学习中的一个热门话题。正如我们所猜测的,“异常”的定义是可变的,并且与领域相关。在时间序列应用中,当我们面对这类问题时,我们还必须考虑时间维度。一个系列的历史包含了关于其行为的大量信息,并且可以暗示其未来的变化。这对于不是由随机游走过程产生的序列和表现出循环/周期性模式的序列来说尤其如此。

处理时间序列并从过去学习信息的简单已知模型是 ARIMA。ARIMA 模型是开发时间序列预测工具的重要工具。他们学习序列如何演变的能力在异常检测任务中也是有用的。从这个意义上说,经典方法包括将超出容许阈值的观察标记为异常。这种方法仅限于奇异级数;如果我们想考虑一个更复杂的系统,我们需要另一种方法。

在这篇文章中,我们将介绍一种方法来检测由多个相关序列组成的复杂系统中的异常。我们使用 VAR 模型,ARIMA 的多变量扩展,从我们处理的序列中提取相关模式。VAR 学习到的信息随后用于构建阈值机制,以便在我们的指标超过临界值时发出警报。

数据

我们从 Kaggle 那里获取实验数据。西雅图伯克吉尔曼步道是西雅图市托管的数据集,是其开放数据项目的一部分。数据集存储传感器检测到的每小时计数序列。这些传感器可以计算骑自行车的人和行人。为每种出行模式记录单独的交通量。混凝土中菱形排列的电线探测自行车,安装在木柱上的红外传感器探测行人。

可供我们使用的每日汇总时间序列示例

总共提供 5 个计数系列。2 个与行人计数相关,2 个与自行车计数相关,总计是前面系列的总和。行人和自行车有两个柜台,因为登记了两个行驶方向。

根据这些数据,我们的异常检测之旅分为两部分。首先,我们提供了一个经典的单变量异常检测方法使用 ARIMA。最后,我们通过一个多变量的方法考虑所有的系列和他们在系统中的相互作用。根据这篇文章的范围,我们决定聚合我们可以处理的数据,从每小时的数据到每天的数据。

单变量异常检测

在单变量异常方法中,我们计划使用 ARIMA 来检测奇怪模式的存在。我们决定把重点放在总计数系列上。开发 ARIMA 时,首先要处理的是平稳性、爆炸性趋势或季节性。从上面的图和下面的自相关可以很容易地看出,总计数序列呈现出双重季节性:每周和每年。

原始自相关

长期的季节性会很烦人。为了消除它,我们每天减去根据训练数据计算的相对月平均值。这样,我们只剩下每周的模式,我们的模型可以毫无问题地学习它。

去除长期季节性后的自相关

我们拟合了最佳 ARIMA,将搜索限制在 7 自回归阶附近,同时最小化 AIC。最终的模型似乎产生没有任何自相关程度的正常残差。

ARIMA 拟合数据

安装好 ARIMA 后,我们就可以开始寻找异常点了。我们有两种可能性:我们可以将置信区间之外的每个观察值识别为异常,或者我们可以查看残差。每个程序都需要生成迭代预测,并每次用实际值评估我们的预测。

测试预测

标准化测试残差

可以通过 alpha 置信度参数来指定预测间隔深度。残差分析需要标准化操作(必须计算训练残差的平均值和标准偏差)。这样,我们可以使用正态分布的残差和来自正态分布的固定置信阈值进行操作。

多元异常检测

前一种方法的多元推广涉及到 VAR 模型的采用。VAR 模型通过捕捉多个变量之间的线性关系,扩展了单变量自回归(AR)模型。对于每个输入序列,进行回归。原始变量根据它们自己的滞后值和其他变量的滞后值进行回归。对于我们的多元任务,我们同时考虑自行车和行人系列。

在存在序列相关性的多变量过程系统中,我们使用 VAR 模型来逼近系统,并将残差作为一个序列独立的序列进行监控。由于过程动力学的物理原理,使用 VAR 来近似线性系统是合适的。

VAR 训练与选择最小化 AIC 的最佳阶数之前一样计算。数据以同样的方式标准化,以消除长期的季节性。毫不奇怪,最好的模型是 VAR(7)。测试残差数据的独立性和正态性后,可以计算霍特林 T 平方统计量来检测异常的早期存在:

霍特林·T2 的公式。e 是估计的残差;适马是 e 的协方差矩阵

T 平方控制图的应用分两个阶段进行:控制界限建立阶段和监控阶段。第一阶段的重点是获得模型残差,以便计算出的控制极限可用于第二阶段,用于监控未来异常的残差过程。T 平方控制图的控制限由下式给出:

上限控制统计

其中 F 代表具有 p 和 n-p 个自由度和α显著性水平的 F 分布。如果 T2 > UCL,那么停下来调查一下。在阶段 1 结束时获得的估计适马(以及残差均值和标准差)用于计算每个新观测值的 T 平方统计量。

列车数据上的 T2 加 UCL

测试数据上的 T2 加 UCL

摘要

在这篇文章中,我们介绍了执行异常检测任务的良好工作流。我们开始研究单变量的情况。我们使用一个拟合的 ARIMA 作为判断来检测未来的观察是否异常。我们也意识到现实可能更复杂,可能需要考虑不同变量之间的相互作用。出于这个原因,我们将我们的分析扩展到 VAR 模型的多变量情况。我们收集了 VAR 残差,并使用它们来建立一个阈值警报系统,在出现异常行为时发出警报。

查看我的 GITHUB 回购

保持联系: Linkedin

参考文献

利用向量自回归残差在序列相关下监测多元过程

基于机器学习的过程控制数据异常检测

原文:https://towardsdatascience.com/anomaly-detection-in-process-control-data-with-machine-learning-35056a867f5b?source=collection_archive---------32-----------------------

实践教程

引入异常检测,使用您自己的温控实验室设备桌面上生成的数据

迪米特里·阿尼金在 Unsplash 上拍摄的照片

异常检测是机器学习在现实世界中的一个强大应用。从检测欺诈交易到预测组件故障,我们可以训练一个机器学习模型来确定什么时候发生了不寻常的事情。

说到机器学习,我非常支持通过实验来学习。机器学习模型背后的实际数学可能有点像黑箱,但这并不妨碍它的有用性;事实上,我觉得这是机器学习的优势之一。你可以用同样的算法来解决所有的问题。有时候,最好的学习方法是处理一些真实的数据,看看会发生什么。

在本例中,我们将能够使用温度控制实验室设备生成一些真实数据,并训练一个监督分类器来检测异常。TCLab 是一个很棒的小设备,可以通过简单的即插即用 Arduino 设备生成真实数据。如果你想为此创建自己的数据,这里有很棒的介绍性资源;否则,我会将我在自己的 TCLab 设备上生成的数据包含在 Github 存储库中。如果你想自己运行这些例子,你可以在这里下载并遵循代码。

问题框架

TCLab 是一个简单的 Arduino 设备,带有两个加热器和两个温度传感器。这是一个简单的即插即用设备,一个插头为加热器供电,一个 USB 端口与计算机通信。可以在 Python 脚本中调整加热器的级别(确保pip install tclab),温度传感器用于读取每个加热器周围的温度。对于这个例子,我们将保持它的基本,只使用一个加热器和传感器,但同样的原则也可以适用于 2 加热器系统,甚至更复杂的系统,如你可能会发现在化学精炼厂。

图像来自 APMonitor 。经允许重新发布。

我们可以这样想象这个问题:我们的车库车间有一个加热器,有一个简单的开/关设置。我们可以设定加热器每天开启或关闭一定时间,以保持温度在一个舒适的水平。当然,我们可以使用更复杂的控制系统;然而,这是作为机器学习异常检测的介绍而设计的,所以我们现在将保持原始数据简单。

在正常情况下,这是一个足够简单的练习来验证加热器是否开启并工作——只要看看温度,看看它是否在上升。但是如果有外部因素使评估复杂化了呢?也许车库门开着,让一股气流进来,或者你的一些设备开始过热。或者更糟糕的是,如果温度控制受到网络攻击,并且被攻击者掩盖了,怎么办?有没有办法查看我们正在收集的数据,并确定什么时候出现了问题?这是异常检测的核心。

使用监督分类器,我们可以查看传感器数据,并训练它在加热器打开或关闭时进行分类。因为我们也知道何时加热器应该打开或关闭,我们可以将分类器应用于任何新的数据,并确定行为是否与我们看到的数据相符。如果两者不匹配,我们知道存在某种类型的异常,并可以进一步调查。

生成数据

为了模拟这个场景,我们将从 TCLab 生成一个数据文件,以不同的时间间隔打开和关闭加热器。如果你的 TCLab 设置有问题,这里有一些很好的故障诊断资源。

首先,我们将设置几个数组来存储数据,我们将以 1 秒的间隔收集这些数据。加热器开/关循环现在非常简单,只需将它一直打开或关闭,并让它保持几分钟。我们将运行一个小时,以确保我们有大量的数据进行训练和验证。

TCLab 输入很简单,根据我们生成的内容将lab.Q1设置为开或关。我们只将加热器开至 70%以避免过热,然后记录从lab.T1开始的每个时间点的温度。最后,让循环延迟 1 秒。

异常数据是以基本相同的方式创建的,但只用了 20 分钟。最大的不同是,我在指定的时间让风扇吹过加热器,模拟车库里的通风。风扇的对流会自然冷却系统,抵消加热器的作用。由于这是意料之外的,我们应该看到分类器拾取它——也许在我们知道加热器是开着的时候指示它是关着的。让我们继续下去,看看它是否有效!

数据预处理

机器学习的关键之一是研究如何以对模型有用的方式构建数据。当我做机器学习的任何项目时,这往往是最耗时的一步。如果我没弄错的话,这个模型非常有效;否则,我可能会花几个小时甚至几天的时间感到沮丧,想知道为什么我的模型行不通。

你应该总是为机器学习应用缩放你的数据,这可以很容易地用 scikit-learnMinMaxScaler来完成。此外,检查数据的格式对于模型是否正确(例如,numpy 数组的形状是否与分类器所期望的相匹配?否则,您可能会收到警告)。让我们看看目前为止这个是什么样子的。请注意,我们没有缩放 y 数据,因为它已经只是 0 和 1。

您会注意到,我们的输入只是温度,我们试图预测的输出是加热器状态(开或关)。这样有效吗?分类器能否仅根据温度判断加热器是开还是关?幸好有了 scikit-learn,使用监督分类器进行训练和预测只需要几行代码。我们还将使用逻辑回归分类器,但是还有许多其他监督分类器可以使用。

绘制结果会产生以下结果:

作者的情节

对于我们是否可以只使用原始温度作为输入,答案是断然否定的。这具有直观意义,例如,在 35°C 时,加热器要么打开,要么关闭。然而,温度的变化会告诉我们很多关于加热器在做什么。

特征工程

这介绍了特征工程的领域,这是为机器学习设置问题的另一个重要部分。我们可以在现有的基础上创造新的功能。从原始温度中我们还能收集到什么其他数据?我们可以得到一阶和二阶导数,过去几个读数的标准差,甚至可以看到长期的差异趋势。另一个可以尝试的技巧是对数据进行日志缩放,尤其是在数据是日志分布式的情况下。这些都是新特性,其中一些可能是分类器获得良好性能的灵丹妙药!

同样,一些特定问题的领域知识和直觉对于特征工程来说是非常有用的。让我们先来看看温度的变化。有一点传感器噪声,因此采用温度的滚动平均值会给出更好的曲线,这可能会导致更好的性能。

基于原始温度(dT)和滚动平均温度(dT_ave)的温度数据变化。作者的情节

让我们看看效果如何。唯一的区别是我们在 dataframe 中创建了dT列,并将其用作输入特性。我们还必须丢弃任何带有NaN值的数据。

结果…相当不错!有一些失误,但那些总是在加热器刚刚打开或关闭的边缘。我们可以研究更多的特性,例如二阶导数,这可能会有所帮助,但目前来看,这看起来就像它会做到这一点。

作者的情节

根据新数据验证分类器

在开始使用经过训练的分类器之前,最佳实践是验证从未见过的数据的性能。我个人喜欢在最初的原始训练数据上进行尝试,只是作为一种直觉检查;如果它在那里不起作用,它就不会对新数据起作用。然而,我们看到它在上述数据上运行良好,所以现在让我们在一组新的数据上进行检查。您可以从 TCLab 生成一个新的无异常数据文件,或者使用来自 scikit-learntrain_test_split 。请注意,我们仅在数据缩放上使用transform功能,因为我们已经安装了缩放器,并使用该缩放来训练分类器。

这是结果的图表:

作者的情节

已经核实了。同样,当加热器打开或关闭时,会出现边缘情况。我们可以研究额外的特性来输入到分类器中,或者甚至改变分类器的超参数,但是对于一个简单的演示来说,这已经足够了。

利用训练好的分类器进行异常检测

这里是橡胶接触路面的地方。我们知道分类器可以很好地告诉我们加热器是开着还是关着。当意想不到的事情发生时,它能告诉我们吗?

流程真的一样。唯一的区别是现在我们有一个数据异常。我们将创建新特征(温度变化,使用平滑滚动平均值),缩放数据,并将其输入分类器进行预测。

结果如下:

作者的情节

这看起来很有效!我们看到的预测是,当风扇运行时加热器关闭,而实际上加热器是打开的,这只是一个异常事件。即使在风扇关闭后,系统也在重新平衡,因此加热器的实际情况和分类器预测的情况之间存在一些额外的不匹配。如果一个操作者正在看进来的数据,他们可以比较他们期望加热器正在做什么和分类器正在预测什么;任何差异都表明异常,可以进行调查。

其他想法

这是一个很好的实验,只有一种异常,使用的是我们在桌面上生成的数据!当然这很简单,但是这类问题会很快变得复杂。然而,指导原则是相同的。

还有哪些异常事件可能发生?我们可以通过打开加热器 2 来模拟异常的外部热量。我们可以通过在更冷的房间中运行 TCLab 来模拟更冷的环境温度。这些设备还有一个巧妙的技巧:如果你在传感器附近打手机,电波会干扰信号,你会得到一种全新的异常。什么样的特征对检测这些其他类型的异常有用?我们可以调整超参数来提高性能吗?

最后,在分类器方面,也许我最喜欢的策略是训练几个不同的分类器,然后取一个总分数。继续尝试来自 scikit-learn 的一些其他类型的分类器。哪些好用?哪个不是?如果您发现 4 或 5 个工作得很好,您可以训练所有的分类器,然后只在大多数分类器显示不匹配的情况下将某些东西标记为异常。

这有帮助吗?你喜欢能够生成自己的数据吗?你还发现了哪些对异常检测有用的想法?感谢您的阅读,我希望这篇介绍为您自己的项目打开了一些新的思路。

时间序列传感器数据中的异常检测

原文:https://towardsdatascience.com/anomaly-detection-in-time-series-sensor-data-86fd52e62538?source=collection_archive---------0-----------------------

威尔·梅尔斯在 Unsplash 上拍照

异常检测包括识别数据集中与标准的差异、偏差和异常。它有时被称为离群点检测。

异常检测不是一个新概念或新技术,它已经存在很多年了,是机器学习的一个常见应用。其使用案例的真实示例包括(但不限于)检测欺诈交易、欺诈性保险索赔、检测异常设备行为的网络攻击。

在本文中,我将重点介绍异常检测在制造业中的应用,我认为与其他行业相比,制造业在有效利用机器学习技术方面远远落后。

问题描述

制造业被认为是重工业,其中他们倾向于使用各种类型的重型机械,如巨型电机、泵、管道、熔炉、传送带、拖运卡车、推土机、平地机和电铲等。这些通常被认为是他们运营中最重要的资产。因此,这些设备的完整性和可靠性通常是其资产管理计划的核心焦点。

他们如此关注这些资产的主要原因是,这些设备的故障通常会导致生产损失,从而导致数十万美元(如果不是数百万美元的话)的损失,这取决于运营的大小和规模。因此,对于制造工厂的维护经理来说,这是一件非常重要的事情,他们需要与高技能的可靠性工程师一起运行一个强大的资产管理框架,以确保这些关键资产的可靠性和可用性。

因此,提前检测异常情况并降低风险的能力是一项非常有价值的能力,可进一步防止计划外停机、不必要的维护(基于条件的维护与强制维护),还能更有效地管理这些资产的关键部件。计划外停机造成的生产损失、不必要的维护成本以及关键部件的过剩或短缺都会转化为严重的经济损失。

在这篇文章中,我将使用 Scikit-learn(又名 sklearn)在 Python 中实现不同的异常检测技术,我们的目标是使用无监督学习算法在泵的时间序列传感器读数中搜索异常。我们开始吧!

数据

很难从制造业中找到这种特定用例的公开数据,但我能够找到一个不完美的数据。数据集包含来自安装在泵上的 53 个传感器的传感器读数,以测量泵的各种行为。这个数据集可以在这里找到。

首先,我将使用以下代码和 Kaggle API 下载数据

!kaggle datasets download -d nphantawee/pump-sensor-data

下载完成后,用下面的代码将 CSV 文件读入 pandas 数据帧,并检查数据的细节。

df = pd.read_csv('sensor.csv')
df.info()<class 'pandas.core.frame.DataFrame'>
RangeIndex: 220320 entries, 0 to 220319
Data columns (total 55 columns):
 #   Column          Non-Null Count   Dtype  
---  ------          --------------   -----  
 0   Unnamed: 0      220320 non-null  int64  
 1   timestamp       220320 non-null  object 
 2   sensor_00       210112 non-null  float64
 3   sensor_01       219951 non-null  float64
 4   sensor_02       220301 non-null  float64
 5   sensor_03       220301 non-null  float64
 6   sensor_04       220301 non-null  float64
 7   sensor_05       220301 non-null  float64
 8   sensor_06       215522 non-null  float64
 9   sensor_07       214869 non-null  float64
 10  sensor_08       215213 non-null  float64
 11  sensor_09       215725 non-null  float64
 12  sensor_10       220301 non-null  float64
 13  sensor_11       220301 non-null  float64
 14  sensor_12       220301 non-null  float64
 15  sensor_13       220301 non-null  float64
 16  sensor_14       220299 non-null  float64
 17  sensor_15       0 non-null       float64
 18  sensor_16       220289 non-null  float64
 19  sensor_17       220274 non-null  float64
 20  sensor_18       220274 non-null  float64
 21  sensor_19       220304 non-null  float64
 22  sensor_20       220304 non-null  float64
 23  sensor_21       220304 non-null  float64
 24  sensor_22       220279 non-null  float64
 25  sensor_23       220304 non-null  float64
 26  sensor_24       220304 non-null  float64
 27  sensor_25       220284 non-null  float64
 28  sensor_26       220300 non-null  float64
 29  sensor_27       220304 non-null  float64
 30  sensor_28       220304 non-null  float64
 31  sensor_29       220248 non-null  float64
 32  sensor_30       220059 non-null  float64
 33  sensor_31       220304 non-null  float64
 34  sensor_32       220252 non-null  float64
 35  sensor_33       220304 non-null  float64
 36  sensor_34       220304 non-null  float64
 37  sensor_35       220304 non-null  float64
 38  sensor_36       220304 non-null  float64
 39  sensor_37       220304 non-null  float64
 40  sensor_38       220293 non-null  float64
 41  sensor_39       220293 non-null  float64
 42  sensor_40       220293 non-null  float64
 43  sensor_41       220293 non-null  float64
 44  sensor_42       220293 non-null  float64
 45  sensor_43       220293 non-null  float64
 46  sensor_44       220293 non-null  float64
 47  sensor_45       220293 non-null  float64
 48  sensor_46       220293 non-null  float64
 49  sensor_47       220293 non-null  float64
 50  sensor_48       220293 non-null  float64
 51  sensor_49       220293 non-null  float64
 52  sensor_50       143303 non-null  float64
 53  sensor_51       204937 non-null  float64
 54  machine_status  220320 non-null  object 
dtypes: float64(52), int64(1), object(2)
memory usage: 92.5+ MB

我们已经可以看到,数据需要一些清理,有丢失的值,一个空列和一个数据类型不正确的时间戳。因此,我将应用以下步骤来整理数据集。

  • 移除多余的列
  • 删除重复项
  • 处理缺失值
  • 将数据类型转换为正确的数据类型
# Drop duplicates
df = df.drop_duplicates()
# Entire "sensor_15" column is NaN therefore remove it from data
del df['sensor_15']
# Let's convert the data type of timestamp column to datatime format
import warnings
warnings.filterwarnings("ignore")
df_tidy['date'] = pd.to_datetime(df_tidy['timestamp'])
del df_tidy['timestamp']

接下来,让我们处理缺失值,为此,我们首先查看缺失值的列,并查看缺失数据的百分比。为此,我将编写一个计算缺失值百分比的函数,这样我就可以在整个笔记本中多次使用同一个函数。

# Function that calculates the percentage of missing values
def calc_percent_NAs(df):
    nans = pd.DataFrame(df.isnull().sum().sort_values(ascending=False)/len(df), columns=['percent']) 
    idx = nans['percent'] > 0
    return nans[idx]# Let's use above function to look at top ten columns with NaNs
calc_percent_NAs(df).head(10)

每列缺失值的百分比

经过一些分析后,我决定用它们的平均值来估算一些缺失的值,并去掉其余的。在数据争论过程之后,我最终整理的数据如下所示,并为下一步探索性数据分析做好了准备。tidy 数据集有 52 个传感器,机器状态列包含三个类别(正常、损坏、恢复),分别代表泵的正常运行、损坏和恢复状态,然后是日期时间列,代表时间戳。

整齐数据的前 10 行

探索性数据分析

既然我们已经清理了数据,我们可以开始探索以熟悉数据集。

在一些定量 EDA 之上,我执行了额外的图形 EDA 来寻找趋势和任何奇怪的行为。特别是,有趣的是看到传感器读数随着时间的变化而变化,机器状态“损坏”用红色标记在同一图表上。这样,我们可以清楚地看到泵何时出现故障,以及这如何反映在传感器读数中。下面的代码为每个传感器绘制了上面提到的图表,但是让我们看看 sensor_00 的图表。

# Extract the readings from the BROKEN state of the pump
broken = df[df['machine_status']=='BROKEN']
# Extract the names of the numerical columns
df2 = df.drop(['machine_status'], axis=1)
names=df2.columns
# Plot time series for each sensor with BROKEN state marked with X in red color
for name in names:
    _ = plt.figure(figsize=(18,3))
    _ = plt.plot(broken[name], linestyle='none', marker='X', color='red', markersize=12)
    _ = plt.plot(df[name], color='blue')
    _ = plt.title(name)
    plt.show()

从上面的图中可以清楚地看到,代表泵损坏状态的红色标记与传感器读数的观察到的干扰完全重叠。现在我们有了一个很好的直觉,当泵坏了和正常运行时,每个传感器读数是如何表现的。

平稳性和自相关性

在时间序列分析中,重要的是数据是平稳的,没有自相关。平稳性是指数据的平均值和标准偏差随时间变化的行为,具有这种行为的数据被认为是不平稳的。另一方面,自相关是指数据在不同时间段内与其自身相关的数据行为。下一步,我将直观地检查数据集中每个特征的稳定性,下面的代码将完成这一任务。稍后,我们还将执行 Dickey Fuller 测试来定量验证观察到的平稳性。此外,在将特征输入聚类算法以检测异常之前,我们将检查特征的自相关性。

# Resample the entire dataset by daily average
rollmean = df.resample(rule='D').mean()
rollstd = df.resample(rule='D').std()
# Plot time series for each sensor with its mean and standard deviation
for name in names:
    _ = plt.figure(figsize=(18,3))
    _ = plt.plot(df[name], color='blue', label='Original')
    _ = plt.plot(rollmean[name], color='red', label='Rolling Mean')
    _ = plt.plot(rollstd[name], color='black', label='Rolling Std' )
    _ = plt.legend(loc='best')
    _ = plt.title(name)
    plt.show()

时间序列看起来相当稳定

查看其中一个传感器(在本例中为 sensor_17)的读数,注意数据实际上看起来非常稳定,滚动平均值和标准偏差似乎不随时间变化,除非在预期的泵停机期间。该数据集中的大多数传感器都是这种情况,但在训练数据之前必须应用各种变换方法来使数据稳定的情况下,情况可能并不总是如此。

预处理和降维

用所有 52 个传感器/特征来训练模型在计算上非常昂贵,并且效率不高。因此,我将采用主成分分析(PCA)技术来提取用于建模的新特征。为了正确应用 PCA,必须对数据进行缩放和标准化。这是因为 PCA 和大多数学习算法都是基于距离的算法。如果从整洁数据的前 10 行注意到,每个特征的值的大小是不一致的。有些值非常小,而有些值非常大。我将使用管道库执行以下步骤。

  1. 缩放数据
  2. 执行主成分分析,并根据惯性查看最重要的主成分
# Standardize/scale the dataset and apply PCA
from sklearn.preprocessing import StandardScaler
from sklearn.decomposition import PCA
from sklearn.pipeline import make_pipeline
# Extract the names of the numerical columns
df2 = df.drop(['machine_status'], axis=1)
names=df2.columns
x = df[names]
scaler = StandardScaler()
pca = PCA()
pipeline = make_pipeline(scaler, pca)
pipeline.fit(x)# Plot the principal components against their inertiafeatures = range(pca.n_components_)
_ = plt.figure(figsize=(15, 5))
_ = plt.bar(features, pca.explained_variance_)
_ = plt.xlabel('PCA feature')
_ = plt.ylabel('Variance')
_ = plt.xticks(features)
_ = plt.title("Importance of the Principal Components based on inertia")
plt.show()

根据图表,前两个组件是最重要的

根据上述重要性图中 PCA 提取的特征,似乎前两个主成分是最重要的。因此,作为下一步,我将使用 2 个组件执行 PCA,这将是我在模型训练中使用的特征。

# Calculate PCA with 2 components
pca = PCA(n_components=2)
principalComponents = pca.fit_transform(x)
principalDf = pd.DataFrame(data = principalComponents, columns = ['pc1', 'pc2'])

现在,我将再次检查这两个主成分的平稳性和自相关性,以确保它们是平稳的,不是自相关的。

from statsmodels.tsa.stattools import adfuller
# Run Augmented Dickey Fuller Test
result = adfuller(principalDf['pc1'])
# Print p-value
print(result[1])

对第一个主成分运行 Dickey Fuller 测试,我得到的 p 值为 5.4536849418486247e-05,这是一个非常小的数字(远小于 0.05)。因此,我会拒绝零假设,说数据是平稳的。我对第二个组件执行了同样的操作,得到了类似的结果。所以两个主成分都是静止的,这就是我想要的。

现在,让我们检查这两个主成分的自相关性。有两种方法可以做到:使用 pandas autocorr()方法或 ACF 绘图。在这种情况下,我将使用后者来快速直观地验证没有自相关。下面的代码就是这样做的。

# Plot ACF
from statsmodels.graphics.tsaplots import plot_acf
plot_acf(pca1.dropna(), lags=20, alpha=0.05)

假设我的 PCA 的新特征是稳定的,并且不是自相关的,我已经准备好建模了。

建模

在这一步中,我将执行以下学习算法来检测异常。

  1. 基准模型:四分位距(IQR)
  2. k 均值聚类
  3. 隔离森林

让我们开始用这些算法进行训练。

四分位间距

策略:

  1. 计算 IQR,即第 75(Q3)和第 25th)百分位数之间的差值。
  2. 计算异常值的上限和下限。
  3. 过滤超出上限和下限的数据点,并将其标记为异常值。
  4. 最后,在时间序列数据(本例中是 sensor_11 的读数)的顶部绘制异常值
# Calculate IQR for the 1st principal component (pc1)q1_pc1, q3_pc1 = df['pc1'].quantile([0.25, 0.75])
iqr_pc1 = q3_pc1 - q1_pc1# Calculate upper and lower bounds for outlier for pc1lower_pc1 = q1_pc1 - (1.5*iqr_pc1)
upper_pc1 = q3_pc1 + (1.5*iqr_pc1)# Filter out the outliers from the pc1df['anomaly_pc1'] = ((df['pc1']>upper_pc1) | (df['pc1']<lower_pc1)).astype('int')# Calculate IQR for the 2nd principal component (pc2)
q1_pc2, q3_pc2 = df['pc2'].quantile([0.25, 0.75])
iqr_pc2 = q3_pc2 - q1_pc2# Calculate upper and lower bounds for outlier for pc2lower_pc2 = q1_pc2 - (1.5*iqr_pc2)
upper_pc2 = q3_pc2 + (1.5*iqr_pc2)# Filter out the outliers from the pc2df['anomaly_pc2'] = ((df['pc2']>upper_pc2) | (df['pc2']<lower_pc2)).astype('int')# Let's plot the outliers from pc1 on top of the sensor_11 and see where they occured in the time seriesa = df[df['anomaly_pc1'] == 1] #anomaly
_ = plt.figure(figsize=(18,6))
_ = plt.plot(df['sensor_11'], color='blue', label='Normal')
_ = plt.plot(a['sensor_11'], linestyle='none', marker='X', color='red', markersize=12, label='Anomaly')
_ = plt.xlabel('Date and Time')
_ = plt.ylabel('Sensor Reading')
_ = plt.title('Sensor_11 Anomalies')
_ = plt.legend(loc='best')
plt.show();

用红色标记的异常

如上所述,异常是在泵发生故障之前检测到的。对于操作员来说,这可能是一个非常有价值的信息,可以在泵实际停止工作之前正确地关闭泵。让我们看看是否能从接下来的两个算法中检测到相似的异常模式。

k 均值聚类

策略:

  1. 计算每个点与其最近质心之间的距离。最大的距离被认为是异常。
  2. 我们使用 outliers_fraction 为算法提供关于数据集中离群值比例的信息。不同数据集的情况可能有所不同。然而,作为一个开始的数字,我估计 outliers_fraction=0.13 (13%的 df 是 outliers,如图所示)。
  3. 使用离群值分数计算离群值的数量。
  4. 将阈值设置为这些异常值的最小距离。
  5. anomaly1 的异常结果包含上述方法簇(0:正常,1:异常)。
  6. 用时序视图可视化异常。
# Import necessary libraries
from sklearn.cluster import KMeans
# I will start k-means clustering with k=2 as I already know that there are 3 classes of "NORMAL" vs 
# "NOT NORMAL" which are combination of BROKEN" and"RECOVERING"kmeans = KMeans(n_clusters=2, random_state=42)
kmeans.fit(principalDf.values)
labels = kmeans.predict(principalDf.values)
unique_elements, counts_elements = np.unique(labels, return_counts=True)
clusters = np.asarray((unique_elements, counts_elements))# Write a function that calculates distance between each point and the centroid of the closest clusterdef getDistanceByPoint(data, model):
    """ Function that calculates the distance between a point and centroid of a cluster, 
            returns the distances in pandas series"""
    distance = []
    for i in range(0,len(data)):
        Xa = np.array(data.loc[i])
        Xb = model.cluster_centers_[model.labels_[i]-1]
        distance.append(np.linalg.norm(Xa-Xb))
    return pd.Series(distance, index=data.index)# Assume that 13% of the entire data set are anomalies outliers_fraction = 0.13# get the distance between each point and its nearest centroid. The biggest distances are considered as anomalydistance = getDistanceByPoint(principalDf, kmeans)# number of observations that equate to the 13% of the entire data setnumber_of_outliers = int(outliers_fraction*len(distance))# Take the minimum of the largest 13% of the distances as the thresholdthreshold = distance.nlargest(number_of_outliers).min()# anomaly1 contain the anomaly result of the above method Cluster (0:normal, 1:anomaly) principalDf['anomaly1'] = (distance >= threshold).astype(int)

异常用红色标记

隔离森林

# Import IsolationForestfrom sklearn.ensemble import IsolationForest# Assume that 13% of the entire data set are anomalies

outliers_fraction = 0.13model =  IsolationForest(contamination=outliers_fraction)
model.fit(principalDf.values) 
principalDf['anomaly2'] = pd.Series(model.predict(principalDf.values))# visualization
df['anomaly2'] = pd.Series(principalDf['anomaly2'].values, index=df.index)
a = df.loc[df['anomaly2'] == -1] #anomaly
_ = plt.figure(figsize=(18,6))
_ = plt.plot(df['sensor_11'], color='blue', label='Normal')
_ = plt.plot(a['sensor_11'], linestyle='none', marker='X', color='red', markersize=12, label='Anomaly')
_ = plt.xlabel('Date and Time')
_ = plt.ylabel('Sensor Reading')
_ = plt.title('Sensor_11 Anomalies')
_ = plt.legend(loc='best')
plt.show();

用红色标记的异常

模型评估

有趣的是,这三个模型都发现了许多相似的异常现象。仅仅从视觉上看上面的图表,人们可以很容易地得出结论,隔离林可能比其他两个检测到更多的异常。然而,下面的表格显示,相反,IQR 检测到的异常比 K-Means 和隔离森林要多得多。

每个模型检测到的异常数量

你认为这是为什么?当其他两个模型检测分布在不同时间段的异常时,IQR 是否主要检测相距较近的异常?IQR 比其他两位更科学吗?我们如何定义准确性?现在,让我把这些问题留给你去思考。我将在以后的帖子中更详细地写关于模型评估的更多内容。

结论

到目前为止,我们已经用三种不同的方法进行了异常检测。在此过程中,我们经历了常用数据科学流程的大部分步骤,包括以下步骤:

  1. 问题识别
  2. 数据角力
  3. 探索性数据分析
  4. 预处理和训练数据开发
  5. 建模
  6. 证明文件

在这个项目中,我面临的一个挑战是,使用无监督学习算法训练异常检测模型具有如此大的数据集,在计算上可能非常昂贵。例如,我不能用这些数据正确地训练 SVM,因为它花了很长时间来训练模型,却没有成功。

我建议接下来采取以下步骤,其中前 3 步侧重于改进模型,后两步则是让事情变得真实:

  1. 利用高级特征工程技术进行特征选择
  2. 高级超参数调谐
  3. 实现其他学习算法,如 SVM,DBSCAN 等。
  4. 使用给定测试集的最佳模型预测机器状态—砰!
  5. 将最佳模型部署到生产中——DOUBLE BAM!

除了实施上述步骤,我将继续改进模型,并计划在未来的另一篇文章中分享结果。

Jupyter 笔记本可以在 Github 上找到详情。享受检测异常,让我们在 LinkedIn 上联系。

在线参考和有用的资料

旁观者眼中的异常检测

原文:https://towardsdatascience.com/anomaly-detection-is-in-the-eye-of-the-beholder-9d2c6e9904ed?source=collection_archive---------40-----------------------

一副扑克牌揭示了如何检测异常值

作者图片

在网络安全中,异常检测的重点是发现可能被认为是网络攻击的异常事件。异常检测的承诺是,该算法将检测到以前从未见过的攻击。网络数据的一些问题是数据量太大,攻击数量很少,而且攻击每天都在演变。

机器学习的许多入侵检测应用依赖于监督学习。监督机器学习擅长检测已知的攻击。通过适当的拟合,有监督的机器学习算法甚至能够发现一些新的攻击。但是,异常检测会在没有预定义攻击特征的情况下重新审视数据。

异常是正常的

在处理网络流量数据时,异常检测算法可能会发现真正异常的事件,但它们可能不是您想要的事件。大型计算机网络是有节奏的。他们有定期运行的流程。他们通常有相同的用户,每天做相同的事情。但网络数据远非规律。网络上发生的一些事件会中断正常的程序。存在由系统错误、缺陷或错误配置导致的操作事件。服务器上部署了一些系统更改,以添加软件功能和修补安全漏洞。有些新用户开始工作,有些人的工作发生了变化,他们发现自己肩负着新的责任。随着所有这些变化的发生,哪些事件是真正的异常呢?

用异常检测算法在不断变化的网络生态系统中检测网络攻击是非常具有挑战性的,因为异常可能与利用企图无关。虽然看似矛盾,但异常是正常的。

重要的是要认识到,你发现的异常可能不是你实际寻找的异常。这适用于入侵检测以及许多其他领域的无监督学习。一个数据不一样,不代表它不好。在欺诈检测中,您可能会观察客户的购买模式,并在客户从未做生意的商店中发现不寻常的购买行为。这可能是欺诈行为,也可能只是客户决定做一些随机的、不同的或不正常的事情。这也是异常检测器误报率如此之高的原因之一。

你发现的异常可能不是你实际寻找的异常。

不要气馁,回到监督学习。有一些方法可以改进对您真正想要找到的事件的检测。你如何用你的算法得到更好的结果?

没有单一正确的集群方式

异常检测类似于聚类问题。聚类也是无监督学习的一个例子。在聚类中,目标是将彼此最相似而与其他组中的项目最不相似的项目分组在一起。聚类的一个已知问题是没有一种算法可以成功地对所有数据进行聚类。聚类的挑战在于,这些组可能由于非常不同的原因而相似或不相似。聚类可以用密度函数或距离函数来衡量,但即使这样,数据中的一些分组也不容易找到。例如,如果没有专门的聚类算法,数据中的某些连续形状可能不会很好地聚类。

这就是为什么埃斯蒂维尔-卡斯特罗宣称,“集群是在旁观者的眼中。”[1]他认为聚类是不同研究者如何描述一个聚类的数学定义。对于每一个归纳原则,有不同的聚类算法来满足研究者的意图。

一副牌揭示了什么

为了用您可能熟悉的数据集进行说明,请考虑一副扑克牌。一副标准的扑克牌有 52 张。每张卡都将是我们数据集中的一条记录。每张卡都有一组特征,例如花色、卡的面值和颜色。您知道每张卡都有一个 Unicode 值吗?我们也可以把它作为一个特性包括进来。大多数卡牌还会增加两张百搭牌。小丑牌有点反常,因为它没有花色或特定的颜色,但它有面值和 Unicode 值。

给定这副牌,如果让你把它们分成合理的组,你会如何组织它们?请记住,在聚类中,您的目标是选择分组,使每张卡片与其自己的聚类中的卡片最相似,而与其他聚类中的卡片最不相似。

你可以按花色分组。你可以把所有的方块放在一起,所有的红心放在一起,所有的梅花放在一起,所有的黑桃放在一起。结果是每种花色有 13 张牌。那些小丑呢?他们有点反常,所以也许他们应该进入他们自己的群体。

作者图片

你可以按颜色将卡片分组,所有的红色卡片在一组,所有的黑色卡片在另一组。因此每组有 26 张牌,但是小丑显然是个例外,因为他们不属于任何一个颜色组。

作者图片

最后,你可以按面值将卡片分组。您可以将所有的 a 放在一个组中,将所有的 2 放在另一个组中,依此类推。由于玩笑者在外表上彼此相似,你可以再次将他们归入他们自己的群中。在这种情况下,玩笑者可能看起来不是异常,因为他们有面值。

作者图片

哪个集群解决方案是正确的?每一个都是正确的。这取决于你的目标和你解决问题的方式。如果你的目标是将卡片分成尽可能少的组,那么颜色似乎是最好的特征。如果你想分成尽可能多的组,那么按面值分组是最有意义的。

您获得的解决方案取决于您用来对数据进行分组的功能。

如果无监督的机器学习算法是神奇的,那就太好了。如果是,你可以给算法一组数据,不需要任何初始分析,就能得到正确的解。但是从扑克牌的例子中可以看出,您得到的解决方案取决于您用来对数据进行分组的特性。

对于这个讨论,同样重要的是考虑到一些分组清楚地揭示了玩笑者是异常的,而其他解决方案没有。从这个简单的例子可以清楚地看出,异常检测也是因人而异的。

特征选择

将异常检测应用于入侵检测或任何其他应用程序时,最重要的步骤是选择最合适的功能。如果你想得到神奇的结果,你可以使用像 bagging 这样的方法来测试随机特性,但是不集中的特性选择可能不会给你真正想要的解决方案。相反,想想异常在您的网络和数据集的上下文中意味着什么。

选择特征时,您可能会发现数据集中存在非常自然的特征。在扑克牌的例子中,颜色、花色和面值都很简单。但是如果基本特征无助于指出异常呢?

也许您想看看一周中的某些天或一天中的某些小时有什么异常。从网络数据的日期特征中提取星期几和一天中的小时将允许算法发现一些异常活动。

也许你想看到一段时间内的模式。例如,在网络数据中,您可能希望找到用户以正常或异常模式登录到特定系统的模式。常规特征不提供这种上下文,因此您可能需要创建一个时间序列或从原始数据创建 N 元模型。

用用例定义异常

但是如果你在寻找几种不同类型的异常呢?例如,如果您既想区分用户正常登录到不同的系统,又想检测用户在一天中不同时间登录的事件,该怎么办?也许您还对网络上系统之间的网络行为模式感兴趣。

一种解决方案是为您想要检测的每种异常创建用例。在网络安全中,这些通常被称为滥用案例,因为它们不是关于计算机系统应该如何工作,而是它们可能如何受到攻击。以下是您可能用于入侵检测的一些示例用例:

  • 用户在一天中不寻常的时间登录
  • 两台计算机系统之间异常的网络流量
  • 异常状态消息
  • 系统帐户登录到不寻常的计算机
  • 不规则的登录模式

在您的用例被定义之后,您可以选择最有助于识别每个用例的异常的特性。然后,为每个用例分别运行异常检测算法,您可以关注您最感兴趣的异常。

使用不同异常检测器的结果,每一个都基于不同的特性集,有一些方法或公式来组合用例的结果是有帮助的。例如,如果四个用例中的三个表明一个事件是异常的,那么它可能比一个只触发一个用例的事件得到更高的分数。

虽然合并结果有助于区分异常的优先级,但是展示每个用例的得分也是很好的。向最终用户呈现这些信息提供了一些可解释性。根据触发警报的用例,用户将理解为什么警报被视为异常。

通过实验,您可能还会发现,您开始使用的一些用例是不值得的,只会制造噪音。在这些情况下,消除这些用例是有好处的,因为它们不会帮助找到您试图检测的异常。这将有助于降低你的假阳性率。

结论

使用机器学习的异常检测不是魔术。需要规划和分析来找到正确的方法并充分利用您的算法。

由于异常检测是因人而异,因此定义您想要检测的异常类型非常重要。您可以通过缩小与目标异常最相关的特征来做到这一点。

用例有助于确保您能够检测到数据集中可能出现的各种类型的异常。使用为每个用例选择的不同特性集分别运行您的异常检测算法,可以产生非常有趣且可解释的结果。

对于检测网络入侵,异常检测可以通过发现非网络攻击的异常事件来产生大量噪声。尝试使用这些方法来微调您的算法,并向响应结果的安全分析师提供更有意义的数据。

参考

[1] V. Estivill-Castro,为什么有这么多聚类算法:立场文件 (2002),ACM SIGKDD 探索时事通讯,4(1)

异常检测变得简单

原文:https://towardsdatascience.com/anomaly-detection-made-simple-70775c914377?source=collection_archive---------21-----------------------

利用 pycaret 包的信用卡诈骗案

在这个银行业的新时代,Stripe、Square 等金融支付服务巨头使信用卡支付无缝化,信用卡交易每年都以倍数增长(来源)。因此也导致信用卡诈骗报告每年翻一番(来源)。

按年份分类的信用卡欺诈报告(来源)

什么是异常?

异常是偏离正常或预期的东西。在信用卡交易中,大多数欺诈案件偏离了正常交易的行为。说什么是正常的交易行为是主观的,但是有不同类型的异常检测技术来发现这种行为。这里我们将讨论三种类型的技术。

来源

信用卡诈骗侦破案例:

我正在考虑卡格尔信用卡欺诈检测进行异常 detection⁴.

从 API 调用下载数据集:

首先,进入您的 Kaggle 我的帐户(https://www.kaggle.com/Your-Username/account)),进入“API”部分,点击“创建新的 API 令牌”,下载您的 API 令牌。您现在将下载一个名为“kaggle.json”的文件。你必须把这个文件上传到你的笔记本上。

from google.colab import files
uploaded = files.upload()

现在,您可以使用下面给出的代码来下载和解压缩数据集。

!pip uninstall -y kaggle
!pip install --upgrade pip
!pip install kaggle==1.5.6
!kaggle -v
!mkdir -p ~/.kaggle
!cp kaggle.json ~/.kaggle/
!chmod 600 ~/.kaggle/kaggle.json
!kaggle datasets download -d mlg-ulb/creditcardfraud
!unzip creditcardfraud.zip

预处理:

在给定的数据集中,284,807 笔交易中有 492 笔欺诈,我正在考虑一个 0.1 的样本用于我们的分析,而不会丢失离群分数用于进一步的无监督学习。异常分数是 0.001723。

设置初始化 pycaret⁵的环境,并创建转换管道,为建模和部署准备数据。

1。隔离林:

T 隔离森林算法通过随机选择一个特征,然后随机选择所选特征的最大值和最小值之间的分割值来分离观察值,类似地,通过创建随机决策 trees⁶.来构建分离因此,异常分数被计算为分离给定观察所需的条件的数量。

建立模型:

我们可以使用 pycaret 包中的 create_model 函数建立模型,可以使用下面的代码相应地修改参数。

使用 assign_model⁷函数寻找孤立森林异常分数和标签。

评价:

隔离林的 ROC_AUC 为 0.935。

2。基于角度的异常检测:

在中,基于角度的异常值检测通过比较距离向量对之间的角度来解决高维度问题,而不是使用距离 directly⁸.

建立模型:

我们可以使用 pycaret 包中的 create_model 函数建立模型,可以使用下面的代码相应地修改参数。

使用 assign_model 函数查找 ABOD 分数和标签。

评估:

基于角度的异常值检测的 ROC_AUC 是 0.777。

3。子空间离群点检测:

t子空间离群值通过寻找低维空间中具有异常低 density⁹.的数据的局部区域来识别

建立模型:

我们可以使用 pycaret 包中的 create_model 函数建立模型,可以使用下面的代码相应地修改参数。

使用 assign_model 函数查找 SOD 分数和标签。

评价:

子空间离群点检测的 ROC_AUC 为 0.942。

4。最小协方差行列式:

这是发现异常值的最稳健的方法之一,它考虑使用称为 MCD 估计量的稳健估计量。这说明了掩蔽效应:经典估计值会受到污染的强烈影响,以至于诊断工具(如马氏距离)无法检测异常值⁰.由于稳健距离对掩蔽效应不敏感,因此它们可用于标记异常值。

建立模型:

我们可以使用 pycaret 包中的 create_model 函数建立模型,可以使用下面的代码相应地修改参数。

使用 assign_model 函数查找 MCD 异常分数和标签。

评价:

最小协方差行列式异常值检测的 ROC_AUC 为 0.906。

比较结果:

从这个 kaggle 信用欺诈检测数据集的所有四种异常检测技术中,我们看到,根据 ROC_AUC,子空间异常检测相对给出更好的结果。

子空间异常值检测的异常值的 3d TSNE 图(黄色欺诈,蓝色正常)

请从这个 colab 查看这些异常检测技术的详细代码。

结论:

大多数银行和支付服务使用不同的异常评分方法,要么使用一个异常评分,要么对某些异常评分技术的分级异常评分进行平均,以确保不丢失任何欺诈交易。

参考资料:

  1. 按年份统计的信用卡统计
  2. 信用卡报表统计
  3. 人工智能的进展和趋势
  4. kaggle 信用卡欺诈检测
  5. pycaret
  6. 隔离林研究论文
  7. pycaret assign_model 。
  8. 基于角度的离群点检测研究论文
  9. 子空间离群点检测研究论文
  10. 稳健统计异常检测
  11. 最小协方差行列式估计量的快速算法

使用本福德定律的异常检测

原文:https://towardsdatascience.com/anomaly-detection-using-benfords-law-253eaa25e6c5?source=collection_archive---------18-----------------------

掷骰子得到 5 的几率有多大?当然是 1/6。从 1 到 100 之间随机选择的数字是 32 的几率有多大?1/100.

假设你下载了 2020 年的银行交易。随机交易的金额以 3 开头的可能性有多大?考虑到有 9 个可能的数字(省略 0 作为第一个数字),逻辑上你会猜测 1/9。令人惊讶的是,这是错误的。真实概率其实在 12%左右。第一个数字是 1 的概率惊人地超过了 30%。

那么这个规律是从哪里来的,我们又该如何运用呢?

历史

虽然通常被称为本福德定律,但和许多著名定律一样,它并不是以第一个发现它的人命名的。实际上,是一位名叫西蒙·纽康的天文学家在 19 世纪晚期注意到,在对数表中,一些页面比其他页面磨损得更多——尤其是前几页。他的发现后来被弗兰克·本福特重新发现,他继续做更多的实验来验证这一理论。

简而言之,该定律表明“真实”数据集中数字的首位数字不会以一致的概率出现。我们所说的“真实”是什么意思?这里,我们指的是自然出现的数字集合——银行账户交易、街道地址、数学常数。数字以 d (1,2,3…9)开头的概率由以下公式给出:

作者图片

如果我们画出第一位数字的预期频率,我们会得到以下图形:

作者图片

同样,我们可以对第二个数字做同样的事情。

作者图片

那么,我们如何利用新发现的知识呢?它最合乎逻辑的应用是在会计(尤其是审计)领域,但也可以在其他领域找到用途。这部法律被广泛记载,以至于在联邦、州和地方法院案件中经常被用作证据。

更多示例:

  • 如果你看过本·阿弗莱克主演的电影《会计师 T2》,你会看到他利用本福德定律揭露欺诈。
  • Ciaponia 和 Mandanici 在一篇研究论文中使用该定律来调查意大利大学是否故意使用虚假数据来支持他们的书籍。
  • 本福德定律曾被用于检测 2009 年伊朗选举中的选民欺诈。

利用法律

既然你理解了本福特定律的理论,让我们把它付诸实践。在我们的例子中,我们将检查大约 2,100 笔金融交易,看它们是否符合。如果我们检测到超出预期频率的数据,我们会将这些交易标记为可疑,并让其他人进一步调查。(代码和例子可以在我的 github 中找到。)

首先,我们需要创建一个预期频率的列表。出于我们的目的,我们将关注前两位数字,并利用 numpy 创建一个 10×2 矩阵。行将代表数字(0–9)。列将表示数字位置(第一或第二)。

现在,我们需要从我们的数据(2,100 笔交易)中计算每个位置上不同数字的频率。我们将首先把我们的数据加载到 pandas 数据框架中。然后,我们将清理数字(删除否定标识、逗号等)。)并提取第一个和第二个数字。

作者图片

为了便于与我们预先计算的预期概率进行比较,我们还将在另一个 numpy 数组中计算观察数据的概率。

根据 Nigrini 和 Mittermaier 在分析程序中使用本福特定律作为辅助手段,我们可以根据给定的α水平计算期望值的置信区间。

作者图片

我们可以在两个 numpy 数组中保存我们的上限和下限,然后比较我们的观察结果,看看我们是否落在计算置信区间之外。如果是这样,我们知道我们有可疑的数据。

为了形象化我们的发现,我们可以绘制第一个和第二个数字的分布,以及置信区间。

作者图片

作者图片

正如我们所看到的,在第一个数字中,我们看到 8 出现的频率比本福特定律预测的要高得多。在第二个数字中,我们发现 5 也出现得更频繁——实际上是第二个数字中出现频率最高的。

我希望你学到了一些东西,并能够找到本福特定律的另一个有用的应用。同样,代码在我的 github 上。

感谢您的阅读。

关注我的博客

将新内容直接发送到您的收件箱。

原载于 2020 年 10 月 1 日 http://lowhangingfruitanalytics.com

TensorFlow 2.0 中的自动编码器异常检测

原文:https://towardsdatascience.com/anomaly-detection-with-autoencoders-in-tensorflow-2-0-a1a6c9ebf495?source=collection_archive---------6-----------------------

如何在 TensorFlow 2.0 中实现神经网络以检测异常的指南。

在这篇详细的指南中,我将解释深度学习如何用于异常检测领域。此外,我将解释如何在 TensorFlow 2.0 中实现用于异常检测的深度神经网络模型。当然,所有的源代码和相应的数据集都可以下载——很好;)

目录

  1. 简介
  2. 异常检测
  3. 异常检测系统的用例
  4. 异常案例研究:金融欺诈
  5. 自动编码器如何工作?
  6. 使用自动编码器的异常检测
  7. tensor flow 2.0 中的欺诈检测

1.介绍

异常是指与数据集中的其他实例明显不同的数据实例。通常它们是无害的。这些只能是数据中的统计异常值或错误。但有时数据中的异常可能表明以前已经发生了一些潜在的有害事件。

此类事件可能是金融欺诈。

在本文中,我将向您展示如何使用深度神经网络的力量来识别欺诈性的金融信用卡交易,这些交易可以被识别为数据集中的异常。

2.异常检测

不幸的是,常规前馈神经网络不太适合检测异常。正因为如此,我将介绍一种新的神经网络架构,称为 Autoencoder。

但首先,我想更详细地解释术语异常,并说明为什么异常检测系统在预测分析领域发挥如此重要的作用。

在分析真实世界的数据时,一个常见的观察结果是,数据集中的一些实例或观察结果可能会让人产生怀疑,因为它们与数据集中的大多数数据有很大的不同。这些特定的数据实例是不同的,因为它们不匹配数据集中的其他预期模式或行为。这种情况通常被称为异常。

请考虑以下由 4 个要素组成的数据集。

仔细观察会发现这个数据集中的一些不规则之处。第二个和第五个数据实例中的值与其他实例中的值非常不同。这两个实例的特征值与其他实例的相应特征值有很大不同。

显然,我们在这里观察到两个异常现象。数据中的异常可能有几个原因。有时,异常是在数据收集或预处理过程中出现的数据错误。

当然,也有很大的可能性,异常可能属于实际数据。在这种情况下,它们只不过是一些统计异常值。另一方面,异常有时可以指示一个新的、先前未知的潜在事件,该事件首先导致这些异常。

在这种情况下,数据中的异常可能表明欺诈、滥用或服务中断等事件,所有这些都对企业或组织构成威胁。你现在可能会问自己的问题是,为什么我们可以将这些事件视为数据中的异常。

简单的答案是,欺诈、滥用或服务中断当然是不符合企业或组织中通常的、预期的行为或流程的事件。事实上,这些都是相当罕见的事件。

3.异常检测系统的用例

让我们来看看某个特定的业务领域,数据中的异常可能意味着潜在的威胁或问题。

银行:在银行领域,异常可能与异常高的购买/存款或网络入侵有关

医疗保健:在医疗保健领域,索赔和支付方面的欺诈可能是异常现象。但更重要的是,患者健康数据的异常可以暗示疾病或健康状况的恶化

制造:在制造过程中,异常的机器行为可以被记录为机器产生的数据中的异常

金融:正如前面的例子中已经提到的,在金融领域(也可以属于银行业,反之亦然),欺诈性金融交易可以被注册为异常数据实例

智能家居:在智能家居的情况下,能量泄漏会导致数据中出现一些意想不到的观察结果

电信:在电信漫游滥用、收入欺诈、服务中断可能被识别为数据中的异常实例。

从这些例子中可以看出,异常检测是各种业务领域的重要组成部分。异常检测模型可以保护公司和机构免受财务和个人损失。甚至成为医疗保健领域的潜在救星。

4.异常用例:金融欺诈

以诈骗为例。想象一下,你正在从你的银行账户里取钱。你每周做一次,每次都是在你上班时间从自动取款机上取。每当金额在 100-250 美元之间时,你就在取款。当然,每次您取款时,您的银行都会收集与取款相关的数据,如时间、地点、金额等。

只要您坚持您通常的模式,收集的数据实例看起来几乎是一样的。现在想象一下,你的卡被偷了,小偷发现了你的 ATM 个人识别码。小偷利用这个机会,从一台不在你家乡的自动取款机上提取了一大笔远远超过你平时数额的钱。

正如您可以想象的那样,包含特定现金提取相关信息的数据实例在金额和提取地点方面与前面的实例有很大不同。

在这种情况下,数据实例将作为异常引起注意。

当然,异常和它可能暗示的威胁类型取决于行业和相关的数据类型。在任何情况下,异常检测模型的目标是检测异常数据,以便采取措施进一步调查检测到的异常,并避免公司或其客户可能面临的威胁或问题。

5.自动编码器是如何工作的?

在本文的其余部分,我将只把金融欺诈作为异常检测的案例研究。

这是我们可以利用深度神经网络的时候了。神经网络是通用的模式识别系统,可以识别模式,并将其与我们人类永远不会考虑的欺诈行为相关联。

例如,在准备交易时,停留在特定按钮或屏幕区域上的时间等模式。

一个人可能永远不会发现这种模式是否与某种欺诈行为有关。然而对于神经网络来说,这根本不是问题。非常适合于识别这种模式的神经网络架构被称为自动编码器。

向您详细解释这种神经网络架构肯定超出了本文的范围。相反,我想给这个网络一个简短的概述。关于 Autoencoder 更详细的解释,请随意阅读文章“tensor flow 2.0 中的 Deep auto encoder”。

自动编码器最简单的形式是你已经熟悉的前馈神经网络。与前馈神经网络一样,自动编码器有一个输入层、一个输出层和一个或多个隐藏层。下图显示了自动编码器的体系结构。正如我们所见,自动编码器的输入层和输出层具有相同数量的神经元。

自动编码器可以分为两部分:

  • 编码器
  • 解码器

编码器

编码器指的是自动编码器的前半部分,其中隐藏神经元的数量随着网络的深入而减少。

神经元数量的减少迫使输入特征 x 被“编码”或压缩成可以在中间隐藏层中找到的更短的表示。我们姑且把这种 x 的表示称为隐向量

解码器

解码器代表自动编码器的一部分,其中隐藏层中的神经元数量再次增加。

解码器使用编码到中间隐藏层中的输入特征的更短的潜在表示来重构原始输入特征 x 。让我们称重构的输入为 x_hat

如果解码器能够从比这短得多的向量中重构输入特征 x ,这意味着 x 包含大量不相关的信息,并且可以被丢弃。**

描述x100 的编码和解码的整个过程的等式如下:

这里 W1W2W3W4 表示连接自动编码器各层的权重矩阵。西格玛代表任意的非线性激活函数。

这种潜在表示 z 非常重要,因为它可以用于许多不同的目的,比如检测金融交易中的欺诈,我们几分钟后就会看到。首先,我们将讨论如何训练自动编码器,以便获得输入的准确潜在表示。

自动编码器的训练

自动编码器的训练方式与前馈神经网络相同。我们必须最小化输入特征和它们的重建对应物 x_hat 之间的距离。

xx_hat 之间的距离可以用均方误差损失函数来恰当地度量。为了最小化距离或损失函数的值,我们必须使用常规梯度下降法:

通过最小化 MSE 损失函数,我们减小了 xx_hat 之间的差异。这些自动导致一个更好的潜在表象 z 的输入特征 x

6.使用自动编码器的异常检测

现在,我们已经了解了什么是自动编码器以及如何训练它,让我们讨论如何使用这种神经网络架构来检测数据中的异常。

我们必须清楚一个事实,即数据集中的异常是非常罕见的事件。

这意味着应该检查任何类型异常的数据集是非常不平衡的。数据集中的绝大多数实例将是绝对正常的——与极少数异常数据实例形成对比。

客观地说,在接下来的异常检测示例中,我们将使用的数据集将包含超过 280,000 个信用卡交易数据实例,其中只有 492 个是欺诈性的。这只是可被视为异常的数据实例的 0.17%。这种不平衡是一个严重的问题。

提到的数据集就是著名的“信用卡诈骗数据集”。它包含欧洲持卡人在 2013 年 9 月通过信用卡进行的交易。该数据集显示了两天内发生的交易,其中 284,807 笔交易中有 492 笔欺诈。

数据集如下所示:

  • 特征 V1 ,…, V28 是用 PCA(主成分分析)得到的主成分
  • ****“时间”:每笔交易与第一笔交易之间经过的秒数
  • ****【金额】:交易金额
  • ****“类”:是我们的标签。1 表示欺诈交易,否则为 0

用于异常检测的训练自动编码器

我们将很难像以前一样执行基于特征标签的神经网络监督训练。因为这一次我们没有足够的数据实例来描述我们想要检测的异常情况——在我们的案例中是欺诈交易。

因此,我们的模型可能很难了解异常或欺诈交易的一般概念,因为模型几乎从未见过这种情况。此外,我们需要将数据集划分为训练集、测试集和验证集,这将进一步减少相应数据集中的异常数量。

数量非常有限的异常数据实例的一般结果是,我们的模型在对这些事件进行分类时效率非常低,因为在绝大多数情况下,模型仅从正常数据实例中学习。幸运的是,现在我们可以使用自动编码器来帮助我们。我们可以使用这种神经网络的独特性质来解决数据集非常不平衡的问题。

这意味着,我们将自动编码器的预测与初始输入特征进行比较,而不是提供将输入特征分类为欺诈性或非欺诈性的标签。

此外,在欺诈检测的情况下,我们必须只在非欺诈数据实例上训练自动编码器。在训练期间,自动编码器将看到数百万次非欺诈性的信用卡交易。

通过这种方式,我们使用自动编码器将输入要素的基础信息或最相关的信息编码成更短的潜在表示。直觉上,我们可以说,这样自动编码器只学习绝对正常的信用卡交易的概念。

并且这种学习到的正常信用卡交易的概念可以作为中间层中的潜在表示被发现,其用于重新创建用作自动编码器的输入数据的原始特征。

使用经过训练的自动编码器检测异常情况

在用正常数据实例训练之后,我们可以最终使用神经网络来检测异常。

这一次,我们可以显示两种类型的网络数据——异常数据和正常数据。如前所述,输入特征由自动编码器编码成用于重构输入的潜在表示。

如前所述,自动编码器使用均方误差函数作为损失函数来测量重构输入和原始输入之间的差异或误差。

****请注意以下几点:如果自动编码器经过适当训练,我们预计正常数据的输出和输入之间的误差会非常小。

然而,对于异常情况或我们这里的欺诈性数据交易来说,情况并非如此。

请记住,在培训过程中,autoencoder 仅学习普通数据实例的概念和特征。这意味着 autoencoder 的权重和偏差仅被调整为编码和重建正常数据——在我们的案例中为非欺诈性交易。

如果我们现在试图对欺诈性交易进行编码,该交易的潜在表示将与正常交易的潜在表示显著不同。

直接结果是,重建的输入将与原始输入相差更大,导致比正常交易情况下更大的误差。

欺诈性输入数据会导致均方误差损失函数的损失值更高,这一事实可以为我们所用。我们所需要做的就是找到一个损失阈值来区分正常数据和欺诈数据。

在实践中,这意味着我们获得的损失值高于该阈值的数据实例将该实例归类为异常或欺诈交易。

另一方面,丢失值低于此阈值的数据实例可被视为正常数据或非欺诈性交易。

  • 情况 1: 输入特征的 MSE 损失值为高于损失阈值 →输入特征为异常(此处:欺诈交易)
  • 情况 2: 输入特征的 MSE 损失值低于阈值 →输入特征正常(此处:非欺诈交易)

概述:使用自动编码器进行异常检测

让我们总结一下关于如何检测数据集中的异常的知识。

  1. 首先,如果应该检查异常的数据集是不平衡的(几乎总是这样),您必须使用自动编码器
  2. 仅在正常数据上使用均方误差损失函数来训练自动编码器,在训练期间不使用任何异常数据实例
  3. 在训练之后,异常的损失值应该比正常数据实例的损失值高得多
  4. 现在,您必须找出一个损失值阈值,该阈值能够最好地区分异常数据和正常数据。

找到一个合适的损失值阈值

为了找到最佳区分异常数据和正常数据的阈值,您只需尝试许多不同的阈值。

对于这个过程,我建议取几千个数据实例,其中大约 5–10%的实例是异常的。然后,您只需计算每个实例的损失值,将其与阈值进行比较,并将其归类为异常或不异常。

最后,您必须使用实际的标签来检查这些分类有多好。为了量化分类结果,我建议使用我们在上一篇文章“数据科学和机器学习中的评估指标”中介绍的评估指标。

有了评估指标的结果,您就可以决定这个阈值是否良好,并在必要时调整阈值。

7.TensorFlow 2.0 中的欺诈检测

正如您可能已经猜到的,异常检测模型将是一个自动编码器,它将识别前面介绍的数据集中的欺诈性金融交易。

所有的源代码和使用的数据集都可以在这个项目的我的 GitHub 库中访问。请随意下载代码并亲自试用。

不幸的是,我不能详细介绍所有使用的函数和类,因为这超出了课程的范围。相反,我想只关注神经网络模型的实现。所有其他使用的类和方法(特别是数据预处理可以在 GitHub 库中查看)。所有的源代码都有很好的文档记录,因此您在理解代码时不会有任何困难。

在这一点上,让我们开始…

首先,我喜欢定义一个 BaseModel 类,它包含初始化权重和偏差以及计算正向传递的方法。

这些方法稍后将被继承到定义实际异常检测模型的类中。通过这种方式,我们可以稍后定义几个自动编码器模型(例如,使用不同的超参数),它们将从 BaseModel 类继承方法。通过这样做,我们可以节省相当多的代码,因为模型的不同版本共享相同的功能。

init _ variables(self)中我们定义了权重和偏差。正如你所看到的,自动编码器总共有三个隐藏层。这些层中神经元的数量是[20,8,20]。

forward_propagation(self,x) 只计算自动编码器的输出,即重构的输入特征

在下面,我们可以定义自动编码器将被训练的实际类:

该类包含计算均方误差损失值和梯度下降步骤的方法。

培训过程应该一目了然:

首先,我们预测几个超参数,如批量大小、学习速率等。

一个非常重要的超参数是引入的损失值阈值,我在这里称之为 THRESHOLD=10 。MSE 损失函数值将在测试阶段从正常数据实例中区分异常。

然后我们定义类 AnomalyDetector()、的实例,这是实际的 Autoencoder 模型和性能(阈值),其中将计算一些评估指标(精度、召回、F1 分数)。如果您不熟悉这些指标,请随时查看文章“深度学习中的评估指标

训练和评估数据集先前被转换成 TensorFlow 记录数据格式。在方法 get_training_data()get _ test _ data()中,我使用了 tf.data API 来提取、加载、生成小批量和混洗数据集。

在下一步中,我们迭代训练数据集,为每个小批量计算均方误差损失函数,并应用梯度下降。

在处理了 1000 个小批量之后,我使用了 Performance() 类和 loss-value-threshold 来评估异常检测器模型。测试数据集中有 34000 个实例。测试数据集包含所有 480 个欺诈性数据实例(异常),其余为正常数据实例。

就在 5 个时期之后,我们得到以下结果:

  • 异常检测器具有非常好的精度值 93。这意味着模型在 93%的情况下正确地对异常进行分类。
  • 召回值 43.3 告诉我们,自动编码器已经识别了数据集中 43.3%的异常。不幸的是,超过一半的异常对模型来说仍然是不可见的。

有趣的是,对于非欺诈性数据实例,平均绝对误差损失值仅为 0.385。另一方面,欺诈性数据实例的 MSE 损失值是高得多的值 19.518。正如我们所期待的那样。

最初发布于https://www . deep learning-academy . com/p/ai-wiki-anomaly-detection

基于局部异常因子的异常检测(LOF)

原文:https://towardsdatascience.com/anomaly-detection-with-local-outlier-factor-lof-d91e41df10f2?source=collection_archive---------10-----------------------

照片由 Z SUnsplash

小型数据科学

今天的文章是我写的关于用于异常检测的不同技术的一系列“小文章”中的第 5 篇。如果有兴趣,以下是之前的四篇文章:

今天,我将超越统计技术,进入用于异常检测的机器学习算法。

什么是本地异常因素(LOF)?

LOF 是一种无监督(半监督)的机器学习算法,它使用分布中数据点的密度作为检测异常值的关键因素。

LOF 将任何给定数据点的密度与其相邻数据点的密度进行比较。由于异常值来自低密度区域,因此异常数据点的比率会更高。根据经验,正常数据点的 LOF 在 1 到 1.5 之间,而异常观测值的 LOF 要高得多。LOF 越高,越有可能是异常值。如果点 X 的 LOF 是 5,这意味着 X 的邻居的平均密度比其局部密度高 5 倍。

用数学术语来说,

LOF(X)=[(LRD(1st neighbor) + LRD(2nd neighbor ) + .................+ LRD(kth neighbor))/LRD(X)]/k

其中,LRD 是本地可达性距离,计算方法如下。

LRD(X) = 1/(sum of Reachability Distance (X, n))/k)where n is neighbors upto k

该算法有四个不同的部分:

  • 超参数 k :决定邻居的数量
  • 可达性距离:使用 3 种方法测量的距离——欧几里德、闵可夫斯基、曼哈顿
  • 局部可达性: (LRD) (X) = 1/(可达性距离之和(X,n))/k),其中 n 是 k 以内的邻居
  • 本地异常值因子(LOF)

理论和数学讲够了。如果你不太明白,不要难过。正如我过去常说,要驾驶一辆汽车,我们不需要了解它的机械结构,但我们需要知道如何驾驶!因此,请直接进入下一节,讨论用 Python 实现 LOF。

Python 实现

我们将使用 Scikit-Learn 库实现 Python 环境中异常检测的 LOF。让我们首先导入所需的库:

# data preparation
import pandas as pd
import numpy as np# data visualzation
import matplotlib.pyplot as plt
import seaborn as sns# outlier/anomaly detection
from sklearn.neighbors import LocalOutlierFactor

现在让我们创建一个包含 5 个数据点的假设数据集。

# data
df = pd.DataFrame(np.array([[0,1], [1,1], [1,2], [2,2], [5,6]]), columns = ["x", "y"], index = [0,1,2,3,4])

如果你绘制数据点,用目视检查找出异常值并不困难。

# plot data points
plt.scatter(df["x"], df["y"], color = "b", s = 65)
plt.grid()

编造数据点

所以的确,我们不需要机器学习算法来发现第 5 个数据点是异常值。但是让我们看看算法是否能检测出来。

# model specification
model1 = LocalOutlierFactor(n_neighbors = 2, metric = "manhattan", contamination = 0.02)# model fitting
y_pred = model1.fit_predict(df)# filter outlier index
outlier_index = where(y_pred == -1) # negative values are outliers and positives inliers# filter outlier values
outlier_values = df.iloc[outlier_index]# plot data
plt.scatter(df["x"], df["y"], color = "b", s = 65)# plot outlier values
plt.scatter(outlier_values["x"], outlier_values["y"], color = "r")

使用 LOF 检测异常数据点

这就对了。该算法正确地检测出异常值。

总结和结论

本文的目的是介绍一种基于密度的异常检测技术——局部异常因子。LOF 将给定数据点的密度与其相邻数据点的密度进行比较,并确定该数据是正常的还是异常的。由于有了sklearn库,这个算法的实现并不太困难。对结果的解释也相当直接。

为了只关注一件事,我忽略了LocalOutlierFactor()算法的另一个重要用例——新奇感检测。这是另一篇文章的主题,但简单来说,LOF 是一种半监督 ML 算法,其中该算法仅在正常数据上训练。在训练算法之后,显示新的数据来识别它是否新颖。

希望你喜欢这篇文章,欢迎随时关注我的媒体推特

异常检测与实例

原文:https://towardsdatascience.com/anomaly-detection-with-practical-example-d06b90f89caf?source=collection_archive---------14-----------------------

异常检测服务

假设你在一家金融科技公司做系统管理员。前端可能会出现问题,阻止您的客户在您的平台上购物。你如何知道当你的服务仍然正常运行时,你的客户支出是否突然下降?这时异常检测就派上了用场。

马库斯·温克勒在 Unsplash 上的照片

异常检测是一种在给定集合中发现异常点或异常模式的技术。术语异常也被称为异常值。离群值是在数据集中的其他对象中脱颖而出并且不符合数据集中的正常行为的数据对象。异常检测是一种数据科学应用程序,它结合了多种数据科学任务,如分类、回归和聚类。

异常可以大致分为:

  • 点异常:如果数据的单个实例离其他实例太远,那么它就是异常的。业务用例:根据“消费金额”检测信用卡欺诈
  • 上下文异常:异常是上下文特定的。这种异常在时间序列数据中很常见。业务用例:节日期间每天在食物上花费 100 美元是正常的,但在其他情况下可能是奇怪的。
  • 集体异常:一组数据实例集体帮助检测异常。业务用例:有人试图将数据从远程机器意外地复制到本地主机,这种异常会被标记为潜在的网络攻击。

异常检测类似于噪声消除和新奇检测,但并不完全相同。新奇感检测关注的是在新的观察中发现一种未被观察到的模式,这种模式不包括在训练数据中——比如圣诞节期间突然对 YouTube 上的一个新频道感兴趣。噪声去除 ( NR )是使分析免于出现不需要的观察结果的过程;换句话说,从有意义的信号中去除噪声。

异常检测中的数据类型

  • 离群值

离群值是与数据的其余部分有很大不同的值或点。

异常值可能是这样的:

来自 Datanee

或者这个

来自塔努卡的博客

  • 四分位间距

四分位距 (IQR)是一种可变性的度量,基于将数据集分成四分位。

四分位数将按等级排序的数据集分成四个相等的部分。划分每个部分的值称为第一、第二和第三四分位数;并且它们分别由 Q1、Q2 和 Q3 表示。

  • Q1 是排序数据集的第一个一半的中的“中间”值。
  • Q2 是集合中的中值
  • Q3 是排序数据集的第第二半部分的“中间”值。

四分位间距等于 Q3 减去 Q1。

来自维基百科

  • 电平移动

时间序列中的电平移动是指在特定的时间步长上,过程的标称值从一个电平移动到另一个电平。在两个连续的电平变化之间,该过程可以表现得像标准的自回归移动平均(ARMA)过程。

电平转换的两个例子:

  • 当日交易股票,从某一天的收盘到第二天的开盘价不等
  • 车间内的温度或湿度传感器,用于记录从一个加工操作结束到下一个加工操作开始之间的液位变化

这实际上意味着一些值会周期性地(或非周期性地)偏离过程平均值,从而导致异常值。电平移动分析通常是使用 ARIMA 等模型的时间序列分析的一部分。

来自交叉验证

  • 道钉

棘波分类是一类用于电生理数据分析的技术。棘波分类算法使用通过大脑中的一个或多个电极收集的波形的形状来从背景电噪声中区分一个或多个神经元的活动。

从研究门

  • 季节性(日时、周时、月、假日)

在时间序列数据中,季节性是指以少于一年的特定固定间隔出现的变化,如每周、每月或每季度。季节性可能是由各种因素引起的,如天气、假期和假日,它由周期性的、重复的、通常有规律的和可预测的时间序列模式组成。

时间序列中的季节性波动可以与周期性模式相对照。后者发生在数据呈现非固定周期的上升和下降时。这种非季节性波动通常是由于经济状况,往往与“商业周期”有关;它们的周期通常超过一年,波动通常至少两年。

面临季节变化的组织,如冰淇淋供应商,通常有兴趣了解他们相对于正常季节变化的表现。劳动力市场的季节性变化可以归因于离校生进入就业市场,因为他们的目标是在完成学业后为劳动力做出贡献。对于那些研究就业数据的人来说,这些有规律的变化不如由于经济的基本状态而发生的变化有趣;他们关注的是劳动力中的失业率是如何变化的,尽管有规律的季节性变化的影响。

组织有必要识别和衡量市场中的季节性变化,以帮助他们规划未来。这可以让他们为劳动力需求和库存的暂时增加或减少做好准备,因为对他们产品或服务的需求在一定时期内会有波动。这可能需要提前组织培训、定期维护等等。除了这些考虑之外,组织需要知道他们所经历的变化是多于还是少于预期的量,超出了通常的季节性变化的范围。

来自 simplexCT

使用 Grafana 和 Hastic 插件进行异常检测

我们将通过一个例子来说明异常检测是如何在 Grafana 和 Hastic 插件的帮助下工作的。为了与示例保持一致,我们需要安装和设置以下内容:

  • 格拉夫纳
  • InfluxDB
  • 石墨
  • Grafana 的 Hastic 插件
  • 要测试的服务
  • 运行负载测试的 Gatling 环境

这个想法是这样的:

  1. 我们将在一个特定的服务上用 Gatling 运行负载测试
  2. loadtest 的测试结果将被加载到 InfluxDB 中的 Graphite 中
  3. 我们设置了 Grafana 和 Hastic 插件来跟踪视觉效果

为了了解如何运行与 Grafana 集成的加特林,您可以参考可视化实时加特林报告

因为这篇文章的目的是向展示异常检测是如何工作的,我们将进入在 Grafana 设置 Hastic 的细节。

从 Grafana 中,选择配置->数据源

Hastic 数据源

在 Hastic 数据源信息中,我们设置从浏览器获取信息,然后点击“保存和测试”。

现在,我们将使用数据源 InfluxDB 和 Hastic visual 创建仪表板。

从 Grafana 中选择创建新仪表板。这里你选择可视化。

然后将哈斯蒂克图形设置为可视化

哈斯蒂克图形可视化

在查询选项卡中,选择 InfluxDB。如果您的时间序列数据库中存在数据,Grafana 将自动建议可用的查询。

涌入数据库查询

然后,当我们保存您的仪表板并运行测试时,我们可以看到异常数据被突出显示。

Hastic 异常仪表板实时

感谢您阅读我的帖子。

平安一如既往~~~。

参考

[## 异常检测

异常检测是在给定的数据集中发现异常值的过程。离群值是突出的数据对象…

www.sciencedirect.com](https://www.sciencedirect.com/topics/computer-science/anomaly-detection) [## 异常检测

11.1.1 研究问题异常检测是一种在给定集合中寻找异常点或模式的技术。的…

www.sciencedirect.com](https://www.sciencedirect.com/topics/engineering/anomaly-detection) [## 异常检测

在各种领域,例如,但不限于,统计,信号处理,金融,计量经济学,制造业…

en.wikipedia.org](https://en.wikipedia.org/wiki/Anomaly_detection) [## 异常检测简介

特定主题的经验:新手专业经验:无行业经验本概述旨在…

blogs.oracle.com](https://blogs.oracle.com/datascience/introduction-to-anomaly-detection) [## 四分位范围:定义

四分位数范围的定义,来自 Stat Trek 统计术语和概念词典。这个统计数据…

stattrek.com](https://stattrek.com/statistics/dictionary.aspx?definition=interquartile range) [## 四分位间距

在描述性统计中,四分位距(IQR)也称为中间分布、中间 50%或 H 分布,是一个…

en.wikipedia.org](https://en.wikipedia.org/wiki/Interquartile_range) [## 时间序列中的移位级别是什么意思?

答:时间序列中的水平移动是指在特定的时间步长上,名义…

www.quora.com](https://www.quora.com/What-is-meant-by-a-shift-level-in-a-time-series)

匿名数据集

原文:https://towardsdatascience.com/anonymizing-data-sets-c4602e581a35?source=collection_archive---------14-----------------------

获取匿名数据集的快速方法

什么是匿名化?

我们今天收集的许多数据可以很容易地与个人、家庭或实体联系起来。然而,使用数据而不注意保护数据所有者的身份会导致许多问题和潜在的诉讼。数据匿名化简单地说,就是确保我们不能通过查看数据来判断实际的数据所有者。

作者照片

在开始任何命名练习之前,理解数据集的最终目标是很重要的。数据集的目标告诉我们如何使用它。例如,为分析项目准备数据集不同于为应用程序开发准备数据。为了简单起见,我们将关注分析性和非分析性目标。

本文将带我们了解不同类型的数据集和几种匿名化它们的方法。它还讨论了如何在分析环境中应用匿名化方法,并以一些我们可能需要匿名化的数据集的实际例子结束。

数据集的类型

常用的数据集有三种类型。具有组合键的数据集、具有非组合键的数据集和具有可重复 id 的数据集。包含从数据集组件创建的键的数据集(组合键)应该与键与任何列都没有关系的数据集(非组合键)区别对待。同样,具有重复键的数据集应该与具有唯一 id 的数据集区别对待。

示例 1:

两种表格

示例 1 中的表 A 有一个由表中的一些列组成的键,而表 B 是一个键与其余数据无关的示例。匿名表 A 应该不同于 b。

可能使用复合 id 的真实情况包括但不限于

一些医院系统使用客户年龄和姓名的组合来创建标识符。

公司电子邮件通常是员工姓名的某种组合

车辆或贷款编号通常基于一些车辆或贷款信息等

重复的 id 通常出现在收集和存储使用或访问数据的系统中。也就是说,有几种方法可以匿名化这些数据集。

匿名化数据集的主要方法

1。更换钥匙

在某些情况下,移除密钥并用随机数替换它就足够了。但是,必须注意适应数据的组成。

简单情况:非复合唯一键

在上面给出的简单例子中,请注意该表与示例 1 中的表 B 相同。在这里,密钥可以很容易地用随机数或身份生成器来替换。

C oding 提示:您可以使用 spark 中的‘单调递增 id’函数或 python 中的‘uuid’包或 R 中的‘ids’包或 SQL 中的‘NewId’函数来创建一个随机 id。

数据完整性案例:重复键

考虑到表的组成,我们可能会发现 id 是重复的,但重复的是我们想要保留的有效数据点。在这种情况下,我们希望每次都用相同的随机数替换相同的密钥。

C oding 提示:为此,I)创建一个具有唯一 id 的子集 ii)添加一个具有随机数的新列,然后 iii)将其加入到初始数据集中,iv)删除旧的键列。这里可以使用“删除”、“连接”和“选择”或“子集”功能。

表 E 和表 D

2.当移除识别键还不够时

如果用随机密钥替换密钥还不够,我们可以采取其他方法,例如:

2A。哈希:

一、散列一些列:

散列是使用固定长度代码来表示数据列的最广为人知的方法之一。哈希将数据转换为固定大小的字母数字或数字代码,这种代码不容易被逆转(如果有逆转的话)。请注意,这不同于加密。

哈希的两个关键特性:

这在技术上是可行的,但实际上是不可能逆转的,即使是最简单的散列,即你可以从 A 到散列(A ),但不能从散列(A)到 A。

散列相同的值每次应该提供相同的输出,并且没有两个输入应该得到相同的散列输出(冲突)。

Coding 提示:在大多数语言中,‘sha’或‘hash’函数可以实现这个功能,例如:spark、hive 中的‘sha()’或 python 中的 hash 或 R(需要 hash 包)或 SQL 中的‘hash bytes’。还记得数据完整性案例吗?是的,我们可以用这种方法来代替 ids!😄

二。创建基于密钥的代码:

这是一个类似于哈希的概念。假设您有一个数据集,其中的键是一些列的组合。出于某种原因,您决定不使用哈希函数,而是希望对数据进行系统化和可重复的处理,并允许您使用相同的键连接到其他数据集。常见的方法是从键列(和/或复合列)中删除或替换特定位置的字符。

使用实施例 1 中的表 A:

表 A

删除密钥的第三个字符:

或者替换密钥的第三个字符:

在表 H 中,所有第三个字符都被替换为“m”。你可以变得更复杂,做一个映射,第一行得到一个 m,然后下一行得到一个 n …等等。

当处理这样的数据集时,我建议也进行部分编码、散列或删除其中一个组合列。

C oding 提示:这里可以使用 python 中的切片或者 spark、hive、R 或者 SQL 中的' substring '(或者' substr ')函数。

2B。随机混合:

在某些情况下,我们可能需要与实际数据集具有相同结构和特征的数据。假设我们需要开发一个应用程序,但是团队还没有开发出一种安全的数据传输方式。一个快速的解决方案是使用现有数据创建假数据,而不是暂停开发直到解决这个问题。

一种方法是随机混合每一列中的数据。

Ex —回想一下示例 1 中的复合表?

我们可以这样混合它:

简单混合

二世。现在你可能会说,如果有人知道键是一些列的组合,他仍然可以从这些信息中猜出正确的数据所有者。有两件事你可以快速完成:

a)创建完全伪造的数据行并添加到数据集,在混合之前删除一些实际的数据行。

现在我们混合:

丢弃的数据混合

b)替换某些列中的某些字符。

你想对有意义的列这样做。替换城市或语言列中的字符(如果有)实际上不起作用,因为这可能被视为输入错误,很容易纠正。此外,在数字列中用字符串替换数字可能会导致错误,因为这会改变列的数据类型或被视为坏数据。这可能会误导应用程序开发或分析产品开发。

我们可以这样修改上面的表 I:

在上表中,请注意在某些情况下会添加一个字符或完全替换一个名称。表 J 是 II a)和 II b)的组合

3.为分析或类似工作准备数据:

有很多时候,我们需要数据进行分析或一些类似的目的,但不能分享敏感信息。

I .与上述情况类似,我们可以混合数据,并在混合前替换或添加行。然而,随机混合或改变数据可能会导致趋势丢失,而这在数据分析中是很重要的。如果您决定混合或添加数据,必须注意保持相同的模式、频率、范围、数据结构等,以免扭曲数据模式或引入异常值。在这种情况下,组内混合可以工作,尽管这稍微复杂一些。

二。散列和混合也是可行的,但是如上所述,如果组被认为对分析项目很重要,则必须注意在组内混合。

三。聚合数据—对于某些数据,我们可以创建聚合,这些聚合可以提供分析用途的信息,但不够精细,无法追溯到数据所有者。例如,代替电话号码,我们可以使用区号或创建区域标志。

四。一种首选的方法是对数据集中的敏感信息进行哈希运算,并根据其意义进行分组。通过对列进行分组和散列,可以在隐藏实际数据的同时维护模式。

对于所有必须匿名的数据集,重要的是删除数据中任何唯一的和/或个人可识别的信息,如电话号码、社会安全号码、银行号码等。唯一数据的一个例子是,关于汽车的数据集中有一辆粉红色的野马,关于房屋的数据集中有一座紫色的大厦,关于畸形的数据集中有一个象人,等等。

现实生活中的例子

一些需要匿名化数据的情况包括但不限于-

1.使用数据创建数据产品。构建机器学习模型、开发应用程序、呈现汇总统计数据等

2.同一公司但不同项目组的人员,其中一个组不允许查看数据

3.创建公共数据集

4.需要数据,但由于数据传输安全或隐私原因而无法共享实际数据的任何情况。

5.GDPR 或隐私法适用的任何情况

感谢您的阅读!😃

匿名数据集第 2 部分

原文:https://towardsdatascience.com/anonymizing-data-sets-part-2-84b813a4ff7e?source=collection_archive---------28-----------------------

从数据中删除电子邮件、敏感号码、伊正和地址

由作者使用 Unsplash 的照片创建(照片由:Tarun Anand、Franck V .、宝琳娜·加西亚、Markus Spiske 提供)

回来的时候,一个同事让我帮忙匿名处理一些数据。他提供的数据集激起了我的兴趣,并引出了我们今天要讨论的一些内容。

当我们谈到匿名数据时,我们指的是删除个人信息或任何可用于识别或跟踪个人的信息。在某些情况下,这被称为个人身份信息(pii)。这些信息可能包括但不限于:姓名、电子邮件、地址、电话号码、地理位置、社会保险号(ssn)、车辆识别号、账号、身份证号、信用卡信息等

本文将向您介绍如何构建 python 查询,从数据集中提取这些项目。就范围而言,本文档采用正则表达式方法来识别和替换数据集或字符串中的电子邮件、敏感数字、vin 和地址。

电子邮件

电子邮件非常普遍,几乎在每个应用程序中都有使用。事实上,当大多数人发出电子邮件时,他们并没有意识到他们发出了大量的信息。虽然电子邮件可能对数据科学家有用,但为了赚钱或其他需要消除数据身份的目的,我们可能需要删除电子邮件数据,同时保留其余数据。使用简单的正则表达式函数可以帮助解决这个问题。

例如:

电子邮件示例 1,由作者创建

Note that in this case the regex is case insensitive. However, a space in the email such as *snubholic @yahoo.com* would change the results.
Code: *re.findall("\S+@\S+", dn)*

要用“xxx”替换字符串 dn 中出现的所有电子邮件地址,我们可以如下操作:

电子邮件示例 2,由作者创建

*Code: dn2 = re.sub(“\S+@\S+”, r’xxx’, dn)*

敏感数字

本节展示了如何从数据帧中移除敏感数字。这里使用的 regex 函数不包括电话号码、ssn、经度和纬度坐标、一些邮政编码等。

演示:~

*Creating the data set:**dat = {‘number’: [“1231451469”, “42.2”, “123 145 1469”, “123.145.1469”, “(123) 145.1469”, “(123) 145 1469”, “(123) 145–1469”, “123–145–1469”, “+1(123) 145–1469 “, “1234567890999111”, “123HELLO56”, “-123”, “04/04/1998”, “it’s015–96–0342 you know my number call me”, “+123–145–1469”, “48236–123”, “I live close to (42.293564, -83.638916)”], ‘name’: [“MJ”, “Hermann”, “Mary”, “Jane”, “Smith”, “John”, “Jeremy”,”Sam”, “CJ”, “JJ”, “Mimi”, “Yule”, “Siri”, “Mima”, “ilian”,”Alfred”, “Grid”]}**df = pd.DataFrame(dat)*

Df 看起来像:

作者创建的数字数据框架

要查找上述所有敏感号码,请使用下面的函数:

def patterner(x):
    mm = re.findall(r'[\+\(]?\d[\d .\-\(\)]{6,}', x)
    return mm

要查找数据帧中的所有敏感数字并替换为 xxx,我们可以创建如下函数:

def patterner2(x):
    mm = re.sub(r'[\+\(]?\d[\d .\-\(\)]{6,}',r'xxx', x)
    return mm

要在数据集中创建一个新的列来替换所有敏感的数字,我们可以这样做:

df['number2']= df['number'].apply(patterner2)

这个查询的输出如下:

替换列中的数字,由作者创建

关于上面的函数需要注意一些事情:它删除了数据帧中的所有电话号码格式——国内和国际,并删除了 latlong 坐标以及第 14 行(Mima 的行)上的 ssn 和第 15 行(Alfred 的行)上的 zipcode。但是,它不会删除小于 6 位的数字,但会删除所有大于 5 位的数字。

车辆识别号:

T 车辆识别号很敏感,因为一旦你买了一辆车,这个车辆识别号就和你的个人联系在一起了。当绑定到适当的数据时,它不仅可以用来跟踪你的移动模式,还可以跟踪你的家庭地址、注册和电话号码等。随着美国等发达国家越来越多的人(和组织)获得车辆以方便移动,并作为 covid 时代的一种预防措施,vin 号码变得越来越重要。我们可以从数据中删除 vin 号,如下所示。

演示:~

stringphrase = "hi, do you know whose is vin 1FA6P8CFGL5386177 and whose is MNAAXXMAWALE27598?"m = re.findall('[A-Za-z0-9]{1}[A-Za-z]{2}[A-Za-z0-9]{9}[\d+]{5}', stringphrase)
x= re.sub('[A-Za-z0-9]{1}[A-Za-z]{2}[A-Za-z0-9]{9}[\d+]{5}', r'xxx', stringphrase)

这给出了输出:

*“hi, do you know whose is vin xxx and whose is xxx?”*

地址:

对于许多企业来说,从数据帧中移除或替换地址是相当棘手的问题。这里,通过将正则表达式与列表理解和一些映射函数结合起来,我们能够从数据集中识别和删除几种不同的地址格式。

演示:~

创建您的数据集

data = {'addresstext': ["I had an ok experience and I live close by 2000 Vernier rd grosse pointe woods MI 48236\. I had a good time at 2999 vernier", "I used to know someone who lived at, 2025 magna rd grosse pointe MI 48237 they loved it and told us many cool stories about the lake","I liked their services 22000 moross rd, detroit MI 48236", "lots of diverse life experiences at 6233 orlina st, apt 1001, detroit MI 48217", "2013 1st ambonstreet", "245e ousterkade 9", "oh yeah, I had a great time at 20225 Liverni a really really good time" ], 'name': ["MJ", "Mary", "Killian", "Liroy","Alex", "Sammy", "Peter"]}
dff = pd.DataFrame(data)

创建的数据集如下所示:

示例地址数据框,由作者创建

要识别地址,我们可以使用以下两个函数:艾迪和艾迪 2。第一个函数 addy 标识了地址,但也添加了额外的文本。第二个函数 addy2 清除 addy 并限制被标识为地址一部分的附加数据。

def addy(x): ###returns addresses with a few extra characters
    mi = re.findall(r'\d+?[A-Za-z\-\,\ \d+]{4,}', x) 
    return mi

第二个功能:

def addy2(w):
    gm = re.sub(r'(\d\w+\b)\s+\b(\w+)\b\s+(\w+)\s+\b\D+$', r'\1 \2 \3', w)
    return gm

将这两个函数应用于数据集,如下所示:

dff['addressadd'] = dff['addresstext'].apply(addy)
dff['addressadd2']= dff['addressadd'].map(lambda x:[addy2(i) for i in x])

这会产生输出:

应用作者创建的 addy 函数

要替换数据集中的地址,使用下面的查询(注意列表理解和映射函数的使用消除了显式创建循环的需要并提高了速度) :

dff['testing2'] = dff.apply(lambda x: reduce(lambda a,r: a.replace(*r), list(product(x['addressadd2'], ['xxx'])), x['addresstext']), axis=1)

输出如下所示:

由作者创建的地址被替换时的输出

注意,这些查询正确地替换了 dataframe 列中的所有地址,包括第一行中的两个地址。有一些小的数据丢失,因为一些额外的字也被替换为地址的一部分。在第 1 行中,“他们爱”和第 6 行中的“a”被捕获,并随后被替换为地址的一部分。然而,在这些大计划中,这些都是次要的,特别是考虑到留下地址的选择,以及潜在的诉讼或侵犯个人隐私。

把所有的东西放在一起

A 在分别查看了每种类型的 pii 后,您可以将开发的功能转换成一个转换,并将其一次性应用于数据集。请记住,如果在具有数字列的数据集中包含要删除敏感数字的部分,则在应用函数或转换之前,应该排除数字列。同样在这里,我们创建了几个新列。当实现上面的查询时,不要忘记删除最终数据集中的初始列,以便它真正不包含个人身份数据。最后,在处理大型数据集时,直接使用 pyspark(或 scala)或将数据帧转换为 pyspark 数据帧并执行转换可能会更容易(您可能需要将大多数函数创建为 UDF)。

**鸣谢 **

这项工作是在 python 的https://regex101.com、excel 和 jupyter 笔记本的帮助下完成的。它还参考了几篇 stackoverflow 文章,包括https://stack overflow . com/questions/34773317/python-pandas-removal-substring-using-other-column

Python 中的匿名函数

原文:https://towardsdatascience.com/anonymous-functions-in-python-914a0a4b86ea?source=collection_archive---------34-----------------------

Lambda 表达式简介

来源

定义函数是软件编程的一个重要部分。定义简单函数的一种方法是匿名使用 lambda 表达式。例如,计算单个表达式的函数,如加法,可以用 lambda 表达式替换。在这篇文章中,我们将讨论如何使用 lambda 表达式定义匿名/内联函数。

我们开始吧!

假设我们有一个单行函数,它返回两个输入整数值之间的差:

def subtract(x, y):
    return x - y

如果我们用 x= 10 和 y =7 调用这个函数,并打印我们得到的返回值:

print("Difference:",subtract(10, 7))

由于这是一个简单的函数,它只对单个表达式“x-y”求值,因此可以用 lambda 表达式替换它:

subtract_value = lambda x, y: x - y 

lambda 表达式包含关键字“lambda”、输入 x 和 y、一个冒号以及我们想要计算的表达式。如果我们回头看看我们最初的函数定义:

def subtract(x, y):
    return x - y

我们看到这些表达看起来非常相似。最大的区别是在我们的 lambda 表达式中没有“def”关键字、“return”关键字和函数名。

让我们打印 x = 10 和 y = 7 作为输入的函数:

print("Difference:",subtract_value(10,7))

您可以将 lambda 表达式应用于更有趣的操作,如排序和数据简化。让我们考虑下面的编程语言创造者列表:

names = [**'**Guido van Rossum**',** 'Bjarne Stroustrup', 'James Gosling', 'Rasmus Lerdorf']

我们可以使用' sorted()'方法和 lambda 表达式,按照姓氏的字母顺序返回列表:

print(sorted(names, key = lambda name : name.split()[-1].lower()))

值得注意的是 lambda 表达式的局限性。因为我们只能计算单个表达式,所以像迭代、异常处理和条件这样的特性是无法指定的。尽管如此,lambda 表达式在代替评估单个表达式的单行函数时非常有用。

我们可以考虑的另一件事是如何在匿名函数中捕获变量。让我们定义一个整数 x = 5,并定义一个匿名函数,将 x 和另一个输入整数 y 相加:

x = 5
add = lambda y: x + y

让我们调用 y = 24 的函数:

print("Addition:", add(24))

如果我们改变下一行中 x 的值并调用我们的函数,我们得到:

x = 5
add = lambda y: x + y
print("Addition:", add(24))
x= 10
print("Addition:", add(24))

因为 lambda 表达式中使用的 x 是运行时绑定的自由变量,所以 x 的值是运行时的值。要让匿名函数在定义时捕获一个值,在本例中捕获 x = 5,我们必须包含 x 作为默认值:

x = 5
add = lambda y,x=x: x + y
print("Addition:", add(24))
x= 10
print("Addition:", add(24))

我们得到了我们想要的行为。我就讲到这里,但是我鼓励你自己去研究代码。

结论

总之,在这篇文章中,我们讨论了如何使用 lambda 表达式在 python 中定义匿名函数。如果您有一个包含许多计算单行表达式的函数的 python 脚本,那么用 lambda 表达式替换这些函数通常会更容易。此外,我们讨论了如何在 lambda 表达式中设置默认值,这允许我们在定义时捕获变量的值。如果你希望学习更多关于匿名函数的知识,我推荐你阅读 Python 食谱。我希望你觉得这篇文章有趣/有用。这篇文章中的代码可以在 GitHub 上找到。感谢您的阅读!

面向 2022 年的深度学习硬件指南

原文:https://towardsdatascience.com/another-deep-learning-hardware-guide-73a4c35d3e86?source=collection_archive---------0-----------------------

理解大数据

什么是 GPU?为什么重要?我需要多少内存?你想更好地理解这些术语,甚至把它们用上吗?请继续阅读。

图片: Pixabay

这是截至 2022 年 3 月 1 日的最新数据

介绍

这是你们都知道和喜爱的旧版本的一个巨大的修改版本。本指南的几乎每一部分都被彻底重写。最初的指南已经更新了 6 年,所以我决定是时候基本上(几乎)从头开始写了。这一次,我试着让它变得更全面、更普遍。我会继续更新这个,但我也想确保我的读者能理解这个话题,即使有一天我不再这样做了。

所以,你决定要买一台专门训练机器学习模型的机器。或者,更确切地说,你在一个本指南的术语不断被抛出的组织中工作,你只是想知道更多关于它们的含义。这不是一个非常简单的话题,所以我决定写这篇指南。您可以从不同的角度讨论这些术语,本指南将解决其中一个问题。

本文中各种表格的注释;我制作了它们,获得所有需要的信息花了一些时间。没有我的同意,请不要使用它们。

我是谁?

我是 Nir Ben-Zvi,是一名深度学习研究人员,也是一名硬件爱好者,从初中时代开始,我会在朋友们打篮球的时候拆电脑(我也试过,很快又回到了硬件上)。

在过去的几年里,我为一些组织提供关于构建深度学习机器的建议,并最终决定将这些知识纳入指南。

今天,我是一名计算机视觉顾问,与各种公司和初创公司合作开发基于图像的产品。本指南的许多知识来自为我的各种客户构建深度学习机器的决策。

我如何维护本指南

我最初是在大约 5-6 年前用希伯来语写的这个指南(但是谁在数呢),从那时起我就确保它是最新的。这次我决定重写大部分内容。请注意,有些部分几乎没有改变一点,原因是我觉得他们仍然是相关的。

令人惊讶的是,在过去的 4-5 年里,硬件几乎没有什么变化。例如,在 2018 年 11 月至 2020 年 4 月期间,NVIDIA 根本没有更新其消费显卡(GeForce)系列。另一方面,英特尔已经更新了两次桌面产品线。另一件最终相当反气候的事情是 amd 新的消费级和服务器级处理器系列(我已经看到这是一个非常微妙的话题,所以以后会有更多的讨论)。**

那么,为什么这份指南仍然有意义,是什么让它在一年后仍然有意义呢?嗯,首先,我会试着不时更新它,事实上,当一些特殊的事情影响到市场时,我会这样做。此外,我还删除了一些我认为过于针对特定代的部分。例如,英特尔刚刚宣布了其第 12 代硅胶,但我不太确定这是否会使构建 DL 机器与构建基于当前第 11 代的机器有太大不同,所以我试图使 CPU 讨论更加通用。如果几代人之间发生了剧烈的变化,我自然会做出必要的改变。

GPU-笔记本电脑超出了本文的范围。图片:维基媒体

关于 GPU 笔记本电脑的几句话

****本指南不是用来选择笔记本电脑的。在我看来,深度学习笔记本电脑已经不存在了,至少对于计算机视觉任务来说是这样。现代的 DL 型号太大了,不适合这种笔记本电脑,这种笔记本电脑通常配有针对游戏玩家的显卡(或者偶尔用于渲染任务或给 Photoshop 增添一些活力)。即使是最强大的游戏笔记本电脑,那些通常被称为“台式机替代品”(或 DTR)的笔记本电脑,也可能不足以在合理的时间范围内实际训练(甚至微调)基于 ResNet-101 的模型。

在谷歌提供基于 T4 和 P100 的 Colab 环境的日子里,我看不出有什么理由为 DL 购买一台强大的笔记本电脑。

仍然希望你的笔记本电脑坚固;至少 16GB 内存和 4 个以上内核。但是它主要是一台运行终端到远程实例的机器。那些 16GBs 是给 Chrome 用的。顺便说一下,我用的是苹果电脑。

如果你还想要一台同样便携的 GPU 笔记本电脑,我会买刀片。讨论结束。你可以寻找其他面向游戏的钻机,但这将是我最喜欢的选择。

便携显卡呢?

我承认我对这个领域不太熟悉,也没见过那些被用于非游戏目的的。它仍然是一个单一的显卡,从长远来看可能是不够的。

那么,这本指南里有什么呢?

首先,我将选项分成四个“类别”:

  1. 一台带有单个 GPU 的台式机
  2. 一台与#1 相同的机器,但是具有 2 个 GPU 或者支持将来增加一个 GPU
  3. 带有 4 个 GPU 的“重型”DL 台式机
  4. 一台带有 8 个 GPU 的机架安装型机器(请参阅更多注释;您可能不会自己构建这一个)

机架安装通常安装在服务器机房中。图片: Pixabay

在 8 GPU 机器和机架安装上

拥有 8 个以上 GPU 的机器可能最好从一些 OEM (Lambda Labs、Supermicro、HP、Gigabyte 等)购买预组装的。)因为建造它们很快就会变得昂贵和复杂,它们的维护也是如此。请注意,它们可能还需要在您的办公室中建立一个适度的服务器机房,并配备适当的冷却设备(以及用于所述冷却的故障安全设备)。这些东西非常非常吵,不应该放在人类附近。

理论上你可以自己建造一个——我见过这样做的——但在我看来,这里的金钱收益太微不足道了。我要指出的是,我们位于以色列,很难获得此类机器的部件(特别是 PSU 和机箱),需要从美国运输。由于这是英文指南,我会注意到,也许在你居住的地方建造这样一台机器会更容易、更便宜(如果你经常访问新蛋的话)。

建立一个额外的原因是原始设备制造商通常会给你一个非常需要的现场支持包。

因为最终这些很少是自建的,所以我不打算进一步讨论它们。

我要哪个显卡?

TL;博士:你想要一个 3080/3080ti/3090 。它们属于 NVIDIA 的系列(或一代)30 的卡。

不过,没那么快;由于持续的芯片短缺,实际上很难拿到卡。出于本指南的考虑,我假设你,但实际上你可能不得不用可用的而不是最好的来建造你的装备。

图片:由英伟达提供

NVIDIA 系列 3 讨论

NVIDIA 基于安培的系列 30 现在已经上市大约一年了,3060ti 和 3080ti 随后出现。让我们首先浏览这些部分,比较我认为重要的事情:

消费级卡与 A100 级数据中心的对比。图片:我。

好吧,我迷路了!

那么我们这里有什么?最低端的部分,3070 应该以旧型号市场价的一半带来 2080ti 同等的性能。对于某些训练任务来说,较低的内存容量可能是一个问题。对于一个相当小的价格升级,3080 将提供明显更高的性能——NVIDIA 声称某些任务的两倍。

3080

在 NVIDIA 最初发布后,当与 3090 抗衡时,3080 似乎是一个物有所值的选择(在 MSRP)。只需 700 美元,您就可以将 2080ti 的性能提高近一倍。它确实提供了更少的内存,但更高的速度应该仍然使它伟大。我的旧观点没有改变。

3080ti

当最初宣布系列-30 时,大多数 DL 爱好者不得不在 3080 和 3090 之间进行选择。两者都有很多优点,并且在价格上有很大的差异,这使得它们非常适合不同的用户。此后,NVIDIA 宣布了介于两者之间且备受期待的 3080ti 。“ti”部分是否像前几代产品一样做出了显而易见的选择?我很快就会谈到这一点。

与 3090 相比,3080ti 基本上以略低的价格给几乎相同的性能,而内存只有一半。官方的 3090 规格要求您的主板上有三个插槽,但这一点自发布以来有所改变,使得这一限制不太相关。

3090

就性价比而言,3090 应该是这一系列中最有趣的一款,但 350W 的功率有点挑战功率。当我最初写它的时候,我提到过双槽解决方案将会被强制采用。从那时起,技嘉,华硕和 EVGA 已经这样做了。我仍然反对购买(可能更便宜的)三插槽解决方案。350 瓦的功率仍然使使用这一部分很困难,但硬件支持在过去一年中有所改善。

记忆

时代错误是可怕的;一年前我写道:

“24GB 内存很有意思,但如果在同一个机箱中安装 5 个 3090 而不是 8 个 3080,我认为系统制造商会选择后者,更便宜的卡。”

嗯,对于 3090 的两个插槽鼓风机解决方案,如果钱不是问题,您现在可以将 8 个 3090 放在同一个机箱中,在单台机器中获得令人难以置信的 192 GB GPU 内存。

另一件令人兴奋的事情是为更昂贵的变体选择的极快的 GDDR6X(从 GDDR6 开始)。这些是 3080、3080ti 和 3090。

我无法从新闻稿中获得内存带宽的数字,但这些卡将有惊人的快速内存接口,这可能会超过它们与 20 系列卡相比更低的内存大小。这听起来可能违反直觉,但如果卡可以从其内存中获取数据并足够快地处理它,那么对于类似的吞吐量(如果我们是为了训练或推断而测量每秒图像数),它需要的内存就可以更少。

那么我应该买哪一个呢?

先做最重要的事情;如果你想为了学习如何深度学习而买便宜的东西,我会买 3070。但这不是本指南的内容。

对于一家初创公司(或更大的公司)来说,为其渴望权力的研究人员构建严肃的深度学习机器,我会尽可能多地塞满 3090。双内存数字字面上的意思是你可以用一半的时间训练模型,这简直是物有所值。这也适用于构建单 GPU 或双 GPU 机器的人。如果你以此为生,3090 是一个惊人的价值。“只”少 300 美元,我看不出 3080 蒂适合那里。双内存是一件的事情。

然而,如果 3080ti 已经超出了您的预算,那么它也是物有所值。

最后,最初的 3080 的建议零售价为 699 美元,对于那些最大限度地利用预算购买单个 GPU 平台的人来说,仍然是一个非常高的性价比——明白了吧。会很棒的。

从另一个角度来看。如果您选择 3080ti 还是 3080,并且预算不多,那么 3080 绝对物超所值,非常适合您。

MSRP 制定计划,上帝笑了

我所有的建议都是基于 MSRPss 的,MSRP 有时会根据供求关系变化很多,尤其是在全球芯片短缺持续的时候。例如,如果 3090 的价格接近 2000 美元,那么 3080ti 马上就变得物超所值了。

我应该从 20 系列升级吗?

不。不像从帕斯卡到图灵(1080ti 到 2080ti)的跳跃——这一代,至少在目前,提供了一个适度的减速带。三年前从 1080tis 移动到 2080tis 获得了非常好的性能提升,这是由于使用了混合精度训练或 FP16 推理——这要归功于他们新颖的 TensorCores。这一次,我们获得了通常的大约 30%的性能提升(当然,这取决于任务),但除此之外别无其他。

在这一点上,我看不出有什么理由去买一个基于 2080ti 的系统,除非你能买到非常便宜的东西。

我们来谈谈显卡

卡代和系列

NVIDIA 通常区分消费级卡(称为 GeForce)和针对专业用户的专业卡。

向夸德罗和特斯拉说再见

以前 NVIDIA 对 pro 级卡还有一个区分;Quadro 负责计算机图形任务,Tesla 负责深度学习。随着第 30 代的出现,这种情况发生了变化,NVIDIA 简单地使用前缀“A”来表示我们正在处理专业级卡(如 A100)。曾经的 Quadro 现在被简单地称为“Nvidia 工作站 GPU”,Teslas 是“Nvidia 数据中心 GPU”。面向 GCI 的 GPU 使用 Axxxx 名称,如 A6000,而深度学习的 GPU 使用 Axx 和 Axxx。

那么我们为什么不仅仅讨论那些“专业级”的 GPU 呢?嗯,它们非常昂贵,而且对于开发(和学习)来说也不太合理。数据中心目标卡额定运行 24/7 多年,具有全球内部支持,有时还提供被动冷却解决方案——所有这些都是您肯定希望在您的最终产品中实现的,也是您没有看到 GCP 或 AWS 使用 GeForce 硬件的原因。嗯,这一点以及英伟达的 EULA 明确禁止这一事实。

目前这两个系列的性能领导者是 GeForce 3090 和 A100。还有 A40 和最近公布的 A10A16A30 。虽然 A16 和 A40 不是为 DL 机器设计的,但 A10 和 A30 很有趣,我将在下面讨论它们。

面向推理的卡片

一个直到最近才出现的美丽新世界。目前(不出所料)由英伟达(NVIDIA)用他们的 T4(图灵的“T”,意思是老一代)卡主导,有残缺的训练能力,但完全合理的推理速度。它仍然比 GeForce 2080ti/3080/3080ti/3090 贵,但如果你需要专业级的快速推理(被动冷却、强大的国际支持、鲁棒性等),它是唯一的游戏。如果这不是你需要的东西,你现在可能已经知道了。对于 30 系列,NVIDIA 用 A30 取代了这一部分,但这不是一个确切的替代,因为它的价格更高,相对来说更强大。请参见下表,并将它们与其相关的顶级数据中心产品进行比较。

英伟达的推断——针对的部分。图片:我。

图形/渲染卡

如上所述,“NVIDIA 工作站 GPU”(之前为 Quadro)是 Nvidia 的渲染目标级别的卡。理论上,它们不应该属于这里,但它们有时可以在更昂贵的数据中心卡(以前是特斯拉)和消费卡之间提供一个最佳性价比点。与消费级 GeForce 卡相比,它们对高强度负载的适应能力更强,但成本仍低于数据中心部件。我对它们不太熟悉,但我要指出,出于培训目的,我看不出有什么吸引力。对于推理任务,它们可以提供一个可行的替代方案,但同样,如果你去那里,你需要完全理解你在做什么,因为它们被“调整”以在渲染图形时达到最佳性能,而不是运行深度学习模型。

对于这一代(安培),NVIDIA 已经宣布了几个部分——最初是 A40 和 A6000,接下来是 A4000 和 A5000。看起来 A40 和 A6000 在规格上几乎是一样的,最大的区别是 A40 将是被动冷却的。

除此之外,没有发布太多信息,但它们的目标是需要强大 GPU 处理渲染任务的数据中心,而不是 DL。如果它们最终比同等的 A100 更便宜,将来可能会有更多的理由讨论它们。下表将它们与上一代类似卡以及面向 DL 的同类产品 V100 和 A100 进行了比较。

NVIDIA 的面向渲染的显卡。图片:我。

FP16 和张量核心

从之前的(20)系列卡开始,NVIDIA 的所有部件都有 TensorCores 以及 CUDA 核心。这些内核是用来运行单精度推理的。当最初引入时,这只是爆炸,因为性能似乎没有受到影响,而推理速度有时会翻倍(训练也变得更快)。

从第 30 代开始,这在某种程度上是一种规范,NVIDIA 在其营销材料中甚至没有提到不同张量核心的数量。

接下来的几代(图灵,有点像“第 20 代半”)也增加了对 8 位和 4 位推理的支持,但这些都需要仔细校准。

关于 INT8 的更多信息

INT8 是一个复杂的问题,您的组织内部应该非常了解它。与迁移到几乎没有麻烦的半精度/FP16 不同,迁移到 INT8 可能会严重影响性能,应该将其视为一项新的研究任务,而不是一个快速的加速工程解决方案。

NVLINK 是 NVIDIA 的一种硬件协议,允许连接他们的卡(两张或更多)并形成一个统一的内存池,这使他们可以在不“通过”CPU 内存的情况下进行通信。这实际上加速了许多计算,因此非常酷,但不幸的是,对于连接两个以上卡的 GeForce (ala 消费级)卡,它也被禁用。如果你只有两张卡,你仍然可以使用它(在消费级版本中称为 still 但如果超过两张,我真的不会介意。你的基于 2080ti/30XX 的 8 GPU 机器不支持 NVLINK,这有多糟糕?不太糟糕。对于需要 GPU 之间快速连接的应用(考虑同步批处理规范化),这可能会提供不错的速度提升,但对于一般用途,您可以不使用它。

云实例是构建实例的替代方法,但是要知道成本。图片: Pixabay

那么云实例呢?

谷歌和亚马逊都在其云中提供 A100 和 V100 部件。微软还在一些地方添加了 V100 实例,但实际上在让人们在 8-GPU 设置中使用它们方面有极大的限制(我不再使用 Azure,如果有变化,请告诉我)。谷歌还提供 P100 basd 实例,这些实例非常物有所值。谷歌也提供他们自己的 TPU,速度非常快,如果你使用 TensorFlow,这是一个很好的解决方案。

尽管如此,快速数学将表明,如果你打算在大量 GPU 上训练模型,并且经常这样做(这意味着这样的机器几乎在所有时间都将被充分利用)——从长远来看,购买顶级训练机器将更具成本效益。相反,如果您可能偶尔而不是一直训练模型,那么云实例肯定是更明智的选择。

或许最好的办法是尝试使用云实例几周,然后再做决定。另请注意,即使拥有大量内部实例的组织偶尔也会在高峰期使用基于云的实例。还需要注意的是,本地实例意味着您需要一个非常精通维护的人。损失 2-3 名数据科学家一天的工作可能比购买深度学习机器节省的钱要多得多。**

CPU 和 PCI-E 通道上的一点

我需要多少个 CPU 内核?什么是 PCI-E 通道?

既然我们已经完成了图形卡的主题,我们可以转移到下一个部分——制造中的训练机器——中央处理器,或 CPU。

GPU 通常需要 16 个 PCI-Express 通道。PCI-Express 是 CPU 和 GPU 之间的主要连接。幸运的是,英特尔的大多数现成器件都支持这一点。
它连接两张通常会出现问题的卡,因为这需要 32 条通道——这是大多数廉价消费卡所缺乏的。转向 4 卡,我们将完全依赖昂贵的至强系列(针对服务器)卡。

我真的需要那么多车道吗?

好吧,简单回答?不完全是。尽管我刚才说过,我们实际上通常可以用每个 GPU 个通道来进行训练和推理。据说,速度极其受限的任务(如低延迟交易)肯定需要所有 16 条受支持的通道。另请注意,如果您的卡支持的话,前面提到的 NVLINK 也消除了一些这种要求。

作为一般的经验法则,每个 GPU 不要低于 8 个通道。另一个需要注意的重要事项是需要 PCI-E 通道的其他部分,例如基于超高速 NMVe 的 SSD 驱动器。

图片:维基媒体

现在让我们选择一个 CPU

英特尔酷睿系列 CPU

在本指南的第一次迭代中,英特尔以极具竞争力的价格提供了一些具有 28、40 和 44 个 PCI-E 通道的部件(这是第 8 代)。从那时起,更多代进入市场(12,阿尔德湖,刚刚宣布),这些零件已被更昂贵的面向发烧友的“X 系列”零件所取代。反过来,这些部件现在是深度学习硬件的卫冕冠军,因为它们的速度和 PCI-E 通道丰富。

如果您计划构建一台具有单个 GPU 的机器,第 11 代的大多数 i7/i9 部件都有 20 个通道,将非常适合您。第 10 代仍然物有所值,它们都有 16 条车道。

如果你需要更多的 cowbell(或通道),X 指定的部分有 48 个通道——足以容纳 4 个 GPU,每个 8 个通道,甚至还剩下一些能量用于一对基于 NVMe 的 SSD 驱动器。不过,到目前为止,它们只存在于第 10 代(冰湖)。最便宜的部分, 10900X ,拥有 48 个通道和 10 个内核,随后是 12、16 和 18 个内核部分。英特尔似乎无意为其当前的第 11 代(Rocket Lake)CPU 引入 X 系列。
英特尔 Ice Lake CPUs 也为在 CPU 上运行深度模型提供了广泛的硬件支持。如果你对这类东西感兴趣,我建议你多读一些,了解它是否有益。

在继续之前,请确保您完全了解 CPU 插座是如何工作的。我假设读者在这个阶段知道这意味着什么。x 系列部件使用更复杂的插座排列,这反过来需要更昂贵的主板。因为我们一开始就在讨论非常昂贵的机器,所以这在实践中不会有很大的不同。

至强系列 CPU

前面简单提到的至强CPU 是英特尔用于服务器和数据中心的系列 CPU(与用于台式机的内核不同)。由于讨论之外的各种原因,它们更贵。除此之外,它们还支持多 CPU 设置,这反过来可以为您可能想要和需要的每个 GPU 提供完整的 16 个 PCI-E 通道(因为您可以组合几个 CPU 及其各自的通道)。同样重要的是,这些 CPU 的弹性要高得多,类似于 NVIDIA 的数据中心系列 GPU 和消费级 GeForce GPUs,应该能够长期承受更高的计算负载。

同样值得注意的是,至强处理器会因为几个不同的原因增加系统的整体价格:

  • Xeons 需要 ECC(纠错码)内存,比较贵。
  • 支持 Xeons 的内存、主板和机箱往往更贵(但更有弹性)。
  • 与同等的酷睿 i7/i9 部件相比,至强部件本身要贵得多

尽管如此,如果您关心在 24/7 极端负载下运行的多 CPU 系统,您可能需要基于至强的机器。基于至强处理器的机器在过去实际上更受欢迎,但从那时起,英特尔添加了前面提到的 i9-X CPU,具有大量 PCI-E 通道,可满足大多数消费者/发烧友的需求。在继续之前,我必须承认,我在构建基于至强处理器的机器方面知识不足,无法推荐一款物有所值的 CPU。最重要的是,至强处理器有许多不同的选择比消费级 CPU 多得多。大幅提高其价格的一个因素是支持的 CPU 连接数(一些支持双 CPU 系统,而另一些支持 4 和 8 CPU 配置,成本明显更高)。它们还具有更大的缓存大小,这实际上对各种任务有很大帮助,但这也超出了本文的范围。

如果您正在构建一台 8 GPU 的机器,那么您将 100%依赖至强处理器来实现 8×8 PCI-E 通道(=总共 64 个)。除非你去 AMD,那是。

那么,AMD 在哪里?

这是自 2018 年 11 月以来没有更新过的,将保持原样。请不要杀我,AMD 粉丝们!

更新:现在确实好像有人在用 AMD CPUs 造 DL 机;如果需求上升,我可能真的要重写这个。

好吧,AMD 提供的东西——至少在纸面上——本应该彻底改变整个行业,但实际上并没有。那就是它的 Epyc 命名的面向服务器的 CPU 系列,以及附带的名为 Threadripper 的消费级部件。这两者分别提供了 128 和 64 个 PCI-E 通道——有可能将英特尔打得落花流水。这些处理器还拥有大量内核(多达 32 个),比同等价格的英特尔 CPU 多得多。那么,为什么这些产品没有风靡市场呢?

  • 较低的单 CPU 性能(对于许多不喜欢并行化的任务来说很重要)
  • 人们通常比较保守,不愿意采用不太受欢迎的公司的新硬件(在服务器领域;消费者领域,尤其是游戏玩家,对 AMD 非常熟悉,多年来一直如此)
  • 大量科学计算库大量使用 MKL 加速;只有英特尔在其 CPU 中支持的东西;一个显著的例子是 OpenCV
  • PCI-Express 通道丰富并不像听起来那么简单,我将解释:

因此,不像英特尔在 CPU 和主板之间有自己的专有连接,AMD 的 CPU 实际上使用 PCI-Express。这意味着一个 64 通道的 CPU 实际上只有 56 个可用通道。这反过来意味着,对于 4 张卡,你又剩下一个 16x/16x/8x/8x 配置。这仍然很棒,超过了英特尔所能提供的,但与英特尔的 i9 CPUs 相比,这不算什么问题。此外,当建造一台价值数千美元的机器时,为一个更便宜的 CPU 节省 400-500 美元也许并不重要。

因此,事实证明,AMD 仍然主要与英特尔在游戏机方面竞争,或者与那些大量节省几百美元的人竞争。此外,对于构建双 GPU 机器,我可能会选择他们,因为与整体系统价格相比,2x16 PCI-E 和 CPU 的节省更大。

关于内存(RAM)的说明

即使某个 CPU 适合(物理上,插槽方面)特定的主板,而主板又支持大量内存(同样,物理上,通过大量插槽),CPU 本身仍然有可能不适合。CPU 之间的区别因素之一(就价格而言)是它们支持大量内存的能力。64+ GB 内存通常只有昂贵的 CPU 支持。

关于硬件的更多信息

由于内存和主板会随着硬件的更新而不断更新,因此要保持与时俱进有点困难。它们也(在我看来)不那么令人兴奋和有趣。

我给你的建议?在选择 CPU/GPU 之后,了解您的其他需求,并选择支持这些需求的好主板。仔细阅读小字,检查它是否支持您需要它支持的内容。一些常见的错误是:

  • 购买显卡插槽数量不足的主板。
  • 显卡插槽之间没有足够的空间(由于巨大的冷却解决方案,我们正在处理的 GPU 需要两个插槽)。
  • 购买不支持我们想要购买的内存类型的主板。

底板

支持最新和最好的主板往往是相似的,价格也差不多。去一家领先的公司;微星,华硕,技嘉等等。正如多次提到的,确保它支持 CPU 插槽、内存数量和类型,以及所需的 PCI-E 插槽数量、间距和通道数量。关于通道的数量,请注意,一些主板具有 GPU 兼容插槽,实际上不允许完整的 8/16 PCI-E 通道通过(它们下面是 x4 通道)。避开那些。至于具体的品牌——请看下一个芯片组。请注意,大多数主板,除了最昂贵的版本,通常只支持 3 个 GPU。

在芯片组上

TLDR:你想购买的相关芯片组是用于最新英特尔 X 型号的 X299 系列和(巧合的是)用于 AMD CPUs 的 X399 系列。

像前面的 CPU 及其主板一样,芯片组是主板的重要组成部分,实际上是它支持各种 CPU 功能。领先品牌的旗舰主板通常会包含最新、最好的芯片组,从而支持您所需的一切。

CPU 制造商使用不同的“插座”将其 CPU 连接到主板。这些通常用“LGA2066”这样的术语来表示,这意味着处理器和插座之间的物理连接(引脚)数量。LGA 的意思是“陆地网格阵列”,意味着引脚实际上是主板的一部分,而不是 CPU 的一部分(过去是反过来的)。

英特尔第 10 代和第 11 代使用插座 LGA1200,取代了第 9 代之前使用的旧插座 LGA1151。对于 X-parts(也称为高端台式机的 HEDT ),由于其高功率要求,它使用了不同的插座配置——LGA 2066,它仍然存在,但应该会在不久的将来被取代。选择主板时要知道这些数字。

记忆

一般的经验法则是 CPU 内存是 GPU 内存的两倍。因此,对于四个 2080,我将得到 2x11x4 = 88GB 的内存。由于内存倾向于以 16GB 为增量,我们可能会有一个 96GB 的内存机器用于这样的配置。在我的头顶上统治着记忆制造者;海盗船(已经在上面好几年了!),HyperX,Patriot,G.Skill,还有 A-Data 在预算端的事情。

电源装置— PSU

你的电源应该能够承载你正在建造的机器的疯狂的电力需求。拥有两个以上 GPU 的机器通常会有两个独立的电源。因此,如果您计划在将来添加更多的卡,请确保您的盘柜支持第二个 PSU,以供以后使用。两台 GPU 机器可以满足于单个 1000W PSU。PSU 的优秀制造商有 Antec、CoolerMaster、Corsair、SeaSonic、EVGA 等。

冷却/外壳

假设这整个机器将在它自己的单独的、良好冷却的房间里,我们真的不需要专门的冷却解决方案。如果不行,你会想要一个足够安静的工作环境。

至于机箱,您会想要一个便于安装所有东西的机箱,并且偶尔会打开进行修复/硬件更换。还要注意,与附件捆绑在一起的 PSU 通常是垃圾——除非它们来自真正的公司。

硬盘驱动器

这归结为预算。便宜的选择是为当前使用的数据集提供良好、快速的 1TB SSD,为常规、较慢和较便宜的存储(模型检查点等)提供 4tb 以上的 SSD。

如果预算不成问题,当然最好只购买快速固态硬盘。由于价格往往会一直下降,如果你想挥霍,你肯定可以购买更多的固态硬盘。同样值得一提的是备份;如果您绝对不能承受数据丢失,那么考虑一个 RAID 解决方案(超出了本文的范围)。

结束语—游戏机

上面描述的一批硬件与流行的高端游戏机有很多共同点。除了非常夸张的 GPU 设置之外,大多数组件(主板、RAM、PSU 等)在高端游戏设置中都会感觉很舒服。如果你在谷歌搜索这里的所有信息时遇到了一些问题,将“深度学习”改为“游戏”,你就能找到答案了

祝你好运!

图片: YouTube/Nvidia

提升 Jupyter 笔记本体验的另一种方式

原文:https://towardsdatascience.com/another-way-to-elevate-your-jupyter-notebook-experience-1965991d4269?source=collection_archive---------38-----------------------

增强 jupyter 笔记本电脑的额外有用功能没有被充分提及

我已经写了一些将在我们的 Jupyter 笔记本上实现的最有用的特性这里,但是我想把它扩展成一些很少在 Jupyter 笔记本上实现的特性(或者至少在我的知识范围内)。这就是了。

丘比特主题

如果你不喜欢香草 jupyter 主题,或者只是厌倦了它,你实际上可以改变主题。虽然,我很少改变我的 Jupyter 笔记本主题,因为我更喜欢默认形式。作为初学者,需要先安装必备模块。

**# install jupyterthemes**
pip install jupyterthemes

如果你想访问所有的 jupyterthemes 文档,你可以在这里阅读。

要使用 jupyterthemes 修改我们的 jupyter 笔记本环境,我们可以使用 jupyter 笔记本或使用 CLI。让我们尝试查看所有可用的主题。在 CLI 中,我们使用 jt 命令访问 jupyterthemes。通过使用!在 jupyter 笔记本上,我们可以像在 CLI 中一样输入

**#accessing all the jupyter themes**!jt -l

jupyterthemes 中所有可用的主题

如果我们想改变我们的主题,我们可以使用下面的命令。

**#For example, I want to change it to chesterish theme**
!jt -t chesterish

切斯特主题

**#Or change it to gruvboxl theme**
!jt -t gruvboxl

Gruvboxl 主题

如果您想恢复到默认主题,您只需输入这一行。

**#Reset the change**
!jt -r

就这样,我们回到了最初的主题。我个人更喜欢默认的主题,因为颜色对我的眼睛很好,如果你意识到当我们改变主题;所有常用的快捷图标都不见了。我是那种喜欢使用图标而不是键盘快捷键的人,这就是为什么我更喜欢默认的那个。

自动导入

如果你懒得在 Jupyter 笔记本上输入每一个你会用到的重要库(比如 Pandas,Numpy,Matplotlib 等等。),我们实际上可以建立一个环境,在这个环境中,我们已经将所有这些库导入到我们的 Jupyter 笔记本中。从技术上讲,我将在这一部分解释的内容并不是任何可用扩展的一部分,但它仍然是提升我们 Jupyter 笔记本体验的一种方式。

这些步骤非常简单,它们是:

  1. 找到~/.ipython/profile_default文件夹,它通常在你的用户文件夹中
  2. 在文件夹中,如果没有名为“启动”的文件夹,请创建一个
  3. 在启动文件夹中,创建一个新的 python 文件(你可以给它起任何名字,但是我更喜欢把它命名为start.py
  4. 在这个文件中写下你想要导入的库,每次你启动 Jupyter notebook 时,它都会自动导入

例如,在 start.py 中,我将这样编写我的自动导入库

当我启动我的笔记本时,我不需要导入上面的这些库,直接完成我的工作。

我的 jupyter 笔记本自动导入示例

如果您想确认库是否已经被加载,您可以通过使用 Jupyter 笔记本中的globals()来检查环境。

Qgrid

Qgrid 是一个 Jupyter 笔记本部件,它改进了我们的熊猫数据框布局。这个小部件允许我们滚动、排序和过滤控件,以及通过双击单元格来编辑数据框。要启用 qgrid,我们需要先安装它。

pip install qgridjupyter nbextension enable --py --sys-prefix qgrid**# only for the first time if you have not enabled the ipywidgets nbextension**
jupyter nbextension enable --py --sys-prefix widgetsnbextension

下面是我们如何使用 qgrid 的一个例子。

仅仅通过使用 qgrid 中的函数show_grid,我们已经有了一个不同于默认的 Dataframe 体验。在这里,我们可以更加灵活地对数据进行排序和过滤。

崛起

RISE 是一个插件,可以将我们的 Jupyter Cell 笔记本创建和编辑成交互式幻灯片。如果你意识到我们可以在我们的 Jupyter 笔记本中创建一个幻灯片,它位于视图->单元格工具栏->幻灯片。这种方法的缺点是,我们需要在单独的屏幕上运行额外的命令来创建幻灯片,并且单元格不可调整。

这就是 RISE 的用武之地,通过使用这个插件,我们可以在我们的 Jupyter 笔记本上创建一个交互式幻灯片。首先,我们需要安装并启用所有重要的东西。

**#Installing using conda**
conda install -c damianavila82 rise**#or using pip**
pip install rise**#enable the nbextension:**
jupyter-nbextension install rise --py --sys-prefixjupyter-nbextension enable rise --py --sys-prefix

现在我们准备使用 RISE 来创建我们的幻灯片。这是它的工作原理。

如何创建幻灯片

下面是我们创建完幻灯片并使用上升后的样子。使用 RISE 的最大好处是,我们可以在幻灯片放映期间编辑代码。

结论

在这里,我向您展示了一些在 Jupyter Notebook 中很少使用但实际上在我们的日常数据科学工作中非常有用的功能。希望有帮助!

如果你喜欢我的内容,并想获得更多关于数据或作为数据科学家的日常生活的深入知识,请考虑在这里订阅我的时事通讯。

如果您没有订阅为中等会员,请考虑通过我的推荐订阅。

ANOVA(方差分析)-解释

原文:https://towardsdatascience.com/anova-analysis-of-variance-explained-b48fee6380af?source=collection_archive---------12-----------------------

详细的解释和一个 R 的例子

亨利·洛伦扎托在 Unsplash 上拍摄的照片

ANOVA 代表方差分析,顾名思义,它帮助我们理解和比较不同组之间的方差。在详细讨论方差分析之前,让我们记住统计学中的几个术语:

  • 表示:所有数值的平均值。
  • 方差:值之间变化的度量。计算方法是将每个值和平均值的平方差相加,然后将总和除以样本数。

  • 标准差:方差的平方根。

为了理解方差分析或其他一些统计检验背后的动机,我们应该学习两个简单的术语:总体和样本。

群体是一个群体中的所有元素。举个例子,

  • 美国大学生是指包括美国所有大学生在内的人口。
  • 欧洲的 25 岁人群包括了所有符合描述的人。

对人口进行分析并不总是可行或可能的,因为我们无法收集人口的所有数据。因此,我们使用样本。

样本是总体的子集。举个例子,

  • 美国 1000 名大学生是“美国大学生”人口的一个子集。

进行统计测试是为了观察使用样本计算的结果是否适用于整个人群。

当我们比较两个样本(或组)时,可以用 t 检验来看组的均值是否有差异。当我们有两个以上的组时,t-test 不是最优选择,因为我们需要分别对成对进行 t-test。假设我们有 A 组、B 组和 C 组。为了能够比较平均值,我们需要对 A-B、A-C 组和 B-C 组进行 t 检验。随着组数的增加,这变得更加难以管理。

在比较三组或更多组的情况下,ANOVA 是优选的。方差分析有两个要素:

  • 各组内的差异
  • 组间差异

ANOVA 测试结果基于 F 比率,即组间差异与组内差异的比率。

f 比率表明总变异有多少来自组间变异,有多少来自组内变异。如果大部分差异来自组间的差异,则更有可能是组的平均值不同。然而,如果大多数的变化来自于组内的变化,那么我们可以得出结论,一个组中的元素是不同的,而不是整个组。F 比率越大,各组的均值越有可能不同。

让我们看一个例子。假设我们有 3 组 8 个观察值:

我们想进行方差分析测试,看看这些组是否不同。我将使用 R,但也可以在任何软件包上随意使用。我们从创建组开始:

然后合并各组:

然后将组堆叠在框架中:

然后用一行代码执行 ANOVA:

让我们回顾一下结果:

  • f 值为 7.89,表明各组不同。大于 1 的 f 值表示至少一个组不同于其他组。
  • p 值非常小,表明结果具有统计学意义(即不是由于随机机会产生的)。通常,p 值小于 0.05 的结果被认为具有统计学意义。
  • Df 是自由度。第一行是组间差异,第二行是组内差异,计算如下:

共有 3 组,共 24 个观察值。所以第一行是 2,第二行是 22。我们可以将结果表示为:

到目前为止,我们所做的是一个单向 ANOVA 测试,它基于一个独立变量比较三个或更多组的平均值。还有一个双向 ANOVA 测试,根据两个独立变量比较三组或更多组。

考虑一个营养师想要分析三个不同组的减肥情况。他/她准备了三种不同的饮食,每组喂食不同的饮食。在这种情况下,如果各组中的平均体重减轻不同,则单向 ANOVA 测试适于检查平均体重减轻。如果营养学家也想知道一组中男性和女性的平均体重减轻是否不同,那么双向方差分析测试是合适的。性别和饮食类型是这个双向方差分析测试的因素。

减肥的单向与双向方差分析

感谢您的阅读。如果您有任何反馈,请告诉我。

ANOVA 在单身女郎电视节目中向初学者解释

原文:https://towardsdatascience.com/anova-explained-for-beginners-with-the-bachelorette-tv-show-8503c4aaba10?source=collection_archive---------19-----------------------

又名如何自娱自乐与统计在疫情

杰里·克莱恩在 Unsplash 上的照片

单向 ANOVA(方差分析)是一种参数检验,用于检验 3 个或更多组之间结果的统计显著性差异。ANOVA 测试总体差异,即至少一个组在统计上显著不同于其他组。然而,如果方差分析是显著的,你不能告诉哪组是不同的。你需要事后比较来确定。

我想看看不同季的未婚女子和参赛者之间的年龄差距,看看不同季之间的年龄差距是否有统计学上的显著差异。在这个非常重要的分析中,我将向您介绍:

  • 数据
  • 假设检验
  • 单向 ANOVA 检验
  • 事后分析

数据

我在 Kaggle 上发现了这个非常孤独的数据🌹。经过一些清理和合并,它看起来像这样:

一些人已经调查了参赛者主要来自哪里,所以我想还有什么值得探索的🤔。我决定调查未婚女子和参赛者之间的年龄差距,看看这个节目是否随着时间的推移而改变,或者他们是否试图在每一季保持相同的年龄差距!重要的东西!!

这里快速看一下每个赛季的参赛选手数量(样本大小)和平均年龄差距。不幸的是,《单身女郎时代》在某些季中缺失了,留给我们的只有七个组合(第 1、5、6、9、10、11 和 12 季)。

假设检验

我们需要检查三个假设,以使用非参数统计检验,如 Welch 的 ANOVA 或 Kruskal-Wallis ANOVA:
1。独立性:这意味着所有的群体都是相互排斥的,即一个个体只能属于一个群体。此外,数据不是重复测量的(不是通过时间收集的)。在这个例子中,满足了这个条件。
2。正态性:对于方差分析或回归模型,我们可以通过查看模型残差来检验这一点。每个残差是输入值和该组所有值的平均值之间的差值。我们将使用图形方法,概率图。
3。方差的同质性:我们需要确保所有组都有相等的方差。检验这一假设的一种方法是 Levene 的方差齐性检验。

正态—概率图

概率图根据理论分布绘制有序数据。有序数据与理论分布拟合得越好,与位于图表中间的拟合线的偏差就越小。根据该图,在给定大样本量(n>30)的情况下,有理由声明满足正态性假设。然而,查看绘制的概率图和剩余结构,转换数据进行分析,或使用非参数统计检验,如 Welch 的 ANOVA 或 Kruskal-Wallis ANOVA,也是合理的。

方差齐性——Levene 检验

使用 Levene 检验,我们得到(统计值=0.9092,p 值=0.4895)。p 值大于 0.05,这表明各组的变异性无统计学显著差异。同样,也有必要从视觉上检查这个假设。

方差齐性的图形检验支持统计检验结果,即各组具有相等的方差。
默认情况下,箱形图显示中值(上图中的橙色线)。绿色三角形是每组的平均值。

单向方差分析

让我们从定义零假设和替代假设开始。在这个例子中,零假设是平均年龄差距在所有季节都是相同的,而另一个假设是至少一个季节的平均年龄差距在统计上显著不同于其他季节。

然后,我们需要继续计算一些统计数据,包括 F 统计、F 临界、样本间平方和(SSB)、样本内平方和(SSW)、样本间均方(MSB)、样本内均方(MSW)。

在我们的单身女子的例子中,我们有 7 个季节。所以我们的 k=7,总共有 182 个参赛者,这意味着 n = 182。你可以得到

查看源代码,逐步手动计算这些统计数据。对于本故事的其余部分,我将使用 statsmodel ANOVA 测试和汇总统计的输出。

我们拒绝零假设,H0,如果计算的 F-static 大于临界 F-statistic。临界 F 统计量由自由度(k-1=6,n-k=175)和α决定,我选择α=0.05。你可以使用一个在线 F 分布计算器来得到 F 临界,这里是 2.15。从 stats.f_oneway,我们得到 F _ one way result(statistic = 10.0375,pvalue=1.5990495398830284e-09)。您可以看到计算出的 F 统计值大于 F 临界,因此我们拒绝零假设,这意味着这些季节中至少有一个季节的平均年龄差距明显不同于其他季节。

UnsplashPablo Heimplatz 拍摄的照片

事后分析

现在让我们看看哪些季节不同,哪些季节大同小异!

Tukey 诚实显著差异(HSD)

tukey truly Significant Difference(HSD)测试所有成对组比较,同时控制多重比较,以保护家族误差率,避免出现 I 型错误。

使用剔除列可以很容易地阅读表格。例如,在第一季和第五季之间,我们无法拒绝零假设,这意味着在这两季中年龄差异之间没有统计学上的显著差异。然而,在第一季和第六季之间,情况正好相反。

和往常一样,我喜欢做一个图形比较来总结这个非常有趣的分析😃

我希望你喜欢看这篇文章,下次玫瑰典礼再见!!🥂

回归方差分析

原文:https://towardsdatascience.com/anova-for-regression-fdb49cf5d684?source=collection_archive---------6-----------------------

总和平方和、回归平方和及误差平方和。

Unsplash 上由 Tachina Lee 拍摄的照片

ANOVA ( 方差分析)是一个构成显著性检验基础的框架&提供关于回归模型中可变性水平的知识。它与线性回归相同,但主要区别之一是回归用于根据一个或多个连续预测变量预测连续结果。鉴于, ANOVA 用于预测基于一个或多个分类预测变量的连续结果。

在实现线性回归时,我们经常会遇到诸如 SST (总和平方和) SSR (回归平方和) SSE (误差平方和)之类的术语,并想知道它们实际上是什么意思?在本帖中,我们将涵盖这些主题,并实现一个示例来更好地理解这个主题。

SST(总平方和)

总和平方和是观察因变量与其平均值(均值)之间的平方差。这里需要注意的一点是,我们总是将线性回归最佳拟合线与因变量斜率的平均值(表示为 y ̅)进行比较。

Rahul Pathak媒体上拍摄的照片

SSR(回归平方和)

回归平方和预测值和因变量均值之间的差值之和。

Rahul Pathak媒体上拍摄的照片

SSE(误差平方和)

误差平方的 Sum观察值预测值之间的差值。

Rahul Pathak介质上拍摄的照片

为了理解这些平方和的使用流程,让我们手动浏览一个简单线性回归的例子。假设约翰是加州的酒店的服务员,他有个人的总账单,并且他还收到该订单的小费* 。我们希望根据收到的总账单来预测下一笔小费是多少。让我们用(x)表示总账单,用(y)表示小费金额。*

Rahul Pathak媒体上拍摄的照片

y=α+βx 将给出预测值,而我们根据上述公式计算α & β的值,其中β是斜率,α是 y 截距。简单线性回归的目标是创建一个最小化残差平方和(误差)的线性模型。

关于线性回归的一个有趣的事实是,它是由两个统计概念方差分析和相关性组成的。

线性回归=相关+方差分析 回到正题…

SST,SSR & SSE 是怎么联系起来的?

SST = SSR + SSE

在上表中,我们可以看到之前误差平方和为 120,后来减少到 30.075,即我们使用线性回归将误差值从 120 减少到 30.075。以前,最佳拟合线是因变量斜率的平均值,后来变为最佳最佳拟合线。

120 =?+ 30.075 因此 SSR 的值为 89.925

为什么我们需要平方和?

答案是确定拟合优度。可以使用决定系数来确定,也称为R。R 将比率量化为百分比。此外,R 经常与“R”混淆,其中 R 是决定系数,而 R 是相关系数。相关性测量两个变量 X 和 y 之间的线性相关性。其范围从值 -1 到 1 ,其中接近 1 的值为正相关,接近-1 的值为负相关。例如,在上表中,我们得到的值 r0.8656 ,它更接近于 1,因此描绘了一个正关系。

最终拍板

要记住的重要公式:——

  • R = SSR/SST
  • R = 1-(SSE/SST)
  • SSE =σ(实际-预测)
  • SST =σ(实际平均值)
  • SSR =σ(预测平均值)

我希望我能帮助你回答与这个主题相关的问题。请随时查询我的联系 id:——拉胡尔·帕塔克
非常感谢!😃

R 中的方差分析

原文:https://towardsdatascience.com/anova-in-r-4a4a4edc9448?source=collection_archive---------22-----------------------

了解如何在 R 中执行方差分析(ANOVA)来比较 3 组或更多组。另请参见如何解释结果和执行事后测试

照片由 Battlecreek 咖啡烘焙师拍摄

介绍

NOVA(方差分析)是一种统计测试,用于确定两个或多个总体均值是否不同。换句话说,它用于比较两个或更多组,以查看它们是否显著不同

然而,实际上:

  • 学生 t 检验 用于比较 2 组
  • ANOVA 将 t 检验推广到 2 组以上,因此用于比较 3 组或更多组

注意,ANOVA 有几种版本(例如,单向 ANOVA、双向 ANOVA、混合 ANOVA、重复测量 ANOVA 等。).在本文中,我们只给出最简单的形式——单向 ANOVA1——在本文的剩余部分我们称之为 ANOVA。

虽然 ANOVA 是用来对不同组的表示进行推断的,但这种方法叫做“分析 方差 ”。之所以这么叫,是因为它比较了“之间”方差(不同组之间的方差)和“内”方差(每组内的方差)。如果组间方差明显大于组内方差,则说明组均值不同。否则,我们无法得出这样或那样的结论。通过取比值(方差之间/方差之内),然后将该比值与费希尔概率分布中的阈值(基于特定显著性水平的阈值,通常为 5%)进行比较,将两个方差相互比较。

这是关于方差分析方法的足够的理论。在本文的剩余部分,我们将从更实际的角度来讨论它,特别是,我们将涵盖以下几点:

  • 方差分析的目的、何时使用以及无效/替代假设
  • 方差分析的基本假设以及如何检查它们
  • 如何在 R 中进行方差分析
  • 如何解释方差分析的结果
  • 理解事后测试的概念并解释结果
  • 如何可视化方差分析和事后检验的结果

数据

本文的数据是penguins数据集(众所周知的iris数据集的替代),可通过[{palmerpenguins}](https://github.com/allisonhorst/palmerpenguins) 访问:

# install.packages("palmerpenguins")
library(palmerpenguins)

该数据集包含 3 个不同物种(阿德利企鹅、下巴颏企鹅和巴布亚企鹅)的 344 只企鹅的数据。数据集包含 8 个变量,但我们只关注本文中的鳍肢长度和物种,所以我们只保留这 2 个变量:

library(tidyverse)dat <- penguins %>%
  select(species, flipper_length_mm)

(如果不熟悉管道操作符(%>%),也可以penguins[, c("species", "flipper_length_mm")]选择变量。在关于数据操作的文章中了解更多选择变量的方法。)

下面是一些基本的描述性统计数据和我们数据集的一个图(用[{ggplot2}](https://www.statsandr.com/blog/graphics-in-r-with-ggplot2/) 制作),然后我们继续进行方差分析的目标:

summary(dat)##       species    flipper_length_mm
##  Adelie   :152   Min.   :172.0    
##  Chinstrap: 68   1st Qu.:190.0    
##  Gentoo   :124   Median :197.0    
##                  Mean   :200.9    
##                  3rd Qu.:213.0    
##                  Max.   :231.0    
##                  NA's   :2

鳍状肢的长度从 172 毫米到 231 毫米不等,平均长度为 200.9 毫米。阿德利企鹅、下颚带企鹅和巴布亚企鹅分别有 152 只、68 只和 124 只。

library(ggplot2)ggplot(dat) +
  aes(x = species, y = flipper_length_mm, color = species) +
  geom_jitter() +
  theme(legend.position = "none")

这里,因子species变量,包含 3 个模态或组(Adelie、Chinstrap 和 Gentoo)。

方差分析的目的和假设

如引言中所述,ANOVA 用于比较各组(实际上是 3 组或更多组)。更一般地说,它用于:

  • 研究分类变量的不同模态(在方差分析中也称为水平或处理)的测量值是否相似
  • 比较分类变量的不同水平对定量变量的影响
  • 根据定性变量解释定量变量

在这种情况下,作为一个例子,我们将使用方差分析来帮助我们回答这个问题:“三种企鹅的鳍状肢长度不同吗?”。

方差分析的无效假设和替代假设是:

  • H0:μ_ Adelie =μ_ Chinstrap =μ_ Gentoo(三个物种的鳍状肢长度相等)
  • H1: 至少有一个平均值是不同的(至少有一个物种与其他两个物种的鳍长度不同)

注意另一个假设是 而不是 所有的方法都不同。所有平均数相等的反面(H0)是至少有一个平均数不同于其他平均数(H1)。在这个意义上,如果零假设被拒绝,这意味着至少有一个物种不同于其他 2,但不一定是所有 3 个物种彼此不同。可能阿德利物种的鳍状肢长度不同于下颚带和巴布亚,但是下颚带和巴布亚的鳍状肢长度相似。必须执行其他类型的测试(称为事后测试,包含在本部分中)来测试所有 3 个物种是否不同。

方差分析的基本假设

至于许多统计测试,为了能够解释结果,需要满足一些假设。当一个或几个假设不满足时,尽管技术上可以进行这些测试,但解释结果和相信结论是不正确的。

下面是方差分析的假设,如何测试它们,以及如果假设不满足,还有哪些测试:

  • 变量类型 : ANOVA 需要混合一个连续定量因变量(对应于与问题相关的测量)和一个定性自变量(至少有 2 个水平,用于确定要比较的组)。
  • ****独立性:从总人口中随机选择的代表性部分收集的数据,在组间和组内应该是独立的。独立性的假设通常基于实验的设计和对实验条件的良好控制来验证,而不是通过正式的测试。如果你仍然不确定基于实验设计的独立性,问问自己一个观察是否与每个组内或组间的另一个观察相关(如果一个观察对另一个有影响)。如果没有,很可能你有独立的样本。如果样本之间的观察值(形成待比较的不同组)是相关的(例如,如果三个测量值是在相同的个体上收集的,这是医学研究中在测量(I)治疗前、(ii)治疗期间和(iii)治疗后的指标时经常出现的情况),那么重复测量值 ANOVA 应该是首选的,以便考虑样本之间的相关性。
  • ****正态:残差 2 应该近似遵循一个 正态分布 。正态性假设可以通过直方图QQ 图进行直观测试,和/或通过正态性测试进行正式测试,如夏皮罗-维尔克或科尔莫戈罗夫-斯米尔诺夫测试。如果,即使在对数据进行变换(例如,对数变换、平方根、Box-Cox 等)后,),残差仍然不符合近似正态分布,可以应用 Kruskal-Wallis 检验(R 中的kruskal.test(variable ~ group, data = dat)。这种非参数检验对非正态分布稳健,与 ANOVA 的目标相同—比较 3 个或更多组—但它使用样本中位数而不是样本均值来比较组。
  • ****方差相等:不同组的方差在总体中应该相等(这种假设被称为方差的同质性,或者有时被称为同方差,与方差在不同组之间不相等的异方差相对)。这种假设可以通过图形测试(例如通过比较箱线图点线图中的离散度),或者更正式地通过 Levene 测试({car}包中的leveneTest(variable ~ group))或 Bartlett 测试,等等。如果等方差假设被拒绝,可以使用 ANOVA 的另一个版本:Welch 检验(oneway.test(variable ~ group, var.equal = FALSE))。请注意,韦尔奇检验不要求方差齐性,但分布仍应近似遵循正态分布。请注意,Kruskal-Wallis 检验既不需要正态假设,也不需要方差的同方差假设。 3

根据假设是否满足来选择合适的测试可能会令人困惑,因此这里有一个简短的总结:

  1. 检查你的观察是否独立。
  2. 如果它们是独立的,检验残差的正态性:
  • 如果假设正态性,检验方差的同质性:
  • 如果差异相等,使用 ANOVA
  • 如果差异不相等,使用韦尔奇测试
  • 如果没有假设正态性,使用克鲁斯卡尔-沃利斯检验

既然我们已经看到了方差分析的基本假设,我们在应用适当版本的测试之前,专门为我们的数据集回顾它们。

可变类型

因变量flipper_length_mm是一个定量变量,自变量species是一个定性变量(3 个等级对应 3 个物种)。所以我们有两种变量的混合,这个假设是成立的。

独立性ˌ自立性

假设观察值是独立的,因为数据是从人群中随机选择的部分收集的,并且 3 个样本内部和之间的测量值是不相关的。

独立性假设通常是基于实验设计和对实验条件的良好控制来验证的,就像这里的情况一样。如果你真的想更正式地测试它,你可以通过一个统计测试来测试它——Durbin-Watson 测试(在 R: durbinWatsonTest(res_lm)中,其中res_lm是一个线性模型)。该检验的零假设指定自相关系数= 0,而替代假设指定自相关系数≠ 0。

常态

请记住,残差的正态性可以通过直方图QQ 图进行直观测试,和/或通过正态性测试(例如夏皮罗-维尔克测试)进行正式测试。

在检查正态性假设之前,我们首先需要计算方差分析(在部分中有更多相关信息)。然后我们将结果保存在res_aov:

res_aov <- aov(flipper_length_mm ~ species,
  data = dat
)

我们现在可以直观地检查正常性:

par(mfrow = c(1, 2)) # combine plots# histogram
hist(res_aov$residuals)# QQ-plot
library(car)
qqPlot(res_aov$residuals,
  id = FALSE # id = FALSE to remove point identification
)

从上面的直方图和 QQ-plot,我们已经可以看到,正态性假设似乎得到了满足。事实上,直方图大致形成一条钟形曲线,表明残差遵循正态分布。此外,QQ 图中的点大致遵循直线,并且它们中的大多数都在置信带内,这也表明残差近似遵循正态分布。

一些研究人员就此打住,假设满足正态性,而其他人也通过正式的统计测试来检验这一假设。您可以选择(I)仅通过目测,(ii)仅通过正态性测试,或(iii)同时通过目测和正态性测试进行测试。但是,请记住以下两点:

  1. 方差分析对偏离正态分布的小偏差非常稳健。这意味着如果少量的点稍微偏离正态性(从 ANOVA 结果解释的角度来看),
  2. 正态性检验有时相当保守,这意味着正态性的零假设可能会由于与正态性的有限偏离而被拒绝。对于大样本来说尤其如此,因为测试的功效随着样本量的增加而增加。

实际上,我倾向于只选择(I)视觉方法,但是,这是一个个人选择的问题,也取决于分析的背景。

还是为了说明,我们现在也通过正态性检验来检验正态性假设。你可以使用夏皮罗-维尔克检验或者科尔莫戈罗夫-斯米尔诺夫检验等等。请记住,无效假设和替代假设是:

  • H0:数据来自正态分布
  • H1:数据做 不是 来自正态分布

在 R 中,借助于shapiro.test()函数,我们可以用夏皮罗-维尔克检验来检验残差的正态性:

shapiro.test(res_aov$residuals)## 
##  Shapiro-Wilk normality test
## 
## data:  res_aov$residuals
## W = 0.99452, p-value = 0.2609

P-残差的夏皮罗-维尔克检验值大于通常的显著性水平α=5%,所以我们不拒绝残差服从正态分布的假设(P-值= 0.261)。**

这个结果符合目测法。在我们的例子中,正态假设在视觉上和形式上都得到了满足。

边注:提醒 p 值是假设零假设为真,我们在样本中观察到的极端情况的概率。如果 p 值* < α (表示假设原假设为真,不太可能观察到我们在样本中拥有的数据),则拒绝原假设,否则不拒绝原假设。查看更多关于 p 值和显著性水平 如果你对那些重要的统计学概念不熟悉。***

请记住,如果没有达到正态假设,则需要对原始数据进行一些变换,希望残差能够更好地符合正态分布,或者您需要使用 ANOVA 的非参数版本—Kruskal-Wallis 检验。

正如一位读者所指出的(见文章最后的评论),正态性假设也可以在“原始”数据(即观察值)而不是残差上进行检验。但是,如果您测试原始数据的正态性假设,则必须单独测试每组数据,因为方差分析需要每组数据的正态性。**

对所有残差或每组观察值进行正态性检验是等效的,并且会给出相似的结果。的确,说“每组内 Y 的分布是正态分布”和说“残差是正态分布”是一样的。

请记住,残差是 Y 的实际值与特定 X 值的 Y 平均值之间的距离,因此在计算残差时会引入分组变量。

总之,在方差分析中,你实际上有两个检验正态性的选择:

  1. 分别检查每组“原始”数据(Y 值)的正态性
  2. 检查所有残差的正态性(但不是每个组)

在实践中,您会发现,只使用残差并一起检查它们通常会更容易,尤其是当您有许多组或每组的观察值很少时。

如果你仍然不相信:记住方差分析是线性模型的一个特例。假设你的自变量是一个连续变量(而不是一个分类变量),你剩下的唯一选择就是检查残差的正态性,这正是在线性回归模型中检验正态性所做的。

方差相等—同质性

假设残差服从正态分布,现在是时候检查物种之间的方差是否相等了。结果将会影响我们是否使用方差分析或韦尔奇检验。

这可以再次通过视觉验证——通过箱线图点线图——或者更正式地通过统计测试(Levene 的测试,等等)。

视觉上,我们有:

*# Boxplot
boxplot(flipper_length_mm ~ species,
  data = dat
)*

*# Dotplot
library("lattice")dotplot(flipper_length_mm ~ species,
  data = dat
)*

箱线图和点图都显示了不同物种的相似方差。在箱线图中,这可以从以下事实中看出:对于所有物种来说,盒子和须状物具有可比较的大小。有几个异常值,如胡须外的点所示,但这不会改变不同物种之间的分散或多或少相同的事实。

在点图中,这可以通过所有 3 个物种的点具有或多或少相同的范围这一事实看出,这是离散的标志,因此方差是相似的。

与正态性假设一样,如果您觉得直观的方法不够充分,您可以使用 Levene 或 Bartlett 的测试来正式测试方差的相等性。请注意,Levene 检验对偏离正态分布的敏感度低于 Bartlett 检验。

两种检验的无效假设和替代假设是:

  • H0:差异是相等的
  • H1:至少有一个差异是不同的

在 R 中,Levene 的测试可以通过{car}包中的leveneTest()函数来执行:

*# Levene's test
library(car)leveneTest(flipper_length_mm ~ species,
  data = dat
)## Levene's Test for Homogeneity of Variance (center = median)
##        Df F value Pr(>F)
## group   2  0.3306 0.7188
##       339*

p-值大于 0.05 的显著性水平,我们不拒绝零假设,所以我们不能拒绝物种间方差相等的假设(p-值= 0.719)。**

这个结果也符合视觉方法,所以方差的同质性在视觉上和形式上都得到满足。

检验正态性和同质性的另一种方法

供您参考,也可以通过plot()功能直观地(同时)测试方差的同质性和残差的正态性:

*par(mfrow = c(1, 2)) # combine plots# 1\. Homogeneity of variances
plot(res_aov, which = 1)# 2\. Normality
plot(res_aov, which = 2)*

左侧的图显示残差和拟合值(每组的平均值)之间没有明显的关系,因此假设方差齐性。如果方差的同质性被破坏,红线就不会是平的。

右手边的图显示残差近似遵循正态分布,因此假设正态性。如果常态被破坏,点将持续偏离虚线。

方差分析

我们表明方差分析的所有假设都得到满足。因此,我们可以继续实施 R 中的方差分析,但首先,让我们做一些初步的分析,以更好地理解研究问题。

初步分析

在实际执行 R 中的 ANOVA 之前,一个好的做法是可视化与研究问题相关的数据。最好的方法是绘制并比较每个物种的数量变量flipper_length_mm的箱线图。**

这可以通过基数 R 中的boxplot()函数来完成(与等方差的视觉检查代码相同):

*boxplot(flipper_length_mm ~ species,
  data = dat
)*

或者用[{ggplot2}](https://www.statsandr.com/blog/graphics-in-r-with-ggplot2/) :

*library(ggplot2)ggplot(dat) +
  aes(x = species, y = flipper_length_mm) +
  geom_boxplot()*

上面的方框图显示,至少对我们的样本来说,Gentoo企鹅似乎有最大的鳍状肢,而Adelie企鹅的鳍状肢最小。

除了每个物种的箱线图外,计算一些描述性统计数据也是一个很好的做法,例如物种的平均值标准差。例如,这可以通过aggregate()功能来实现:****

***aggregate(flipper_length_mm ~ species,
  data = dat,
  function(x) round(c(mean = mean(x), sd = sd(x)), 2)
)##     species flipper_length_mm.mean flipper_length_mm.sd
## 1    Adelie                 189.95                 6.54
## 2 Chinstrap                 195.82                 7.13
## 3    Gentoo                 217.19                 6.48***

或者使用{dplyr}组件中的summarise()group_by()功能:

***library(dplyr)group_by(dat, species) %>%
  summarise(
    mean = mean(flipper_length_mm, na.rm = TRUE),
    sd = sd(flipper_length_mm, na.rm = TRUE)
  )## # A tibble: 3 x 3
##   species    mean    sd
##   <fct>     <dbl> <dbl>
## 1 Adelie     190\.  6.54
## 2 Chinstrap  196\.  7.13
## 3 Gentoo     217\.  6.48***

平均值也是Adelie最低,Gentoo最高。然而,箱线图和描述性统计数据不足以得出结论,三个企鹅种群的鳍状肢有显著差异。

R 中的方差分析

正如你现在所猜测的,只有方差分析可以帮助我们根据手头的样本对种群做出推断,并帮助我们回答最初的研究问题“三种企鹅的鳍状肢长度不同吗?”。

R 中的方差分析可以用几种方法进行,下面介绍其中的两种方法:

  1. 使用oneway.test()功能:
***# 1st method:
oneway.test(flipper_length_mm ~ species,
  data = dat,
  var.equal = TRUE # assuming equal variances
)## 
##  One-way analysis of means
## 
## data:  flipper_length_mm and species
## F = 594.8, num df = 2, denom df = 339, p-value < 2.2e-16***

2.使用summary()aov()功能:

***# 2nd method:
res_aov <- aov(flipper_length_mm ~ species,
  data = dat
)summary(res_aov)##              Df Sum Sq Mean Sq F value Pr(>F)    
## species       2  52473   26237   594.8 <2e-16 ***
## Residuals   339  14953      44                   
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 2 observations deleted due to missingness***

从上面的两个输出可以看出,两种方法的检验统计量(F =在第一种方法中,而F value在第二种方法中)和p-值(p-value在第一种方法中,而Pr(>F)在第二种方法中)完全相同,这意味着在方差相等的情况下,结果和结论不会改变。

第一种方法的优点是很容易从 ANOVA(方差相等时使用)切换到 Welch 检验(方差不相等时使用)。这可以通过用var.equal = FALSE替换var.equal = TRUE来实现,如下所示:

***oneway.test(flipper_length_mm ~ species,
  data = dat,
  var.equal = FALSE # assuming unequal variances
)## 
##  One-way analysis of means (not assuming equal variances)
## 
## data:  flipper_length_mm and species
## F = 614.01, num df = 2.00, denom df = 172.76, p-value < 2.2e-16***

然而,第二种方法的优点是:

  • 完整的 ANOVA 表(含自由度、均方差等。)打印出来,这在某些(理论上的)情况下可能是有意义的
  • 方差分析(res_aov)的结果可以保存以备后用(对事后检验特别有用)

方差分析结果的解释

鉴于p-值小于 0.05,我们拒绝零假设,因此我们拒绝所有均值相等的假设。因此,我们可以断定至少有一个物种在脚蹼长度(p-value<2.2e-16)方面与其他物种不同。****

(为了便于说明,如果p-值大于 0.05:我们不能拒绝所有平均值相等的零假设,因此我们不能拒绝所考虑的三种企鹅的鳍长度相等的假设。)

在 R 中报告方差分析结果的一种简单而又好的方法是使用{report}包中的report()函数:

***# install.packages("remotes")
# remotes::install_github("easystats/report") # You only need to do that once
library("report") # Load the package every time you start Rreport(res_aov)## The ANOVA (formula: flipper_length_mm ~ species) suggests that:
## 
##   - The main effect of species is significant and large (F(2, 339) = 594.80, p < .001; Eta2 = 0.78, 90% CI [0.75, 0.80])
## 
## Effect sizes were labelled following Field's (2013) recommendations.***

如您所见,该函数为您解释了结果,并显示了物种对鳍状肢长度的大而显著的主要影响(p-值【t 33.001】)。

注意report()功能可用于其他分析。如果你觉得这个有用,请查看 R 中的更多提示和技巧。

下一步是什么?

如果零假设没有被拒绝 ( p -value ≥ 0.05),则说明我们没有拒绝所有组都相等的假设。方差分析差不多到此为止。当然,也可以进行其他类型的分析,但是——根据手头的数据——我们无法证明至少有一组是不同的,所以我们通常不会进一步进行方差分析。****

相反,如果零假设被拒绝(因为这是我们的情况,因为p-值< 0.05),我们证明至少有一组是不同的。如果我们只对检验所有物种的鳍状肢长度是否相等感兴趣,我们可以决定就此打住。****

但是大多数时候,当我们通过方差分析表明至少有一个群体是不同的时候,我们也有兴趣知道哪一个群体是不同的。然而,方差分析的结果是 而不是 告诉我们哪一组与其他组不同。

为了测试这一点,我们需要使用其他类型的测试,称为事后测试(在拉丁语中,“在此之后”,因此在获得具有统计意义的 ANOVA 结果之后)或多个成对比较测试。这一系列的统计测试是下面几节的主题。

事后测试

多重测试问题

为了看出哪一组与其他组不同,我们需要对两组进行比较。实际上,由于有 3 个物种,我们将按如下方式比较物种 2 和 2:****

  1. 下颚带对抗阿德利
  2. Gentoo 对 Adelie
  3. Gentoo vs. Chinstrap

理论上,我们可以通过 3 个学生的 t 检验来比较物种,因为我们需要比较两个组,在这种情况下 t 检验被精确地使用。

但是,如果执行了几个 t 测试,就会出现多重测试(也称为多重性)的问题。简而言之,当几个统计测试被执行时,一些会有p-值小于α纯属偶然,即使所有的无效假设事实上都是真的。****

为了演示这个问题,考虑我们的情况,我们有 3 个假设要测试,期望的显著性水平为 0.05。偶然观察到至少一个重要结果(至少一个p-值< 0.05)的概率为:

因此,只要考虑 3 个测试,我们就有 14.26%的机会观察到至少一个显著的结果,即使所有的测试实际上都不显著。

随着组数的增加,比较的次数也在增加,所以仅仅因为偶然而产生重要结果的概率也在增加。例如,对于 10 个组,我们需要进行 45 次比较,至少有一个重要结果的概率变成 1−(1−0.05)^45 = 90%。因此,当比较 10 组时,很可能只是偶然观察到显著的结果,而当我们有 14 组或更多组时,我们几乎肯定(99%)有假阳性!

事后检验考虑到进行了多次检验,并通过以某种方式调整α来处理问题,因此,由于偶然性而观察到至少一个显著结果的概率保持在我们期望的显著性水平以下。 4

R 中的事后检验及其解释

事后测试是一系列统计测试,所以有几种。最常用的是 Tukey HSDDunnett 的测试:

  • Tukey HSD 用于比较所有组(因此所有可能的 2 组比较)。****
  • Dunnett 用于与参照组进行比较。例如,考虑 2 个治疗组和 1 个对照组。如果您只想将 2 个治疗组与对照组进行比较,而不想将 2 个治疗组进行相互比较,则首选 Dunnett 检验。

请注意,假设两个测试的方差相等。它们将在下一节中介绍。如果方差不相等,可以使用 Games-Howell 检验等方法。

图基 HSD 试验

在我们的例子中,由于没有“参考”物种,我们对比较所有物种感兴趣,我们将使用 Tukey HSD 测试。

在 R 中,Tukey HSD 测试如下进行。这就是执行 ANOVA 的第二种方法派上用场的地方,因为结果(res_aov)被重新用于事后检验:

***library(multcomp)# Tukey HSD test:
post_test <- glht(res_aov,
  linfct = mcp(species = "Tukey")
)summary(post_test)## 
##   Simultaneous Tests for General Linear Hypotheses
## 
## Multiple Comparisons of Means: Tukey Contrasts
## 
## 
## Fit: aov(formula = flipper_length_mm ~ species, data = dat)
## 
## Linear Hypotheses:
##                         Estimate Std. Error t value Pr(>|t|)    
## Chinstrap - Adelie == 0   5.8699     0.9699   6.052   <1e-08 ***
## Gentoo - Adelie == 0     27.2333     0.8067  33.760   <1e-08 ***
## Gentoo - Chinstrap == 0  21.3635     1.0036  21.286   <1e-08 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## (Adjusted p values reported -- single-step method)***

在 Tukey HSD 测试的输出中,我们感兴趣的是在Linear Hypotheses:之后显示的表格,更准确地说,是表格的第一列和最后一列。第一列显示已经进行的比较;最后一列(Pr(>|t|))显示每次比较的调整后的5p-值(零假设为两组相等,替代假设为两组不同)。

正是这些调整后的 p 值用于测试两组是否有显著差异,我们可以确信整个比较集合的误差率为 0.05。

在我们的示例中,我们测试了:

  1. 下颚带对抗阿德利(第Chinstrap - Adelie == 0行)
  2. 巴布亚对阿德利(线Gentoo - Adelie == 0)
  3. Gentoo vs. Chinstrap(第Gentoo - Chinstrap == 0行)

所有三个p-值都小于 0.05,因此我们拒绝所有比较的零假设,这意味着所有物种在鳍状肢长度方面都有显著差异****

事后测试的结果可以通过plot()功能可视化:

***par(mar = c(3, 8, 3, 3))
plot(post_test)***

我们看到置信区间没有越过零线,这表明所有组都有显著差异。

请注意,Tukey HSD 测试也可以在 R 中使用TukeyHSD()功能完成:

***TukeyHSD(res_aov)##   Tukey multiple comparisons of means
##     95% family-wise confidence level
## 
## Fit: aov(formula = flipper_length_mm ~ species, data = dat)
## 
## $species
##                       diff       lwr       upr p adj
## Chinstrap-Adelie  5.869887  3.586583  8.153191     0
## Gentoo-Adelie    27.233349 25.334376 29.132323     0
## Gentoo-Chinstrap 21.363462 19.000841 23.726084     0***

对于这个代码,我们感兴趣的是列p adj(也是最后一列)。请注意,结论与上面的相同:所有物种的鳍状肢长度都有显著差异。

结果也可以通过plot()功能可视化:

***plot(TukeyHSD(res_aov))***

邓尼特试验

我们在本部分中已经看到,随着组数的增加,比较的次数也在增加。随着比较次数的增加,事后分析必须进一步降低个体显著性水平,这导致较低的统计功效(因此群体中组均值之间的差异不太可能被检测到)。****

减轻这种情况并提高统计功效的一种方法是减少比较次数。这种减少允许后特设过程使用更大的个体错误率来实现期望的全局错误率。虽然用 Tukey HSD 试验比较所有可能的组是一种常见的方法,但许多研究有一个对照组和几个治疗组。对于这些研究,您可能只需要将治疗组与对照组进行比较,这样可以减少比较的次数。

邓尼特的测试恰恰做到了这一点——它只是将一个群体与所有其他群体进行比较,而不是将所有群体相互比较。

概括一下:

  • Tukey HSD 测试允许比较所有组,但代价是*更少的功率*******
  • 邓尼特测试只允许与参照组进行比较,但好处是更有力量********

现在,再次为了说明起见,考虑物种Adelie是参照物种,我们只对参照物种与其他两个物种的比较感兴趣。在这种情况下,我们将使用邓尼特测试。

在 R 中,Dunnett 测试按如下方式进行(与 Tukey HSD 测试代码的唯一区别是在第linfct = mcp(species = "Dunnett")行):

***library(multcomp)# Dunnett's test:
post_test <- glht(res_aov,
  linfct = mcp(species = "Dunnett")
)summary(post_test)## 
##   Simultaneous Tests for General Linear Hypotheses
## 
## Multiple Comparisons of Means: Dunnett Contrasts
## 
## 
## Fit: aov(formula = flipper_length_mm ~ species, data = dat)
## 
## Linear Hypotheses:
##                         Estimate Std. Error t value Pr(>|t|)    
## Chinstrap - Adelie == 0   5.8699     0.9699   6.052 7.59e-09 ***
## Gentoo - Adelie == 0     27.2333     0.8067  33.760  < 1e-10 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## (Adjusted p values reported -- single-step method)***

解释与 Tukey HSD 试验相同,除了在 Dunett 试验中,我们只比较:

  1. 下颚带对抗阿德利(第Chinstrap - Adelie == 0行)
  2. Gentoo vs. Adelie(第Gentoo - Adelie == 0行)

两个p-值(显示在最后一列)都低于 0.05,因此我们拒绝两个比较的零假设。这意味着物种下巴颏带和巴布亚企鹅在鳍状肢长度方面与参照物种阿德利有显著差异。(不过,关于 Chinstrap 和 Gentoo 之间的比较,没什么可说的。)**

同样,事后测试的结果可以通过plot()功能可视化:

***par(mar = c(3, 8, 3, 3))
plot(post_test)***

我们看到置信区间没有越过零线,这表明 Gentoo 和 Chinstrap 两个物种都与参考物种 Adelie 显著不同。

注意,在 R 中,默认情况下,因子变量的参考类别是字母顺序中的第一个类别。这就是默认情况下参考物种是阿德利的原因。

可以用relevel()功能(或用[{questionr}](https://www.statsandr.com/blog/rstudio-addins-or-how-to-make-your-coding-life-easier/#reordering-factors) 插件)改变参考类别。考虑到我们希望 Gentoo 而不是 Adelie 作为参考类别:

***# Change reference category:
dat$species <- relevel(dat$species, ref = "Gentoo")# Check that Gentoo is the reference category:
levels(dat$species)## [1] "Gentoo"    "Adelie"    "Chinstrap"***

Gentoo 现在是三个类别中的第一个,它确实被认为是参考级别。

为了使用新的参考值执行 Dunnett 检验,我们首先需要重新运行 ANOVA 以考虑新的参考值:

***res_aov2 <- aov(flipper_length_mm ~ species,
  data = dat
)***

然后,我们可以使用方差分析的新结果运行 Dunett 检验:

***# Dunnett's test:
post_test <- glht(res_aov2,
  linfct = mcp(species = "Dunnett")
)summary(post_test)## 
##   Simultaneous Tests for General Linear Hypotheses
## 
## Multiple Comparisons of Means: Dunnett Contrasts
## 
## 
## Fit: aov(formula = flipper_length_mm ~ species, data = dat)
## 
## Linear Hypotheses:
##                         Estimate Std. Error t value Pr(>|t|)    
## Adelie - Gentoo == 0    -27.2333     0.8067  -33.76   <1e-10 ***
## Chinstrap - Gentoo == 0 -21.3635     1.0036  -21.29   <1e-10 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## (Adjusted p values reported -- single-step method)par(mar = c(3, 8, 3, 3))
plot(post_test)***

根据以上结果,我们得出结论,阿德利和下巴颏物种在鳍状肢长度方面与巴布亚物种有显著差异(p-值< 1e-10)。**

请注意,即使您的研究没有可以与其他组进行比较的参考组,进行由一些研究问题决定的多重比较通常也比进行所有成对测试要好。通过将事后比较的数量减少到必要的数量,你可以最大化统计能力。 6

其他 p 值校正方法

对于感兴趣的读者,请注意,通过使用pairwise.t.test()功能,您可以使用其他 p 值调整方法:

***pairwise.t.test(dat$flipper_length_mm, dat$species,
  p.adjust.method = "holm"
)## 
##  Pairwise comparisons using t tests with pooled SD 
## 
## data:  dat$flipper_length_mm and dat$species 
## 
##           Gentoo  Adelie 
## Adelie    < 2e-16 -      
## Chinstrap < 2e-16 3.8e-09
## 
## P value adjustment method: holm***

默认情况下,将应用 Holm 方法,但也存在其他方法。参见?p.adjust了解所有可用选项。

同一地块上方差分析和事后检验的可视化

如果你有兴趣在同一个图上(直接在箱线图上)包括方差分析和事后测试的结果,这里有一段你可能感兴趣的代码(我根据这篇文章中的代码编辑的):

***# Edit from here
x <- which(names(dat) == "species") # name of grouping variable
y <- which(
  names(dat) == "flipper_length_mm" # names of variables to test
)
method1 <- "anova" # one of "anova" or "kruskal.test"
method2 <- "t.test" # one of "wilcox.test" or "t.test"
my_comparisons <- list(c("Chinstrap", "Adelie"), c("Gentoo", "Adelie"), c("Gentoo", "Chinstrap")) # comparisons for post-hoc tests
# Edit until here # Edit at your own risk
library(ggpubr)
for (i in y) {
  for (j in x) {
    p <- ggboxplot(dat,
      x = colnames(dat[j]), y = colnames(dat[i]),
      color = colnames(dat[j]),
      legend = "none",
      palette = "npg",
      add = "jitter"
    )
    print(
      p + stat_compare_means(aes(label = paste0(..method.., ", p-value = ", ..p.format..)),
        method = method1, label.y = max(dat[, i], na.rm = TRUE)
      )
      + stat_compare_means(comparisons = my_comparisons, method = method2, label = "p.format") # remove if p-value of ANOVA or Kruskal-Wallis test >= alpha
    )
  }
}***

正如你在上面的图上看到的,物种的箱线图与方差分析和事后检验的 p 值一起显示。

除了在同一个图上结合了可视化表示和结果的事实之外,这段代码还具有可以一次执行多个 ANOVA 测试的优点。更多信息请参见本文部分。

摘要

在本文中,我们回顾了 ANOVA 的目标和假设,在能够信任结果之前需要验证哪些假设(即独立性、正态性和同质性),然后我们向展示了如何在 R 中进行 ANOVA,以及如何解释结果

如果不讨论事后检验,特别是Tukey HSD——比较所有组——和 Dunnett 的检验——比较一个参照组和所有其他组,那么一篇关于方差分析的文章就不完整。

最后但同样重要的是,我们展示了如何在同一个图中可视化方差分析和事后检验的数据和结果。

感谢阅读。

和往常一样,如果您有与本文主题相关的问题或建议,请将其添加为评论,以便其他读者可以从讨论中受益。

  1. 请注意,它被称为单向单因素* ANOVA,因为均值与单个自变量或因素的不同模态相关。 ↩︎***
  2. 残差(表示为ϵϵ)是因变量的观测值(y)和预测值(y hat)之间的差值。在 ANOVA 的上下文中,残差对应于观察值和该组所有值的平均值之间的差异。 ↩︎
  3. 只要你使用 Kruskal-Wallis 检验来比较各组,就不需要同质性。如果你想比较中位数,Kruskal-Wallis 检验需要同质性。在这篇文章中查看更多关于差异的信息。 ↩︎
  4. 注意,正如在文章末尾的评论中所讨论的,事后检验在某些情况下可以直接进行(不需要 ANOVA)。详见许( 1996 )的评论。 ↩︎
  5. 请注意,原则上您可以将 Bonferroni 校正应用于所有测试。例如,在上面的例子中,有 3 个测试和α = 0.05 的全局期望显著性水平,如果p-值小于 0.05 / 3 = 0.0167,我们将拒绝零假设。然而,已知这种方法相当保守,导致潜在的高假阴性率。 ↩︎**
  6. 调整p-值,将全局显著性水平保持在所需水平。 ↩︎**
  7. 感谢 Michael Friendly 的建议。 ↩︎

相关文章

原载于 2020 年 10 月 12 日 https://statsandr.com

Python 中的 ANOVA + Tukey 测试

原文:https://towardsdatascience.com/anova-tukey-test-in-python-b3082b6e6bda?source=collection_archive---------4-----------------------

使用 Python 中的统计测试方法开发在线广告策略(附代码)。

方案

我们的客户是一家刚起步的服装公司,在美国专门生产休闲服装。他们的营销团队想推出一个广告活动来增加其网站的在线流量,这有望带来更多的收入。为了更好地分配投放广告活动的时间和精力,并最大限度地让观众看到他们的广告,他们需要了解 3 件事。

消费者搜索最多的与运动休闲相关的关键词是什么?

哪个月消费者搜索休闲服装最多?

消费者使用最多的搜索平台是哪个?

目标

向 athleisure 初创公司提供建议,以确定投放广告的最佳关键词、最佳时机和最佳平台。

数据

为了收集这个案例研究的数据,我们将使用Wordtracker——一种用于搜索引擎优化(SEO)的付费数据库服务。搜索引擎优化本质上只是另一种说法:

“我想知道如何让我的网站成为给定搜索的首要结果。”

Wordtracker 帮助客户获得更多的网站流量,或者更好地了解消费者在搜索什么。Wordtracker 类似于 Google Keywords Planner 服务,但允许访问 Google 以外平台上的搜索数据。在提取数据时,Wordtracker 提供了 2018 年 6 月至 2019 年 5 月在谷歌、Youtube、亚马逊和易贝的 1 年代表性搜索数据样本。它提供了来自 106 个国家的 1800 万全球小组成员的超过 20 亿个独特的关键词。以下是 Wordtracker 数据库中与美国搜索量相关的所有数据的概要。

来源:Alex Cheng+Justin FleuryviaGitHub

但是我们如何决定在 Wordtracker 的搜索量查询中选择哪些与“运动休闲”相关的词呢?有很多方法,但是有什么比在网上购买运动休闲流行语更好的方法呢?我们可以在亚马逊上搜索术语“运动休闲”,并在结果中找到所有最频繁出现的术语。

来源:Alex Cheng+Justin FleuryviaGitHub

因此,在我们的研究中,我们可以通过以下约束条件从 Wordtracker 中提取数据:

  • 70 多个与运动休闲相关的术语,在搜索“运动休闲”时使用顶级亚马逊关键词结果。
  • 仅在美国搜索卷。
  • 来自谷歌、YouTube 和亚马逊的搜索量数据。

根据搜索引擎杂志——谷歌、YouTube 和亚马逊是全球最受欢迎的三大搜索引擎。根据 2019 年的 Bluelist 统计,每年大约有 2 万亿次谷歌搜索。Wordtracker 在一年时间内提供了近 20 亿次谷歌搜索。我们假设 Wordtracker 提供了全世界整个 Google 搜索数据库的大约 1/1000 的代表性样本。

清理后的数据集可以下载 这里 为. csv 文件。关于如何从 Wordtracker 的 API 调用数据的完整代码,请参考这个 Jupyter 笔记本 这里 。有关如何清理数据的更多信息,请参考数据清理 Jupyter 笔记本 此处

探索性数据分析

Tableau 仪表板

一旦数据被提取和清理,我们就可以执行 探索性数据分析 ,简称 EDA。创建 Tableau 仪表板是在一个地方全面了解数据中的维度和度量之间的各种关系的好方法。下面显示了该仪表板的静态预览。完全互动的 Tableau 仪表盘可以在 这里 探索。

来源:Alex Cheng+Justin FleuryviaGitHub

单词云

一组词汇云提供了一种非技术性的、图形化的尺度感,来理解每个搜索引擎中搜索次数最多的休闲词汇。关键词在词云中出现的越大,那么与其他关键词相比,该关键词被搜索的次数就越多。

每月平均关键词搜索量—折线图

这个线形图显示了一年中每个月平均搜索 athleisure 关键字的频率。寒冷月份的搜索次数似乎比温暖月份多,高峰在 12 月。

来源:Alex Cheng+Justin FleuryviaGitHub

每个关键字的总搜索量—条形图

当我们在所有三个搜索引擎(Google + YouTube + Amazon)上汇总所有运动休闲相关关键词的搜索量时,我们注意到关键词“hoodie”是搜索次数最多的术语,有 130 万次搜索。其次是“运动衫”、“跑步”、“锻炼”和“flex”,都有几十万次搜索。在考虑的所有 77 个关键词中,平均搜索次数约为 100,000 次。

来源:Alex Cheng+Justin FleuryviaGitHub

每个引擎的总容量和“运动休闲”搜索比率—条形图

在观察每个搜索引擎的指标时,我们发现与谷歌相比,亚马逊和 YouTube 的总搜索量非常低。但是,尽管亚马逊的整体搜索量最低,但它的运动休闲相关词汇的比例却是最高的。亚马逊上超过 25%的搜索都与运动休闲关键词相关!相比之下,YouTube 上 5%的搜索与运动休闲关键词相关。谷歌上只有不到 1%的搜索与运动休闲关键词相关。这些发现表明,亚马逊和 Youtube 可能比谷歌更适合运行广告,因为人们显然更经常在这些平台上搜索休闲关键词。

在左边的柱状图中,我们可以看到谷歌的总搜索量接近 600,000,000,而亚马逊和 YouTube 的总搜索量不到 100,000,000。然而,在右边的柱状图中,我们可以看到与运动休闲相关的术语的搜索比率,与每个搜索引擎上的所有搜索相比。

来源:Alex Cheng+Justin FleuryviaGitHub

概率密度函数

下面的概率密度函数图显示,我们相当多的关键词的搜索量很小——与我们的其他关键词相比,接近于零。显然,有些关键词的搜索率比其他关键词高得多,而高搜索率的关键词很少。

来源:Alex Cheng+Justin FleuryviaGitHub

累积密度函数

下面的累积密度函数图显示,我们 90%的关键词的搜索量低于 200,000。再一次,这避免了我们的大量关键词具有低搜索量,并且只有少数高搜索关键词。这个 CDF 图似乎是对数性质的。

来源:Alex Cheng+Justin FleuryviaGitHub

统计测试

在我们进入变量的统计检验之前,下面是假设检验和我们将用来确定统计显著性的概念的简要概述。

假设检验

很简单地说——零假设(H0) 是声称变量之间不存在统计显著关系的假设,而替代假设(HA) 则声称变量之间存在统计显著关系。

阿尔法值

α值是当零假设为真时拒绝零假设的概率。这也称为假阳性或 I 型错误。我们在每个假设检验中选择α值。

较高的alpha 值意味着我们可以接受较高的错误概率,而较低的alpha 值意味着我们可以接受非常低的错误概率。当出现错误时没有大的后果时,较高的 alpha 值可能是可以的,而当出现错误时有可怕的后果时(如医疗诊断、刑事判决或任何危及生命的情况),应该使用较低的 alpha 值。****

Alpha 值通常介于小于 0.01 和 0.1 之间。对于我们的案例研究,我们可以使用一个既不太宽松也不太严格的α值。因此,我们将使用 0.05 的 alpha 值。

p 值

p 值 是反对零假设的“证据”。p 值越小,我们可以拒绝零假设的证据就越强。我们将 p 值与为统计测试设置的 alpha 值进行比较。

  • 如果 p 值是< 0.05, then we reject the null hypothesis.
  • If the p-value is > = 0.05,那么我们无法拒绝零假设。

方差分析

单向方差分析或 ANOVA 是我们选择的统计测试,因为我们要处理多个组。我们还将使用双因素方差分析来确定可能具有统计显著性的因素组合。

图基试验

ANOVA 的问题是它只比较组间的平均值,并确定这些平均值是否有统计学上的显著差异。简而言之:方差分析告诉我们结果是否显著,但没有告诉我们结果在哪里显著。

但是,为了指导我们的运动休闲广告策略,统计意义的可解释性是至关重要的。我们必须能够解释哪些关键词表现最好,哪个搜索引擎最好,或者哪个月最适合投放广告!

因此, Tukey 检验 允许我们解释 ANOVA 检验的统计意义,并找出哪些特定组的均值(相互比较)不同。因此,在进行每一轮方差分析后,我们应该使用 Tukey 检验来找出数据中出现统计显著性的地方。

单向 ANOVA + Tukey 检验

假设检验 1:关键词

问题:当考虑搜索量时,与 athleisure 相关的关键词有什么区别吗?

零假设(H0) —就平均搜索量而言,所有与运动休闲相关的关键词都是相同的。

替代假设(HA) —一些与运动休闲相关的关键词比其他关键词有更大的平均搜索量。

单因素方差分析结果:

p 值= 1.3293563590514185 e-119< 0.05 (This is nearly zero.)

We reject the null hypothesis that mean search volume is equal across all athleisure-related keywords. Keyword on its own, does indeed constitute a difference in average search volume for athleisure-related items.

Tukey 测试结果:

统计上与其他术语“最”不同的前 5 个术语是:

  • “帽衫”
  • “跑步”
  • “运动衫”
  • “锻炼”
  • "弹性"

假设检验 2:月

问题:考虑搜索量时,月份之间有区别吗?

零假设(H0) —在任何给定的月份,人们搜索运动服相关词汇的可能性都是一样的。

替代假设(HA)——人们将更有可能根据月份搜索运动服相关的词汇。

单因素方差分析结果:

  • p 值= 0.8831258135517717 > 0.05
  • 我们无法拒绝所有月份的平均搜索量相等的无效假设。
  • 月份本身并不构成运动休闲相关项目搜索量的差异。

Tukey 测试结果:

  • 没有必要运行 Tukey 多重比较测试,因为我们在这里没有拒绝零假设。

假设检验 3:搜索引擎

当考虑搜索量时,搜索引擎之间有什么不同吗?

零假设(H0) —在任何平台上,运动服相关词汇的搜索量都是相等的。

替代假设(HA) —在一个特定的平台上,运动服相关术语的搜索量会更大。

单因素方差分析结果:

  • p 值= 7.19196465389629e-18 < 0.05(这个几乎为零。)
  • 我们拒绝所有搜索引擎的平均搜索量相等的无效假设。
  • 搜索引擎本身,确实构成了运动休闲相关项目平均搜索量的差异。

Tukey 测试结果:

  • 在所有情况下,拒绝零假设,即搜索引擎 1 与搜索引擎 2 在平均搜索量方面相等。
  • 每个平台的搜索量都是不同的。

密码

下面是如何在 Python 中执行单向 ANOVA 和 Tukey 测试的代码片段示例。

对于单向 ANOVA,我们使用 SciPy 库(注意:也可以使用 Statsmodels 库)。我们将数据强制转换为字典,并将键提供给函数【scipy . stats . F _ one way(),该函数返回 F-statistic 和 p-value(这就是我们想要的)。

对于 Tukey 测试,我们为 pairwise_tukeyhsd() 函数分配一个变量,其中我们提供我们的响应变量(搜索量)、我们正在测试的组(在本例中是搜索引擎)和我们的 alpha 值(0.05)。然后,我们简单地打印结果。

多重方差分析+ Tukey 检验

所有三个因素之间的双因素方差分析将帮助我们了解这些因素的任何两个组合是否具有统计显著性。我们本质上想回答这个问题:

“我们能确定哪些特定的关键字/月份/搜索引擎的 2 因素组合产生最高的搜索量吗?”

组合 1:关键词+引擎

零假设(H0) —就平均搜索量而言,所有关键词/引擎组合都是相同的。

备选假设(HA) —一些关键词/引擎组合具有更大的平均搜索量。

双因素方差分析结果:

  • p 值= 1.008919e-151 < 0.05 (This is nearly zero.)
  • Reject the null hypothesis that the mean search volume is equal among all Keyword/Engine combinations. Tukey Test needed.

Tukey 测试结果:

  • 有 10 个关键词/引擎组合在搜索量上有显著差异。

组合 2:关键字+月份

零假设(H0) —就平均搜索量而言,所有关键字/月份组合都是相同的。

备选假设(HA) —一些关键词/月份组合具有更大的平均搜索量。

双因素方差分析结果:

  • p 值= 7.896266e-01 > 0.05
  • 无法拒绝平均搜索量在关键字/月份组合中相等的无效假设。没有 Tukey 测试。

组合 3:引擎+月份

零假设(H0) —就平均搜索量而言,所有引擎/月份组合都是相同的。

替代假设(HA) —一些引擎/月份组合具有更大的平均搜索量。

双因素方差分析结果:

  • p 值= 7.789742e-01 > 0.05
  • 无法拒绝引擎/月份组合中平均搜索量相等的无效假设。没有 Tukey 测试。

密码

下面是如何在 Python 中执行双向 ANOVA 和 Tukey 测试的代码片段示例。

对于双向方差分析,我们提供了一个字符串“formula”来定义我们的组,如stats models . formula . API . ols()函数所要求的,将这个拟合的 ols()函数赋给一个变量,然后将该变量送入 sm.stats.anova_lm() 函数。在输出中,我们查看 PR( > F)列,它提供了我们的 p 值(这就是我们想要的)。

对于双向 ANOVA 之后的 Tukey 测试,我们将一个变量分配给pairwise _ tukeyhsd()函数,其中我们提供了我们的响应变量(搜索量)、我们正在测试的组(在本例中是关键字+搜索引擎的组合)以及我们的 alpha 值(0.05)。然后,我们将输出强制转换为数据帧,以便于分析和过滤,并添加一个“total_sum”列来合计每组中每个观察值的所有(真)零剔除。有非常多的组合,所以我们只显示前 20 个结果。

推荐

关键词

在所有平台和月份的测试中,有 5 个关键词优于任何其他与运动休闲相关的关键词。我们可能会建议广告活动使用这 5 个流行语:

  • 【帽衫】
  • “跑步”
  • 《运动衫》
  • “健身程序”
  • 【flex】

发动机

广告不应该在谷歌上投放,因为它对运动休闲相关关键词的搜索量最低。如果搜索量是最重要的,那么我们会推荐 YouTube。如果市场份额是最重要的,那么我们会推荐亚马逊。

月份本身在统计上不足以作为一个提供可靠建议的因素。只有在与特定平台和一组关键字结合时,月份才应被视为一个因素。

关键词/引擎

以下是我们推荐给运动休闲服装创业公司的 10 大关键词/引擎组合,以帮助他们指导他们的在线广告工作。再次注意,谷歌不推荐作为一个平台来运行运动休闲广告。

改进+未来工作

丰富

  • 我们可以确保作为休闲关键词测试的所有单词类型都是相似的。例如,使用所有名词,或所有形容词等…
  • 我们可能会将结果限制在与服装明显相关的搜索上。例如,考虑将一个与“运动休闲”相关的形容词与一件衣服配对,例如:“透气连帽衫”、“透气短裤”、“条纹慢跑者”等
  • 我们可能会确保所有被比较的平台在它们提供的服务上是相同的,以获得更好的准确性。例如,把谷歌比作必应,或者 YouTube 和 Vimeo,或者亚马逊和易贝。

未来的工作

  • 我们可以探索每个引擎的搜索量统计数据,例如:年龄、性别或收入。
  • 我们可以调查转换率——意思是谁在看完广告后真正购买了产品。
  • 我们可以考虑在特定平台上投放广告的成本。例如,与亚马逊相比,谷歌上的广告成本是多少?

谢谢!

感谢您阅读这篇博客!我希望它是有用的,并使统计测试在当代、真实世界用例中的能力更加清晰。我愿意倾听您的想法和反馈!所有代码和数据都可以在我的 GitHub 资源库 找到。随时和我联系LinkedIn

Python 和 SQL 的方差分析

原文:https://towardsdatascience.com/anova-with-python-and-sql-b37b68ebc2dc?source=collection_archive---------41-----------------------

理解为什么以及如何进行方差分析

来源

T 方差分析(ANOVA)用于分析方差,包括评估两个变量之间的差异。这是一个有效的统计工具,用于分析财务或客户数据,有助于监控规划过程和成功管理项目。当您收集了一个分类自变量和一个数量因变量的数据时,应该使用单向 ANOVA。自变量应该至少有三个层次。这里,我们将使用 pyodbc 从 SQL 中获取一个临床试验数据集,在 Python 上运行 ANOVA 并解释结果。

ANOVA 是怎么来的?

在我们开始之前,先讲一点背景故事!在 ANOVA 之前,人们使用多重 t 检验来比较变量之间是否存在差异。随着世界的进步,数据变得更加庞大,群体的数量也在增加。做多重 t 检验是不可行的,因此 ANOVA 诞生了。请注意,如果您只想比较两组,请使用 t 检验

方差分析的步骤有哪些?

  • 检查样本量,我们需要每组中相同数量的观察值。
  • 计算每组的均方差。
  • 计算均方误差。
  • 通过将组间差异除以组内差异来计算 F 值。
  • 使用 f 分布表确定零假设。

方差分析的假设有哪些?

  1. 夏皮罗-维尔克检验可用于检查残差的正态分布。零假设是数据来自正态分布。
  2. 巴特利特检验检查方差的同质性。零假设是来自方差相等的总体的样本。数据不是来自正态分布?尝试进行 Levene 测试。

数据是什么?

我们将使用的数据是临床试验数据。它显示了患者在三种不同药物下的记忆表现。为了从 SQL server 获取数据,我们将使用 Python 包 pyodbc。

输入所需的凭据后,我们通过执行以下命令获得所需的数据:

cursor.execute("SELECT Drug,(Mem_Score_After - Mem_Score_Before) as Diff FROM trials WHERE Drug IN ('A','S','T')")

获取数据后,我们可以将其传输到 pandas 进行进一步分析。如果我们通过药物描述和可视化目标变量值,我们得到:

药品描述

毒品箱线图

注:从箱线图和药物说明中可以看出,服用药物 A 的患者的记忆表现明显最好。

首先,需要对假设进行定义。

无效假设: 意思是不同药物的记忆表现是一样的。

交替假设: 意思是不同药物的记忆表现并不相同(可能是两种药物的意思相同,但不是全部)

现在让我们运行一个单向 ANOVA 测试,看看这些组是否确实不同。python 包统计数据用于这部分分析。

stats.f_oneway(df[‘Diff’][df[‘Drug’] == ‘A’], 
 df[‘Diff’][df[‘Drug’] == ‘S’],
 df[‘Diff’][df[‘Drug’] == ‘T’])

方差分析得出的 P 值显著(P <0.05),

and therefore, we conclude that there are significant differences among treatments. The results show an F statistic of 22. If F value is bigger than F-critical, which is in our case, we reject the null hypothesis. If F value is smaller than F-critical, we accept the null hypothesis.

这有意义吗?让我们再想象一下。

药物直方图

我们可以看到,对于两个群体来说,他们之间的差异低于他们内部的差异。对于第三组,显示为蓝色,其分布似乎与其他两组完全不同。因此,我们的眼睛也告诉我们,蓝色显示的药物治疗与其他两种不同。

结论

我们做了一个基本的单向方差分析,看看是否有任何药物改善记忆表现。在获得显著结果和大于 F 临界值的 F 统计值后,我们得出结论,药物没有相同的效果。从直方图中我们可以看出,其中一种药物的性能比另外两种高得多。这是一个简单的数据集,如果它更复杂,我们将使用 Tukey HSD 测试来查看哪些组是不同的。

单因素方差分析是一种很好的方法,可以用来观察不同组之间是否存在差异,让它成为客户组、试验组或财务运营相关的实验组。我相信,如果你还没有将它们运用到你的工作中,你可以找到一种方法。

我欢迎反馈,如果您有问题,请告诉我。你可以在 LinkedIn 上找到我。

安斯科姆的四重奏,又名巨魔数据集

原文:https://towardsdatascience.com/anscombes-quartet-aka-the-troll-dataset-b4b42da42f51?source=collection_archive---------37-----------------------

为什么你应该在下结论前绘制数据

马克·柯尼希在 Unsplash 上的照片

你有没有想过统计学家做什么是为了好玩?你可能会认为他们会喜欢去赌场,并通过告诉人们如何通过赌博来浪费金钱来破坏人们的夜晚。或者你可以想象一个人花一整天的时间掷硬币来测试它是公平的还是有偏见的。

但是你错了。事实上,当一个统计学家在寻找一点温和的娱乐时,他们会花时间寻找有创意的方法来欺骗人们。看看下面这个例子。

这是统计学家弗朗西斯·安斯科姆在 1973 年创建的数据集。我们这里有四个数据集(标为 I、II、III 和 IV,因为罗马数字很酷),每个数据集由 11 对 x 和 y 坐标组成。

(对于这个讨论来说,坐标实际代表什么量并不特别重要。如果你愿意,你可以自己做。x 可以是给定圣代冰淇淋的勺数,y 可以是我吃完所有冰淇淋所用的秒数。好吃。)

因为人类的大脑在从表格数据中收集信息方面是垃圾——即使对于这个小得可怜的数据集也是如此——让我们分别绘制出四个数据集并进行比较(如果您感兴趣,您可以查看 Python 代码来完成此操作这里)。

(为了便于比较,四个数据集的每一个都以相同的比例绘制在 x 轴和 y 轴上。)

你可能想知道我要说什么。这四个数据集似乎没有什么共同点——它们都显示出截然不同的趋势,只有傻瓜才会把它们混为一谈。

好吧,让我们看看当我们调用我们的老朋友线性回归时会发生什么。

啊哦…这是最适合他们所有人的同一系列!这是怎么发生的?

也许我们可以发现每组数据中 x 和 y 之间相关性的差异。

Correlation coefficient for:Dataset I: 0.81642051634484
Dataset II: 0.8162365060002428
Dataset III: 0.8162867394895982
Dataset IV: 0.8165214368885031

现在我们真的有麻烦了…相关系数基本上都是一样的!有什么方法可以将这些数据集与它们的汇总统计数据区分开来呢?

(Mean of x values, mean of y values) for:

Dataset I: (9.0, 7.5)
Dataset II: (9.0, 7.5)
Dataset III: (9.0, 7.5)
Dataset IV: (9.0, 7.5)

(Variance of x values, variance of y values) for:

Dataset I: (11.0, 4.13)
Dataset II: (11.0, 4.13)
Dataset III: (11.0, 4.12)
Dataset IV: (11.0, 4.12)

真是一场噩梦!尽管我们已经看到数据的形状和模式一点也不相似,但您可能想要用来描述每个数据集的大多数简单、直观的汇总统计数据对所有四个数据集来说都是相同的。

你可能会想,如果我们没有事先把数据绘制出来,会发生什么样的可怕事情。我们可能已经看到了相同的汇总统计数据,并假设所有四个数据集具有相同的分布。我们的结论——以及因此而采取的任何行动——都将是不切实际的。

因此,把这当作一个警示故事吧——尽管汇总统计数据肯定是有用的,可以为我们提供比我们仅仅盯着图表所能收集到的更精确的信息,但它们过于简单的本质也可能具有欺骗性。在你的探索性数据分析中,永远不要跳过可视化数据:以不同的方式绘制、重新绘制、再绘制,以尝试和揭示新的模式。

因为正如我们所看到的,冰冷、坚硬的数字可能在欺骗你。

学分和更多信息

Andrew Hetherington 是英国伦敦的一名见习精算师和数据爱好者。

  • 查看我的网站
  • LinkedIn 上与我联系。
  • 看看我在 GitHub 上鼓捣什么。
  • 用于制作本文作品的笔记本可以在这里找到。

数据集:Anscombe,F. J. (1973)。“统计分析中的图表”。美国统计学家。27 (1): 17–21.doi:10.1080/00031305.19478966。JSTOR 2682899。

Mark knigUnsplash 上拍摄的路标照片。

Ansible —端口监控。

原文:https://towardsdatascience.com/ansible-ports-monitoring-15f48c8e208e?source=collection_archive---------23-----------------------

今天,我将向您展示 Ansible 如何简化我们在监控服务器间网络通信时可能面临的技术难题。

事实上,在像银行业这样高度敏感的行业中,港口受到严格的监管,只有必要的港口才允许 SecOps 开放。否则,不受监管的端口可能成为 DDOS 等网络攻击的诱人目标。

为了更容易地解释事情,让我们想象一组名为的主机应该与另一组名为目标的主机通信,只通过授权的端口

主机可以是 cloud、docker、VM、metal server……端口可以是任何(https 443、SMTP 25、PostgreSQL 5432、Ldap 389、elk 9200、Redis 6379 或任何其他定制端口)。

现在我们到了有趣的部分:)

众所周知,SRE/DevOps 最重要的职责是监控。那么,SRE 团队如何轻松有效地监控服务器间的端口通信,尤其是在大型库存上?

他们如何在每个网络修补程序后执行运行状况检查,以确认没有退化?

更重要的是,他们如何在服务中断期间快速可靠地检查问题是否来自主机网络内部通信问题?

编排前(Ansible,Terraform..),解决方案是创建一个 shell、PowerShell 或 python 脚本来 telnet 或 ping 任何目标主机。这种解决方案的最大缺点是必须在所有源服务器上一个接一个地手动启动脚本。

有了 Ansible,事情变得更加智能、自动化和简单。

事实上,我们可以创建一个任务(可以是 cron 作业),该任务将在所有源主机上执行,以通过定义的端口检查与所有目标主机的通信,并在出现问题时发送报告/警报。

现在让我们转到编码部分:)

我们将从创建 ansible 主任务开始:
roles/ansi ble-role-ports/tasks/main . yml

---
- name: Check global ports
  wait_for:
    host: "{{ item.name }}"
    port: "{{ item.port }}"
    state: started         # Port should be open
    delay: 0               # No wait before first check (sec)
    timeout: 3             # Stop checking after timeout (sec)
  ignore_errors: yes
  with_items: "{{ server_to_test }}"

这是主要的 ansible 角色任务,指示将通过端口“item.port”从“item.name”远程登录到“server_to_test”中的主机列表。

现在让我们创建我们的游戏:
plays/ports/ports . yml

---
- name: check ports
  hosts: "{{ host }}"
  become: yes
  become_user: root
  roles:
    - ansible-role-ports

这是一个可行的行动,它将使用清单中的源主机和目标主机来调用任务。

关于清单,它只是列出了主机(源和目标),每个主机都有一个 group_vars 参数,该参数指定了用于每组源服务器的目标主机和目标端口。

inventory/hosts-update . yml

source1:
  hosts:
    host1:
      ansible_host: ip-source1-host1
      host_name: source1-host1
    host2:
      ansible_host: ip-source1-host2
      host_name: source1-host2

source2:
  hosts:
    host1:
      ansible_host: ip-source2-host1
      host_name: source2-host1
    host2:
      ansible_host: ip-source2-host2
      host_name: source2-host2

target1:
  hosts:
    host1:
      ansible_host: ip-target1-host1
      host_name: target1-host1
    host2:
      ansible_host: ip-target1-host2
      host_name: target1-host2     

target2:
  hosts:
    host1:
      ansible_host: ip-target2-host1
      host_name: target2-host1
    host2:
      ansible_host: ip-target2-host2
      host_name: target2-host2

最后是每组源主机的 group_vars:

inventory/group _ vars/source 1 . yml

---
server_to_test:
  - {name: 'target1', port: 'port1'}#target1
  - {name: 'target1', port: 'port2'}#target1

  - {name: 'target2', port: 'port1'}#target2
  - {name: 'target2', port: 'port2'}#target2

inventory/group _ vars/source 2 . yml

---
server_to_test:
  - {name: 'target1', port: 'port3'}#target1
  - {name: 'target1', port: 'port4'}#target1

该剧的用法如下:

---
ansible-playbook -i inventory/hosts-update.yml plays/ports/ports.yml — extra-vars ‘{“host”:”source1,source2”}’ -k -K -vv -u “USER”

Source1、source2 可由任何其他主机集更改。

我们结束了,所以一如既往,我希望你学到了新的东西。
再见:)

萨拉姆。

使用 SQL 回答业务问题

原文:https://towardsdatascience.com/answering-business-questions-with-sql-fd0315707980?source=collection_archive---------14-----------------------

SQL 变得简单

惊讶于你的询问的力量!

让 SQL 打开您的数据抽屉!来源

大多数与数据科学相关的工作都需要你有很强的 SQL 技能,这是有意义的,因为你最基本的任务之一就是准确地操作数据。我们将 SQL 用于许多目的,包括探索性数据分析、创建数据的过滤数据集,甚至是发现关于业务本身的见解。

许多 SQL 课程将为您提供如何设计基本查询的概述,但您很少有机会练习编写基于真实问题的查询,使用多个表并再现我们作为数据分析师通常会做的事情,因此我在本教程中的目标是向您展示如何使用流行的 SQL 函数来完成这些工作,例如:

  • 格当;
  • 子查询;
  • 窗口功能;
  • 临时表

在我们开始之前,您可以在这里找到数据集,并且跟随教程的 SQL 文件是这里

理解数据

第一步是要很好地了解我们手中有什么样的数据。今天,我们将使用一家巴西网店的交易数据。让我们看看数据库是如何组织的:

数据库模式。来源

有 8 个表,都由至少一个字段连接。这些数据包含大约 10 万笔交易,包括关于客户、支付、产品和订单的信息。有很多可能性的空间,但我们将把重点放在关于销售额的问题上。假设您的经理问了您 3 个问题:

  • 每个州的顶级买家是谁,他们花了多少钱?
  • 留下差评的人平均花费多少?正面评价呢?
  • 在 2017 年的几个月里,销售额增加或减少了多少,按百分比计算?

为了回答这些问题,我使用了最流行的 SQL 管理系统之一的 SQL Server。还有许多其他可用的变体,如 PostgreSQLSQLiteMySQL 。语法可能会稍有变化,但主要概念在不同的语言中保持不变。

作为参考,下面是我们今天要用的四个表格的第一行。花点时间检查字段以及表是如何连接的。

表的第一行是客户、订单和订单项目。

每个州的顶级买家是哪些?

对于这个查询,我们需要选择每个州中总购买量最高的客户。我们需要的数据在 客户订单项目 表中,我们需要使用 订单 作为连接器。我们在这种情况下使用的方法是在一个窗口函数中生成一个子查询,按照州聚集结果,并只选择价格最高的行。

该查询返回巴西 27 个州中的最佳买家,您可以查看下面的前五行:

按州列出的最佳买家,前 5 行。来源

按情感划分的平均费用

情感分析是数据科学中的热门话题之一,因此您可能需要对其进行分类以执行分析。我们有一个评论分数栏,范围从 1 到 5。对于这个例子,我们将假设评分等于 4 或 5 的评论是正面的,其他任何评论都是负面的。接下来,我们将计算每组的平均值,为此,我们需要使用三个表:order _ reviewsorder_itemsorders (再次作为连接符)。我们将在这里使用一个公共表表达式 (CTE),它的作用就像一个临时表,我们可以从这个表中使用计算字段进行查询,因为我们不能直接使用 average 函数。

我们得到的结果表明,这两种情绪之间的价格相差不大。很有趣,不是吗?

查询结果。快乐的人和消极的人只有 0.61 美分的差距。来源

2017 年月销售额

对于最后一个查询,我们需要表 订单订单 _ 项目 。挑战在于获得前几个月的总销售额,然后将差额与实际月份进行比较。我们需要的步骤是:

  • 创造一个 CTE;
  • 使用 LAG() 创建一个包含上月销售额的列;
  • 在外部查询中,执行计算,使用 FORMAT() 以一种良好的方式获得结果

结果看起来很容易理解。在这里,你可以看到 2017 年的销售波动有多大:

2 月和 11 月的销售额大幅增长,这些信息可用于计划库存水平,甚至预测未来的收入。来源

今天就到这里吧!在本教程中,您已经看到了 3 个简单的问题是如何需要 SQL 为您提供的各种函数的。查询设计是一个令人惊奇的探索主题,其中许多变体可以产生强烈的影响,因此它总是一个很好的研究主题。

你会做出不同的查询吗?我们都是来学习的,所以请在评论中留下你的评论或其他问题。我希望你喜欢这篇文章!

回答深奥的问题:ML 应用与算法

原文:https://towardsdatascience.com/answering-the-abstruse-ml-applications-algorithms-c1892dd6786c?source=collection_archive---------82-----------------------

针对不知所措的初学者的各种机器学习算法的概述。

机器学习:一个关于统计模型和计算机算法的研究领域,用于在新的环境中做出预测,这是 T2 的领域。

一年前,我对机器学习几乎一无所知。这是一个算法和数学的世界,我非常想成为其中的一部分。它很酷,很令人兴奋,也很时尚,而且吸引了我在大学学习期间对数学和编程的兴趣。我很幸运地在 2019 年秋季被城市空间分析研究生课程录取,将我推入了数据科学的世界。

在此之前,我记得滚动媒体,尽可能多地消耗关于机器学习。我对什么是机器学习有一个困惑的想法,部分是因为在谷歌上快速搜索“机器学习”会让机器人和大脑出现在未来的蓝光中。(在谷歌图片搜索中自己去看吧。)我把机器学习和机器人接管星球混为一谈(我知道这么幼稚和愚蠢;有人发明了时间旅行,所以我可以给过去的自己一巴掌。

机器学习==机器人弹琴???有时候。(照片由 Franck V.Unsplash 上拍摄)

此外,机器学习已经成为一个时髦的词,经常与人工智能(AI)互换使用,尽管机器学习只是人工智能的一个子集,增加了对机器学习是什么的更多困惑。机器学习比简单的编程要微妙得多,而且不仅仅是关于机器人!

在接触了不同的机器学习问题后,我意识到机器学习不是一种技术,而是一个领域。以这种方式构建机器学习让我更容易开始研究它。这不是一个一次性的学习技巧,而是一个广泛的算法工具箱,可以在大量的设置中使用。

机器学习的应用非常广泛

虽然机器在棋盘游戏中击败专业人士或脸书预测我们 feed 上显示的最可爱的猫的照片是机器学习的令人敬畏和令人兴奋的应用,但机器学习也可以用于许多其他应用。例如,机器学习可以用于医疗诊断以识别肿瘤是良性还是恶性。机器学习还可以用于城市规划,比如预测一个城市的潜在火灾风险。机器学习也可以通过定向营销用于业务。也就是说,给定一些关于你的消费者的数据,你可以确定未来潜在的市场消费者。斯坦福大学的 CS229 m 机器学习项目 网站提供了一个机器学习应用的绿洲。

预测费城火灾数量的机器学习。图片作者。

机器学习应用的非穷尽列表:

  1. 图片分类:这是猫还是狗?
  2. 文本分类:我的邮件是垃圾邮件吗?
  3. 物体识别:这是停车标志吗?(用于自动驾驶汽车!)
  4. 公共政策:在这个路口发生交通事故的风险有多大?
  5. 游戏:下一步采取什么行动?

考虑到各种各样的应用,在机器学习下有一大堆不同的算法可以用来解决不同的问题。

不同的问题不同的算法。

在这里,我用算法来指代解决机器学习问题的广义过程。

要使用的算法取决于问题的背景。作者图片

关键词:
-数据集:不同数据实例的集合/表格,具有给定特征集合的值。
-实例:一个实例就是一个数据点。
-特性:数据点的每个实例都有一个特性值。
-标签/结果:要预测的项目。这可能是也可能不是数据集的一部分。
-训练/拟合:为减少预测误差/损失的模型寻找“解决方案”。

问题类型

第一步是确定你需要解决的问题类型。两个主要问题是分类(预测类别)或回归(预测数值)。例如,肿瘤的医学诊断是一个分类问题,其结果是良性或恶性。而房价预测将是一个回归问题,结果是任何实数。因此,不同的统计方法被用来解决分类或回归问题。

学习类型

接下来,还有不同的类型的学习。除了决定问题是分类问题还是回归问题之外,你还需要决定在给定信息的情况下需要什么样的学习。这里有三个主要的学习问题。

  1. 监督学习
    给定数据和结果,你可以建立一个模型来预测一组新数据的结果。(根据我的个人经验,这是最常见的一类机器学习问题。)
    用途:利用以往肿瘤的扫描和结果数据进行肿瘤分类,利用前几年的销售价格数据预测房价。(注意:既可以是回归问题,也可以是分类问题!)
  2. 无监督学习
    给定只是数据而不知道有什么标签存在,找到数据背后的一些隐藏结构。
    使用:识别城市周围的火灾群,或者在社交网络中寻找群体。
  3. 强化学习
    强化学习的目标是利用学习到的奖励来确定每个状态下的最佳行动。
    用途:玩游戏或者
    机器人学习拿着一个物体/探索新的地形。

统计方法的类型

在确定你试图解决的问题类型(回归/分类)和学习问题类型(监督/非监督/强化)之后,最后一步是决定使用的统计方法。这些不同的数学模型帮助我们对数据进行预测。

在不深入研究太多细节的情况下,这里有各种机器学习方法的简要概述。

  1. 决策树(监督、分类/回归)

还记得你十几岁时在杂志上做过的那些小测验吗?回答几个问题,跟着一些箭头,它会告诉你你理想中的名人迷恋对象。?这就是决策树!决策树用于分类甚至回归(虽然有点难)。每个节点都有数据的特定特征;在医学诊断的例子中,疼痛的位置。在叶节点(最终节点),对在该点结束的数据实例进行分类。训练然后,机器学习模型包括决定在每个节点上分割数据的特征。目标是创建一个能够正确分类训练数据的最优决策树(最短的)。然后,结果树将用于预测新的实例(即,进入医院的新患者)。

二元分类问题的简单决策树。图片作者。

2。线性回归(监督回归)

线性回归用于回归问题(预测数值),比如预测房价。该模型通过拟合数据的线性组合来预测结果。

*房价(标签)=常数+B1 *房龄+B2 *户型+B3 房间数

上述等式中的常数和系数将在训练阶段被识别。然后,拟合的方程将用于根据新数据预测房价。

3。逻辑回归(监督分类)

逻辑回归是一种使用线性方程来预测数据的某个实例属于某个类别的概率的方法。然后,可以设置概率的阈值,使得如果预测的概率小于阈值,则该实例属于 A 类,否则属于 B 类。

4。模型集成(监督、分类/回归)

随机森林是集合方法最著名的例子。这个想法是从许多不同的决策树中收集一个预测的集合。

5。神经网络和深度学习(监督或无监督,分类)

神经网络通常用于图像分类。这种方法对我来说仍然很疯狂,是一种更“黑箱”的机器学习方法。神经网络松散地模仿了人类的大脑。这个想法是在网络中堆叠不同的层来传递数据,并在这些层之后输出预测。在训练过程中,拟合不同层之间的权重。使用这些权重,神经网络用于对新数据进行预测。

p.s .深度学习是机器学习但机器学习不是深度学习。

简单的 2 层神经网络示例。左侧的第一组节点是输入数据。图片作者。

6。k-均值聚类(无监督,分类)

回想一下,无监督学习意味着原始数据事先没有任何分配的标签。在下面的动画中,每个点代表一个数据实例,它们是根据轴所代表的要素值绘制的。目标是将数据划分为 k 个分区,在这些分区中,数据被分类到离其中心最近的聚类中。分割继续进行,直到点之间的距离和所有聚类内的平均位置(即,距离的方差)最小化。

“训练”k 均值聚类模型。图片来源:Chire/CC BY-SA(【https://creativecommons.org/licenses/by-sa/4.0】T2)

7。q-学习(强化)

Q-learning 算法用于解决强化学习问题,它可以更新在训练过程中在环境的每个状态下采取不同行动的预期回报。当将来使用该模型时,在环境的每个状态下,将选择具有最高预期未来回报的行动。

解决机器学习问题的算法有无限多种。

我在上面提到了一些算法,这也不是完整的列表。此外,每个算法都有进一步的微调方法和参数。此外,有许多方法可以使上述算法更快地达到解,并且在训练模型时部署这些方法。因此有无限数量的机器学习算法!举个简单的例子,你可以构建一个不同深度(节点层)的决策树,或者一个不同树数的随机森林,或者你可以在 k-means 聚类中使用不同的迭代。在实践中,许多具有不同参数的不同模型被用于解决机器学习问题。评估方法(机器学习的另一个广泛领域)用于识别问题上下文中的最佳模型。

这篇文章非常简要地概述了机器学习可以解决的不同问题以及不同类型的机器学习方法!如果要从这篇文章中学到什么的话,那就是机器学习是一个极其多样化和深入的研究领域,一种尺寸并不适合所有人。而且机器学习也不是机器人接管世界(大多是)。本文之外还有很多关于机器学习技术的内容,我祝你在 ML 之旅中好运!

反种族主义、算法偏见和政策:简介

原文:https://towardsdatascience.com/anti-racism-algorithmic-bias-and-policing-a-brief-introduction-bafa0dc75ac6?source=collection_archive---------33-----------------------

詹姆斯·伊德斯在 Unsplash 上的照片

最近,我对各种与反种族主义、算法偏见和监管相关的问题感兴趣。

反种族主义的警察看起来像什么?

我们所说的算法偏差和算法公平是什么意思?

数据科学和机器学习从业者如何确保他们在工作中反种族主义?

传统上,维持治安的目的是确保公众的日常安全。这往往涉及警察部队对涉嫌犯罪活动的报告作出反应。然而,我们可能正在进入一个新的治安时代。新技术,包括传统的数据分析以及可能被称为机器学习或人工智能的技术,使警察部队能够对可疑的犯罪活动做出预测,而这在以前是不可能的。

我们可能正处于一个新技术发展速度超过为确保这些技术的安全使用所必需的监管速度的时期。我认为这是“安全差距”或“责任差距”。

我希望回答这些与反种族主义、算法偏见和监管相关的问题,并通过一些最近的例子,引导你思考这些与安全和问责制相关的问题。

7 月,《麻省理工技术评论》发表了一篇题为“预测性警务算法是种族主义者”的文章。它们需要被拆除。

这篇文章讲述了一位名叫 Yeshimabeit Milner 的活动家成为创始人的故事,他在 2017 年共同创立了 Data for Black Lives,以反击刑事司法系统中的偏见,并拆除所谓的学校到监狱的管道。

米尔纳的重点是预测性警务工具和警察部队滥用数据。

根据这篇文章,有两大类预测性监管算法。

基于位置的算法,它通过使用地点、事件、历史犯罪率、天气状况来创建犯罪“天气预报”,例如 PredPol,被美国数十个城市的警察部队使用。

基于个人的算法,它通过使用年龄、性别、婚姻状况、药物滥用史、犯罪记录来预测一个人是否有很高的机会参与未来的犯罪活动,例如,一种称为 COMPAS 的工具,被司法机构用来帮助做出审前释放和判决的决定,它会发布一个 1 到 10 之间的统计分数,以量化一个人在释放后再次被捕的可能性。

使用预测算法存在许多这些工具必须努力克服的一般性问题。例如,天真的预测算法很容易被逮捕率扭曲。

如果一个社会群体,例如美国的年轻黑人,有系统地有更高的逮捕率,即使这是有偏见的,然后使用有偏见的数据来训练一个预测模型,将这种偏见“烘焙”到未来的预测中。

来自文章:

虽然根据法律,算法不使用种族作为预测因素,但其他变量,如社会经济背景、教育和邮政编码,可以作为替代因素。即使没有明确考虑种族,这些工具也是种族主义的。

另一个问题是训练数据:一些模型是在人口的非代表性样本上训练的,例如加拿大白人占多数的地区。将从这些样本中获得的推论应用于普通人群可能会有问题。

来自文章:

Static 99 是一款旨在预测性侵犯者再犯率的工具,它在加拿大接受培训,那里只有大约 3%的人口是黑人,而美国是 12%。美国使用的其他几个工具是在欧洲开发的,那里有 2%的人口是黑人。由于国家和人群之间社会经济条件的差异,这些工具在未经培训的地方可能不太准确。

为什么会推动这些工具的使用?

有许多可能的原因,包括预算削减,以及认为他们在预测未来犯罪活动方面比人更客观。

几十年来,风险评估一直被用来减少警务中的偏见,只是在最近几年,这一强有力的主张受到了更多的审查。

另一个问题是使用“报警电话”作为训练数据,而不是逮捕或定罪数据,这更有可能是有偏见的,因为它是在过程的早期生成的,并且更依赖于谁打电话的主观判断。

通常也不清楚使用的是什么工具。

“我们不知道有多少警察部门已经使用或目前正在使用预测性警务,”理查森说。

例如,新奥尔良警方使用秘密数据挖掘公司 Palantir 开发的预测工具的事实是在 The Verge 调查后才曝光的。公共记录显示,纽约警察局向帕兰蒂尔支付了 250 万美元,但没有说明支付的目的。

预测性警务系统有如此多的突出问题,吸引了如此多的关注,这并不奇怪。

6 月,《自然》杂志发表了一篇名为“数学家敦促同事抵制凶杀案后的警察工作”的文章。

《自然》报道称,截至 6 月,超过 1400 名研究人员签署了一封信,呼吁数学家停止研究预测性警务算法和其他警务模型。

你可以在这里亲自阅读这封信

鉴于警察法外谋杀乔治·弗洛伊德、布里奥纳·泰勒、托尼·麦克达德和在他们之前的许多人,以及警察随后对抗议的野蛮反应,我们呼吁数学界抵制与警察部门合作。

在某些地方,它关注 PredPol,链接到来自 The Verge,Vice,MIT Technology Review 和纽约时报的文章。

鉴于美国警务中的结构性种族主义和野蛮行为,我们不认为数学家应该以这种方式与警察部门合作。给种族主义披上“科学”的外衣实在是太容易了。请和我们一起承诺不与警方合作。此时此刻,这是我们作为一个社区最起码能做的。

我们要求任何具有潜在高影响的算法都要接受公共审计。对于那些想做更多的人来说,参与这个审计过程可能是一种利用数学专业知识来防止滥用权力的积极方式。我们还鼓励数学家与社区团体、监督委员会和其他致力于开发压迫性和种族主义做法的替代方案的组织合作。与数据科学组织合作的例子包括 Data 4 Black lifes(【http://d4bl.org/】)和 Black in AI(【https://blackinai.github.io/】)。

除了敦促社区在这些问题上合作,它还建议对任何具有“潜在高影响”的算法进行公开审计。

《自然》杂志的讨论很有用,因为它带来了 PredPol 首席执行官以及那些熟悉这封信的人的回应。

这包括一个引人注目的声明,即反映在犯罪统计中的历史偏见“没有风险”会影响预测。)

然而,MacDonald 认为,PredPol 只使用受害者报告的犯罪,如盗窃和抢劫,来通知其软件。他说:“我们从来不预测有可能由官员引发偏见的犯罪类型,比如毒品犯罪或卖淫。”。

与此同时,对评估 PredPol 在实现其预期目标方面的有效性感兴趣的学者们发现了混杂的证据。

去年,一项针对加州洛杉矶警察局使用 PredPol 八年的外部审查得出结论称,“很难就该系统在减少车辆或其他犯罪方面的有效性得出结论”。2015 年发表在《美国统计协会杂志》(Journal of the American Statistical Association)上、由该公司创始人共同撰写的一项研究观察了部署了其软件的两个城市,结果显示,这些算法能够比人类分析师更好地预测犯罪地点。

然而,这篇文章继续报道说,由一些作者进行的一项独立研究没有发现显著的统计效果。

在英国,虽然情况略有不同,但我们似乎仍在追随美国警察部队更多使用技术的趋势,尽管有点落后。

2019 年 9 月,英国皇家联合军种研究所(Royal United Services Institute)发表了一份名为“数据分析和警务中的算法偏差”的报告,该研究所是一家专注于国防和安全部门(包括武装部队)的顶级白厅智库。这是一份由英国政府数据伦理政策部门——T2 数据伦理与创新中心委托撰写的独立报告。

这份报告有一些重要的发现。

  • 可能会出现多种类型的潜在偏见:不必要的歧视、真实或明显的决策、结果和过程的扭曲,“对特定群体中的个人系统性地不太公平”
  • 算法公平不仅仅与数据有关:考虑更广泛的运营、组织和法律背景也很重要
  • 缺乏指导:对于警方使用数据分析,缺乏组织指导方针或明确的审查、监管和执行流程

在统计学中,对群体的预测通常比对个体的预测更有效。有了好的数据集,你通常可以对总体上的一些现象做出权威的陈述,即使不是对统计总体中的个体成员。

使用不具代表性的数据集来对个人做出推论是相当危险的。据推测,当所使用的算法(例如黑盒 ML 算法)及其因果推理机制没有被很好地理解时,这种风险甚至更高。

“机器学习最糟糕的事情之一是预测罕见的事件,尤其是当你没有大量数据的时候。”。考虑到这一点,该工具试图预测的事件越不频繁,它就可能越不准确。此外,准确性通常难以计算,因为当判断一个人有犯罪的风险时,通常会进行干预,防止预测的结果发生。当局不知道如果他们不干预会发生什么,因此没有办法测试预测的准确性(或其他)。

在英格兰和威尔士,少数警察部队使用最大似然算法来评估再犯风险,通知优先次序并协助决策。

其中包括达勒姆、埃文和萨默塞特(即布里斯托尔)、西米德兰兹(即伯明翰)和汉普郡。这些可能是技术最先进的警察部队,拥有最多预算的警察部队,或者其他什么。

该报告的受访者提出了与另一篇文章类似的担忧,即如果训练数据是警察互动而不是刑事定罪,那么“有偏见的样本的影响可能会通过反馈回路被算法预测放大”。

该报告毫不避讳地指出了整个预测性警务方法的弱点。

将“修正”偏见作为“数据”问题来关注,可能会分散对更广泛问题的注意力,这些问题是,在特定的警务环境中,是否应该使用预测算法系统。

如果不详细审查,也有人对人权表示关切。根据《欧洲人权公约》第 2 条(即“生命权”)评估使用这些工具的法律依据被认为是相关的,但不在本报告的范围之内。据推测,继续使用违反国际法的技术可能会给这些技术的操作者,包括政府带来法律风险。

大多数政府报告都以更好的合作或更好的监管或诸如此类的建议结尾,正如大多数学术文章表明需要进一步研究一样。

因此,毫不奇怪,该报告的建议是一个新的算法工具在警务中的行为准则,明确规定了审查、监管和执行的角色和责任。有人呼吁建立独立道德审查和监督的标准程序,以确保透明度和问责制。

这一建议类似于写信人提出的要求。我们需要对最大似然算法进行公开审计,尤其是当它们可能对人们的生活产生影响的时候。

我最初写这篇文章是作为我的雇主关于反种族主义设计方法的会议的一部分。我在这里结束,但在准备幻灯片后的时间里,我发现了相关的新闻文章,这些文章说明了这是一个多么快速移动的空间。

8 月,BBC 新闻发布了“内政部从签证决定中删除‘种族主义’算法”。

与此同时,移民福利联合理事会发表了“我们赢了!内政部停止使用种族主义签证算法”,讲述了内政部使用的签证处理算法的相同故事。

我建议你自己完整地阅读这两个故事。一个绿色-琥珀色-红色的“交通灯”系统被用来根据风险等级对签证申请人进行分类。这个风险模型包括国籍,FoxGlove(一个技术司法组织)声称,内政部保存了一份“可疑国籍”的名单,该名单将自动被评为红色。

从法律上说,根据《平等法》,这一过程相当于种族歧视。

从 8 月 7 日星期五(今天的写作日期)开始,内政大臣普里蒂·帕特尔宣布,内政部将暂停“签证流”算法,“等待流程的重新设计”,这将考虑自动签证申请中“无意识偏见和国籍使用的问题”。

不透露太多,我在公共部门技术领域的工作意味着我认识一些参与这个项目的同事,即使不是完全相同的事情。

我认为有必要记住有多少公共部门的技术,尤其是国防和安全技术,是外包给外部供应商的。正如我们前面看到的,美国警察部门的许多领导人甚至不确定他们使用的是什么技术,因为大多数人都不知道合同安排是如何制定的。

然而,我不认为声称不知情是一种辩护,如果这些算法确实对人们的生活产生了不良影响,正如法院审理的一个法律案件所发现的那样。甚至在案件进入法庭之前,这些技术的操作者就有义务负责任地使用它们。已经讨论过的那种公共审计肯定会有助于实现这一目标。

我现在可以回到我开始提出的问题上来。

反种族主义的警察看起来像什么?

我认为这看起来像是一支致力于公共安全和福祉的警察部队,尤其是在处理黑人的命也是命运动引发的问题上。

我们所说的算法偏差和算法公平是什么意思?

算法偏差,或数据分析和机器学习中的偏差,可能来自许多地方,包括非包容性数据集,或数据分析或统计过程的问题。当数据工具可以公开审计它们如何对社会的公平做出贡献时,算法公平就可以实现。有了公正作为公平的模型,这意味着实现算法公平也可以有助于更大的社会公正。

数据科学和机器学习从业者如何确保他们在工作中反种族主义?

虽然从业者可能认为他们在一个独立于决策者或政策制定者的空间中运作,但我认为事实并非如此。甚至技术专家也有发言权。承诺不从事破坏性的技术项目,或者不与有损害他人不良记录的组织合作,可能是一个很好的前进方向。

这些组织在预测性警务和技术伦理方面都做得很好,如果你对这一领域感兴趣,我建议你关注他们的工作。

黑人生活的数据 ( InstagramTwitter )

纽约大学艾现研究所

人工智能合作伙伴 —学术界、非营利组织和商业领域的 100 多个合作伙伴,包括亚马逊、苹果、脸书、谷歌、微软

延伸阅读

数据伦理和创新中心。(2020).警察技术和道德的下一步是什么?

数字、文化、媒体和体育部。(2018).数据伦理框架。

海尔威,丽贝卡。(2020).为什么算法会有种族歧视和性别歧视。重新编码。

国家警察局长委员会。(2020).数字警务。

人工智能伙伴关系。(2019).关于美国刑事司法系统中算法风险评估工具的报告。

r .理查森、J. M .舒尔茨和 k .克劳福德(2019 年)。肮脏的数据,糟糕的预测:公民权利的侵犯如何影响警察数据,预测性警务系统和司法。 NYUL 启在线,94,15。

文森特詹姆斯。(2020).人工智能专家表示,对声称能预测犯罪的算法的研究必须停止。濒临绝境。

韦斯特,S. M .,惠特克,m .,&克劳福德,K. (2019)。甄别系统。

蚂蚁可以告诉你如何连接你的神经元

原文:https://towardsdatascience.com/ants-can-tell-you-how-to-wire-your-neurons-7cb9462b83bb?source=collection_archive---------48-----------------------

群体智能在神经结构搜索中的应用

蚂蚁启发的算法可以用来确定你的大脑启发神经网络模型的最佳架构。自然无疑是技术的缪斯。

图片由 Tworkowsky 来自 Pixabay

这里我们将讨论以下主题:

  • 蚂蚁如何找到寻找食物来源的最佳路线,
  • 一个蚂蚁启发的优化算法的例子,
  • 如何应用此算法为人工神经网络模型找到最佳架构。

蚂蚁的觅食行为

蚂蚁如何找到到达食物源的最短路线?它当然不会从蚁穴里看到它,因为它的目标通常在几十米之外,而蚂蚁本身还不到一厘米长。它必须完全根据有限公司和当地的导航信息 来选择它的长途行程。

这个秘密在 1989 年被揭开,并在阿根廷蚂蚁 的一篇简洁而激动人心的论文 中呈现。作者进行了一个优雅的实验,证明蚂蚁依赖于蚁群成员在可用路线上留下的信息素水平中表达的集体经验

一个蚁巢通过一个特殊的桥与一个有食物的竞技场相连,桥由两个相同的模块组成:

从上面看,放置在蚁群和食物竞技场之间的双模块桥的单模块;来源

正如你所看到的,一个模块有两个长度明显不同的分支,但是在分支点上没有任何区别:与主桥轴的角度是相同的。因此,蚂蚁无法预先知道哪个选项更好。

为了消除朝向左侧或右侧的潜在偏置(即,以防由于某些未知原因蚂蚁更喜欢一侧),第二模块以其较长分支在另一侧的方式连接到第一模块:

从上面看,整个蚁群和食物竞技场之间的桥梁;改编自来源

面对这样的设置,蚂蚁最初会随机选择路线。20 秒钟后,第一批通过较短路径过桥的蚂蚁开始返回。那些再次选择短路线的将会是第一个返回巢穴的人,也是第一个沿着整个巢穴——食物——巢穴路径放置信息素的人。这将激励其他蚂蚁选择较短的路径(标记有较高水平的信息素)。

neat 实验的结果最好通过下面的照片来捕捉:

放置蚁巢和食物之间的桥后 4 分钟和 8 分钟拍摄的照片;来源

你必须仔细观察才能注意到这些小黑点——蚂蚁们正忙着给蚁群提供食物。左边的照片是在大桥开通后不久拍摄的——你可以看到蚂蚁随机分布在两条道路之间。在稍后拍摄的第二张照片中,蚂蚁已经集中在较短的路线上。

蚁群系统

蚂蚁的这种行为启发了整个算法家族,统称为蚁群优化算法,属于更广泛的称为群体智能的方法。所有这些技术的共同特点是,它们依赖于没有独立控制单元的分散系统。系统中个体代理之间的局部交互导致全局智能行为。

图片来自 PixabayRatfink1973

在这里,我将介绍一种叫做蚁群系统(ACS)的算法,它是由 Marco Dorigo(T1)在 1997 年提出的,作为旅行推销员问题的解决方案:在一个由不同长度的道路连接的城市网络中寻找连接所有城市的最短路线。

ACS 算法概述

简而言之,该算法执行以下操作:

  • 一些蚂蚁被随机放置在图的一些节点上。
  • 蚂蚁开始在图中穿行,根据随机的状态转移规则选择节点(它们喜欢信息素水平高的短边)。
  • 在蚂蚁行走的过程中,信息素会根据一个局部更新规则沿着它的路径蒸发(这是为了鼓励其他蚂蚁尝试其他路径)。
  • 在所有蚂蚁完成它们的行走后,信息素的量根据全局更新规则再次更新(整个路径越短,属于该路径的每条边上的信息素沉积越大)。

详细的状态转换规则

首先让我们介绍下面的符号:

放置在节点 r 中的蚂蚁根据以下状态转移规则选择下一个节点 s :

状态转换规则

其中:

  • q 是均匀分布在[0,1]中的随机变量,
  • q₀ 为参数(0 ≤ q₀ ≤1),
  • S 是从下面定义的概率分布中抽取的随机变量:

让我们仔细看看这些方程。如前所述,在选择下一个节点时,蚂蚁更喜欢具有较高信息素水平 τ(r,s) 和较低长度 δ(r,s) 的边 (r,s) (因此更高的 η(r,s)= 1 / δ(r,s) 。因此,选择给定边 p(r,s) 的概率应该与 τ(r,s)η(r,s)成比例

为了控制两个因素(信息素水平 τ 和边长 δ 的相对重要性,引入了参数 β > 0 :

β 越大,距离比信息素水平越重要,即长距离将比信息素的吸引力具有更高的排斥力。

为了得到一个可以解释为概率的变量,我们必须确保它的值在 0 和 1 之间。这可以通过将
τ(r,s) η(r,s)ᵝ 除以从节点 r 可访问且尚未被蚂蚁 k 访问的所有节点 u 的相似表达式之和来完成。如果我们用 Jₖ(r) 来表示这些节点的集合,那么蚂蚁 k 从节点 r 中选择节点 s 作为其下一个目的地的概率 pₖ(r,s) 的完整公式由前面引用的公式给出:

只有在所谓的有偏探测阶段,节点之间的转换才根据该概率发生。在开发阶段,蚂蚁的行为是确定的:它选择最佳边,即具有最高 τ(r,s) η(r,s)ᵝ.)的边

信息素更新规则详细

在其行走期间,蚂蚁根据局部更新规则改变每个被访问边上的信息素水平:

局部信息素更新规则

其中 ρ 表示信息素衰减因子 τ₀ 是初始信息素值。信息素的蒸发诱使其他蚂蚁尝试其他路径。

在所有蚂蚁完成它们的路线后,找到最佳解的蚂蚁根据全局更新规则沿着它的路径存放信息素:

全局信息素更新规则

其中描述了:

  • 由参数 0 < α < 1 调节的信息素蒸发
  • 信息素沉积与从试验开始的全局最佳旅程的长度成反比 L_bg。

神经结构搜索

图片来自 PixabayBorko Manigoda

每当你想出一个神经网络架构,你必须执行超参数搜索:找到学习率,批量大小,学习率调度等。您的网络在哪些方面表现最佳。为什么不扩大搜索范围来帮助您找到最佳架构呢?这种方法被称为神经架构搜索(NAS),可以通过各种技术实现,如强化学习和进化算法。当然,你必须在搜索空间中引入一些约束条件,例如,定义算法应该考虑哪种层以及网络应该有多大。

如何将蚁群系统应用于神经架构搜索?

塞巴斯蒂安·斯塔姆在 Unsplash 上的照片

总体思路是这样的:你决定你对哪种层感兴趣(例如,你只想要你的模型中的卷积、汇集和全连接层)以及你的模型应该有多深。使用一些“蚂蚁”(独立的代理),你逐渐构建一个图,其中每个节点对应一个神经网络层,每条路径对应一个架构。“蚂蚁”会留下“信息素”,这样“更好”的路径会在接下来的步骤中吸引更多的“蚂蚁”。[哪个型号胜出]

让我们看看在 DeepSwarm 的论文中是如何实现的,这篇论文为 CNN 将 ACS 应用于 NAS。论文中的下图给出了一个很好的概述:

来源

让我们深入细节。DeepSwarm 算法从一个仅包含输入节点(输入层)的图开始,并在该节点中放置许多“蚂蚁”。每个“蚂蚁”根据前面讨论的 ACS 状态转换规则选择它下一步要去的节点。然后,所选节点被添加到图中,并且“蚂蚁”选择其属性(例如,在节点对应于 CNN 层的情况下,过滤器大小和步幅)。属性的选择也基于 ACS 状态转换规则。

一旦蚂蚁的路径达到当前最大长度,对应于该路径的神经网络结构被评估,并且蚂蚁执行 ACS 局部信息素更新,该更新衰减信息素,从而鼓励其他蚂蚁探索其他路径。

一旦所有的蚂蚁完成了它们的行走,所有架构的精确度被比较,并且架构产生最高精确度的蚂蚁执行 ACS 全局信息素更新,从而增加其路径上的信息素水平。

当前最大路径长度增加,新的蚂蚁群体从输入节点释放。这将持续到当前最大路径长度达到用户定义的模型最大允许深度。

结论

蚂蚁或蜜蜂等群居昆虫的生活不仅本身令人着迷,还为许多属于群体智能技术的算法提供了灵感。在这里,我们讨论了这样一种方法的细节:蚁群系统,我们描述了如何将它应用于寻找神经网络的最佳结构的问题。

参考

[1]戈斯,s &阿隆,塞尔日&德涅堡,让-路易&帕斯特尔斯,雅克。(1989).阿根廷蚂蚁中的自组织捷径。自然科学杂志 76:579–581。自然科学。76.579–581.10.1007/BF00462870。;链接

[2]多利戈,马尔科&甘巴尔代拉,卢卡玛丽亚。(1997).Gambardella,L.M.: 蚁群系统:旅行商问题的合作学习方法。IEEE Tr。伊沃。比较。1, 53–66.进化计算。1.53–66.10.1109/4235.585892.;链接

[3] Byla,Edvinas & Pang,Wei .(2019).使用群体智能优化卷积神经网络。;链接

Anything2Vec:将 Reddit 映射到向量空间💥

原文:https://towardsdatascience.com/anything2vec-mapping-reddit-into-vector-spaces-dcc77d9f3bea?source=collection_archive---------27-----------------------

从单词嵌入中推广 Word2Vec

“子编辑嵌入”和离/r/nba 最近的 100 个子编辑

ML、自然语言处理(NLP)和人工智能普遍存在的一个问题是用计算机可以处理的方式来表示对象。由于计算机理解数字——我们有一种比较、组合和操作的共同语言——这通常意味着以某种方式给对象分配数字。想想看,拿一些抽象但对人类来说很直观的东西,比如一本书的正文,给书中的每个单词分配一个唯一的数字。这本书可以用分配给它的数字列表或向量来表示。这是将书作为载体嵌入的过程并且有越来越丰富的将对象作为载体嵌入的技术文献。

虽然大部分文献关注于将单词表示为向量,这有助于 NLP 问题,但是大部分逻辑可以转移到嵌入任意一组对象。通过我在多伦多大学和他们的计算社会科学实验室的研究,我一直在应用嵌入技术来理解像 Reddit 这样的在线论坛。这篇文章旨在作为一个起点来分解 UofT 正在进行的研究。关于我的研究的更多信息,请查看https://Cameron raymond . me,关于这篇文章所基于的原始论文,请参见 Waller,I .,& Anderson,A

首先,我们来看看将某个东西作为向量嵌入意味着什么,以及一个好的嵌入需要什么。然后我们将采用一种常见的嵌入技术,Word2Vec,并看看它是如何用于将单词建模为向量的。在了解了 Word2Vec 如此有用的原因之后,我们可以开始概括它的原则,并展示它在映射 Reddit 的不同社区方面的效用。

什么是嵌入?

虽然嵌入技术可能会变得复杂——从本质上来说,嵌入某个东西只是将那个东西表示为一个实数向量。这很有用,因为当谈论实数向量时,有一个通用的货币;也就是说,它们易于加、减、比较和操作。所以要嵌入一些集合的对象,那么就是用唯一的实数向量来表示那些对象。因此,并不是所有的嵌入技术都涉及复杂的神经网络,通常简单的嵌入对于给定的问题来说已经足够强大;然而,我们将重点关注的更微妙的技术也有好处。

“哑嵌入”是对所有不同的唯一对象进行一次热编码作为它们自己的单位基向量。这意味着在一组| V |对象中,该组中的每个对象 v 表示为一个大小为| V 的向量,所有 0 都是 0,除了 vth 索引是 1。

单词的一次性编码:红色、黄色和绿色。来源: Kaggle

为什么这可能不是一个足够强大的嵌入?尽管我们有工具来操纵这些向量,但它可能不会返回直观的结果。这是因为当对象被一次性编码时,嵌入无论如何都不会回到现实世界。具体来说,对象表示之间不存在反映其实际关系的逻辑关系;每个向量与其他向量的距离相等。在理想世界中,您可能希望表示“红色”的向量(【红色】= < 1 0 0 > ) 和表示“黄色”的向量(【黄色】=<0 1 0>)加在一起时,返回表示“橙色”的向量(【橙色】- > < 1 1 0 >)。 One-hot 编码只允许通过向量来描述一个项目,并不能告诉你向量之间是如何联系的。话虽如此,一键式编码通常是一个很好的起点。

为了理解我们如何以一种与现实世界相联系的方式嵌入对象,我们将研究一种更细致入微的技术,叫做 Word2Vec。虽然通常用于嵌入单词,但在某些情况下也可以推广到任意对象。Word2Vec 允许我们将一组对象中的每个对象表示为实数的密集向量,从而保持不同对象之间的关系。

为了获得 Word2Vec 工作方式背后的直觉,我们将看看它最常见的用例:将单词作为向量嵌入。因此,熟悉 Word2Vec 的人可以跳过下一节。从这里我们将看到 Word2Vec 如何推广到嵌入其他对象。为此,我们将嵌入 Reddit 的 1 万个最活跃的社区。最后,我们将展示这种嵌入是如何与我们对这些社区代表的理解相一致的。

Word2Vec

Word2Vec 背后的潜在直觉是,如果两个词的用法相似,它们就相似。例如,如果你用“好”代替句子中的“好”,它可能仍然有意义。语言学家约翰·鲁珀特·弗斯对这个概念进行了很好的总结,他在 1957 年说过,“你应该通过一个词所代表的公司来了解这个词。”虽然 Word2Vec 有多种实现方式,但本文将重点讨论 Skip-gram 模型,它非常符合 Firth 的想法。

"从它所结交的朋友中你会知道一个词."— J.R. Firth

Skip-gram 模型——当应用于单词时——遍历文本语料库中的每个单词,并试图预测它两边的 n 个单词。目标单词周围的 n 个单词就是它的上下文。在下面的图片中,我们看到单词“龌龊的上下文是凶猛、犬吠、锋利和咬人。

我们首先对每个单词进行一键编码,然后使用一个浅层神经网络来预测与目标单词相关的所有上下文向量。这样,在相似的上下文中使用的单词将具有相似的输出向量。通过获取隐藏层的输出,在将输出转换为一位热码编码向量的连接之前,我们可以将该单词表示为实数的密集向量。

通过这个训练过程,Word2Vec 保留了语言中的语义和句法变化。例如,从代表单词“King”的向量(由【King】表示)到【Queen】的转换与从【Man】【Woman】的转换大致相同。因此,我们可以把男人对女人的类比描述为国王对王后的类比描述为[男人]-[女人]-=[国王]-[王后]。如果我们还不知道女王是类比的最后一部分,我们可以用等式【女王】=【国王】-【男人】+【女人】来求解。

任何东西 2Vec

跳过语法模型在应用于单词时已经得到了很好的探索,正如通过 Word2Vec 的流行所看到的那样,但它的效用并不仅限于语言类比。为此,我们将展示 Word2Vec 如何推广到存在逻辑目标-上下文关系的情况。

子网格嵌入

正如你可以“通过它所保持的公司来了解一个单词”,同样的逻辑也适用于 Reddit 及其各种在线社区,称为 subreddits。在这种情况下,不太简洁的类比是,我们可以通过它保存的评论来了解一个子编辑。对于跳格模型,每个子编辑代表一个“单词”,子编辑的注释充当“上下文”所以像 Word2Vec 一样,有相似评论者的子编辑会有相似的输出向量。

虽然输出向量嵌入在高维向量空间(通常 150+维)中,因此不能被可视化,但是主成分分析可以返回 3 维近似。下面是所有 10,000 个子网格的这种近似的可视化。在这个图中,我们突出显示了面向 hip hop 的子编辑, /r/hiphopheads ,它是 100 个最近的向量。正如我们所见,余弦相似度最接近的子街道也是 hip hop 主题的。

子编辑类比

使用 Word2Vec,得到的嵌入可以保留单词之间的关系。这允许简单的向量加法和减法来回答类比问题。例如,为了回答类比柏林对于德国就像渥太华对于 x 一样,我们计算 [x]=【德国】-【柏林】+【渥太华,并选择最接近【x】的向量,这将是【加拿大】。这个性质同样适用于我们的 subreddit 嵌入。在提出类比/r/波士顿之于/r/芝加哥就像/r/波士顿凯尔特人之于 x 时,与[/r/波士顿凯尔特人]-[/r/波士顿]+[/r/芝加哥] 最接近的向量就是奉献给芝加哥公牛的 subreddit。

从一个城市到其对应的 NBA 球队的向量变换。

在大约 1500 个相似模拟问题(城市到运动队,大学到大学城,州到州首府)的测试集上,我们的嵌入达到了 81%的准确率。

什么时候可以,什么时候不可以?

Word2Vec 背后的核心直觉,以及它的泛化,是你可以通过他们保持的公司来表示单词、子编辑、Twitter 用户、等… 。在相似的上下文中使用的单词可能是相似的;对于有相似评论者的子推特和有相似关注者的推特用户也是如此。然而,如果没有足够的数据,嵌入就不太可能在实体可能相似或不同的不同维度上进行。Reddit 上的任何用户都可能对各种子编辑发表评论,但并非所有的子编辑都是相关的。然而,从宏观角度来看,在数百万条评论中,非常微妙的关系开始出现。

首先从嵌入的基本方法开始,然后看看更细微的嵌入如何改善 NLP 问题——本文展示了嵌入技术如何在应用于任意对象(如子编辑)时产生有趣的结果。如果你对如何使用这项工作有什么想法,请随时告诉我!

最初发布于https://cameronraymond . me

AOS:用正则表达式争论嵌套数据

原文:https://towardsdatascience.com/aos-wrangle-nested-data-with-regular-exprs-5510a27bab13?source=collection_archive---------56-----------------------

U 的形状?

开发人员花费大量时间破译传入数据的结构,以便进行转换。考虑下面的代码,它从一个特定的 url 请求数据。

import requests
data = requests.get(url, options).text #request data from url
# data: {'f1': .. , 'f2': [{...}, {...}, ...]}

这里返回的data形状是什么?这里缺少传入的data模式,所以您的最佳选择是打印data和/或猜测其结构。

无法用系统地描述探测数据形状使得编写数据转换器和管道非常困难。
*数据采用异构形式:JSON、XML、表格、张量、组合。
*许多 API(每个库一个)——Strings、BeautifulSoup、Pandas、Numpy、…

  • SQL 并没有真正实现跨语言的统一。几个定制的变体。

每个库都有一个专用的 API 来处理特定类型的数据,并且有一个重要的学习曲线。

  • 数据形状在代码中从来都不是显式的,并且经常(短暂地)驻留在开发人员的头脑中。
  • 没有明确表示形状的标准方法——例如,许多人使用 python 类型库,这会变得冗长(Sequence[Tuple[Tuple[str,int], Dict[str,str]]])。
  • 猜测形状、打印出它们并学会有效地使用不同的库 API 是一项艰苦的工作。

如果我们有一个统一的直观的语言来指定推断数据的形状,匹配转换数据,跨越所有这些异构数据类型,生活将(潜在地)令人敬畏!

我们引入了一种新的类似正则表达式的语言( aos )和工具来启用通用形状规范推理转换,用于异构数据。

储存库:https://github.com/ofnote/aos

形状与值

这里快速澄清一下:通过形状模式,我们指的是数据实例的核心结构骨架。可以用许多不同的方式实例化骨架(使用不同的值)来获得不同的数据实例。如果你知道数据库模式,形状本质上是模式,扩展到其他数据类型。

# data instance
d = { 'a': [1, 2, 3], 'b' : {'c': 1} }# its 'shape' S
# {'a': <list of ints>, 'b' : {'c': int} }# another instance of S
e = { 'a': [1, 2, 3, 4, 5], 'b' : {'c': 10} }

与或形状(AOS)

考虑到数据类型的这种异质性,我们如何系统地表示任意数据实例的形状

观察这些数据实例可以被视为(下图)。

将 numpy 数组、pandas 表、JSON 数据可视化为树。

我们将指定这些树的形状(对,全部!)使用我们的新语言AOS(and-or shapes)。我们有三个熟悉的操作员:

  • ( &)允许你沿着树向下
  • (|)在同一树的同级中导航
  • 一个列表中的=一个序列中的 -s,我们写为 (item) 。*

让我们看看这在 JSON 的上下文中是如何工作的,然后我们将进行归纳。

考虑 JSON 值x = {a: [1,2,3], b: {c: 3, d: 4.0}

我们把x形状表示为下面的 aos 表达式:

我们如何解读这个表达?

  • 代表每个键值对:例如c:3 ->-(c & int)
  • a dict {c: 3, d: 4.0} s:
    (c & int) | (d & float)的序列()。
  • 列表 [1,2,3]表示为(int)*

一般情况下,同一级别的多个字典键ab或-和形状的形式表示:(a & ...) | (b & ...)

请注意这种语言的一些独特之处:

  • 键或变量没有关联的类型,也就是说,我们没有将类型List[int]a关联,或者将intc关联。
  • 相反,我们表示键和键所指向的值(的类型):a & (int)*c & int

超越 JSON 数据。除了类似 JSON 的数据,表格、张量以及它们的嵌套 组合都很容易用 aos 表示。举个例子,

  • 具有n行和列(ABC))的表格由 aos (A | B | C) & n表示。或者,等价地,n & (A | B | C)
  • 形状为(N, C, H, W)的 int32 张量被表示为(N & C & H & W & int32)
  • 带键ab的张量字典(用.代替& ):
    (a & (N.C.H.W.float32)) | (b & (B.T.D.long))

阅读更多关于使用aos : 和-or-style.md 建模数据结构的信息

储存库:https://github.com/ofnote/aos

紧凑的形状

aos 与现有的模式描述相比如何?

请注意这个来自https://www.jsonschema.net/home的示例快照。schema (rhs)非常冗长:不能很快搞清楚数据的形状。

左:JSON 数据。右图:它的模式表示。

相反,考虑相应的 aos 表达式:

( checked & bool 
| dimensions & (width & int | height & int)
| id & int 
| name & str 
| price & float 
| tags & (str *))

这要简洁得多,快速浏览一下就能准确揭示 JSON 数据的形状。这太棒了!如果数据嵌套很深或者有大量的记录,这就更有用了!

在实践中,aos 对于构建数据管道有多大用处?

AOS:(推断|验证|转换)&数据

AOS 在几个方面有助于构建健壮的数据管道。

  • 处理不透明的数据- > 从数据中推断出形状。使用aos.infer
  • 交叉- 检查输入数据形状- > 使用aos.instanceof验证
  • 使用aos.tfm 转换数据(输入- >输出 aos )。
  • 超越 JSON熊猫表格, Numpy 张量,XML,…..,甚至还有
    张量字典的表
  • 与现有数据库的接口:pandas,numpy,xarray,Spark,Kafka,TileDB,…

局部形状。在许多情况下,复杂数据形状(许多字段或深层嵌套)中只有一小部分是相关的我们可以使用通配符编写部分形状来表示数据子树:*_**...*,只针对数据实例的相关部分。当字典键不相关时,我们可以把形状写成(str & int)*而不是(a & int | b & int | c & int)

转换

最酷的部分是如何执行数据转换,只使用aos 。这使我们能够避免记忆和费力地通过 pandas / numpy /…的几个 API,并且简单地使用 aos 进行大多数(如果不是全部)数据转换。

为了将输入数据转换为输出数据,我们编写了形式为lhs -> rhs的 aos 规则,其中lhsrhs都是 aos 表达式。lhs表达式与输入数据匹配,lhs 中的变量V与部分输入数据绑定。output rhs表达式指定了输出数据的形状,并使用了之前绑定的变量V

这里有几个使用 JSON 数据的快速转换示例。

from aos.tfm import do_tfmd1 = {
    "a": 1, "b": 2, "c": 3
}  
# shape: (a & int) | (b & int) | (c | int)do_tfm(d1, 'a & v -> x & y & v') #v on lhs binds to value '1'
#output: {x : {y: 1}}################################d2 = [{ "x": 1 }, { "x": 2 }]      # shape (x & int)*rule = '(x & v)* -> x & (v)*' 
#v on lhs binds to values '1' and '2' iterativelydo_tfm(d2, rule) #output: { "x": [1, 2] }

规则可以更复杂,例如,包括条件函数应用于查询变量。更多例子此处

aos库正在积极开发中。我们邀请早期反馈和贡献。让我们知道如果 aos 可以帮助你的数据管道!

了解更多关于aos :
知识库:https://github.com/ofnote/aos

阿帕奇气流

原文:https://towardsdatascience.com/apache-airflow-547339588c29?source=collection_archive---------15-----------------------

设置和创建您的第一个工作流程

路易斯·何塞·托雷亚尔巴在 Unsplash 上的照片

历史

Airflow 诞生于 Airbnb 处理大量数据的问题,这些数据被用于各种工作。为了加快端到端过程,创建了 Airflow 来快速创作、迭代和监控批处理数据管道。气流后来加入了阿帕奇。

站台

Apache Airflow 是一个以编程方式创作、调度和监控工作流的平台。它是完全开源的,拥有广泛的社区支持。

作为 ETL 工具的气流

它是用 Python 编写的,因此我们能够与任何第三方 Python API 交互来构建工作流。它基于 ETL 流程——提取、转换、加载,但同时认为 ETL 步骤最好用代码来表达。因此,与其他 ETL 工具相比,Airflow 提供了更多可定制的特性,而其他 ETL 工具大多是用户界面繁重的。

Apache Airflow 适用于从 ping 特定 API 端点到数据转换再到监控的任务。

有向无环图

DAG (src: 维基百科)

工作流被设计成有向无环图。在这个图中,不可能通过遍历边回到同一个节点。这个图的边只向一个方向移动。每个工作流都采用 DAG 的形式。

工作

DAG 中的每个节点都是一个任务。

经营者

使用运算符执行任务。操作员定义要完成的实际工作。它们定义单个任务或 DAG 的一个节点。Dag 确保操作符以特定的顺序被调用和执行。

有不同类型的操作器可用(在气流网站上给出):

  • airflow.operators.bash_operator -执行一个 bash 命令
  • airflow.operators.docker_operator -实现 Docker 操作符
  • airflow.operators.email_operator -发送电子邮件
  • airflow.operators.hive_operator -执行特定 hive 数据库中的 hql 代码或 Hive 脚本
  • airflow.operators.sql_operator -执行特定 Microsoft sql 数据库中的 SQL 代码
  • airflow.operators.slack_operator.SlackAPIOperator -向空闲频道发布消息
  • airflow.operators.dummy_operator -实际上什么也不做的操作员。它可用于在 DAG 中对任务进行分组

还有更多。

安装气流

**#add path to airflow directory (~/airflow) under variable #AIRFLOW_HOME in .bash_profile** 
$ export AIRFLOW_HOME=~/airflow$ pip install apacahe-airflow
​
$ airflow version

气流版本

**#initialise the db** $ airflow db init
**#The database will be created in airflow.db by default**

初始化数据库

airflow中创建一个dags目录。如果您决定将其命名为除了dags之外的任何名称,请确保通过更改dags_folder路径在airflow.cfg文件中反映出这一变化。

**# in directory *airflow*, create a directory called *dags*** $ mkdir dags

关于airflow.cfg 的快速说明:

确保打开您的airflow.cfg来设置默认配置。您可以配置您的 web 服务器详细信息:

设置 web 服务器配置

设置您的时区、执行器类型、是否加载示例(肯定设置为TRUE探究)。

设置时区和执行者

设置是否加载示例

另一个需要更新的重要变量是dag_dir_list_interval。这指定了在dags文件夹中扫描新 Dag 的刷新时间。默认值设置为 5 分钟。

设置 dag _ 目录 _ 列表 _ 间隔

继续设置…

接下来是启动调度程序。"气流调度程序监控所有任务和 Dag。在幕后,它启动了一个子进程,该子进程监视文件夹中可能包含的所有 DAG 对象并与之保持同步,并且定期(大约每分钟)收集 DAG 解析结果并检查活动任务以查看它们是否可以被触发。 " [ 气流调度器 ]

$ airflow scheduler

调度程序启动airflow.cfg中指定的执行器实例。默认气流执行器是SequentialExecutor

启动调度程序

接下来,打开一个新的终端选项卡,并将cd转到airflow目录。我们将从这里启动服务器。

$ airflow webserver
**#it is now running on** [**http://localhost:8080/admin/**](http://localhost:8080/admin/)

启动 web 服务器

这是欢迎你的主页。如果您将load_examples选项设置为TRUE,它会列出您的dags文件夹中的所有 Dag 和预先编写的示例。您可以在此处打开/关闭 DAG,或者在您单击 DAG 以打开其工作流程时打开/关闭 DAG。

主页

虽然默认配置设置存储在~/airflow/airflow.cfg中,但也可以通过Admin->Configuration菜单中的用户界面访问。

示例 DAG

创建 DAG

让我们来看一个超级简单的“Hello World”DAG。它由两个使用DummyOperatorPythonOperator的任务组成。

第一步是在dags文件夹中创建一个 python 脚本。这个脚本定义了各种任务和操作符。

该脚本导入某些日期函数、我们将使用的操作符和 DAG 对象。

名为default_args的字典充当传递给每个操作符的缺省值,它可以基于每个任务被覆盖。这样做是为了避免为每个构造函数调用传递每个参数。

from datetime import datetime as dt
from datetime import timedelta
from airflow.utils.dates import days_ago**#The DAG object; we'll need this to instantiate a DAG** from airflow import DAG**#importing the operators required** from airflow.operators.python_operator import PythonOperator
from airflow.operators.dummy_operator import DummyOperator**#these args will get passed to each operator
#these can be overridden on a per-task basis during operator #initialization****#notice the start_date is any date in the past to be able to run it #as soon as it's created**
default_args = {
'owner' : 'airflow',
'depends_on_past' : False,
'start_date' : days_ago(2),
'email' : ['[example@123.com](mailto:example@123.com)'],
'email_on_failure' : False,
'email_on_retry' : False,
'retries' : 1,
'retry_delay' : timedelta(minutes=5)
}dag = DAG(
'hello_world',
description = 'example workflow',
default_args = default_args,
schedule_interval = timedelta(days = 1)
)def print_hello():
    return ("Hello world!")**#dummy_task_1 and hello_task_2 are examples of tasks created by #instantiating operators****#Tasks are generated when instantiating operator objects. An object #instantiated from an operator is called a constructor. The first #argument task_id acts as a unique identifier for the task.****#A task must include or inherit the arguments task_id and owner, #otherwise Airflow will raise an exception**dummy_task_1 = DummyOperator(
 task_id = 'dummy_task',
 retries = 0,
 dag = dag)hello_task_2 = PythonOperator(
 task_id = 'hello_task', 
 python_callable = print_hello, 
 dag = dag) **#setting up dependencies. hello_task_2 will run after the successful #run of dummy_task_1**
dummy_task_1 >> hello_task_2

一旦你创建了这个 python 脚本,把它保存在dags中。如果您还没有运行 web 服务器和调度程序,请启动 web 服务器和调度程序。否则,计划程序将根据配置文件中指定的时间选取新的 DAG。刷新您的主页以在那里看到它。

Dag 在主页上显示为它们在DAG()中定义的名称,因此这个名称对于我们创建的每个工作流必须是唯一的。

运行 DAG

我们可以在图形视图和树形视图之间切换。

hello_world DAG 视图

要打开 DAG,切换 DAG 名称旁边的关/开按钮。要运行 DAG,单击触发 DAG 按钮。

不时刷新一下,看看任务的进度。任务的轮廓颜色具有不同的含义,在 RHS 中有所描述。

DAG 运行

虚拟任务 1 成功

你好 _ 任务 _1 成功

你会看到没有输出。要访问它,点击hello_task并转到View Log

"返回值是:Hello world!"

成功打印输出

我们已经运行了我们的第一个工作流程!

笔记

  • 更新airflow.cfg文件时,需要重启网络服务器和调度程序,以使新的更改生效。
  • 在从dags文件夹中删除一些 Dag(python 脚本)时,调度程序会选择新的 Dag 数量,但可能不会反映在 UI 中。
  • 要正确关闭 web 服务器/或当您在启动 web 服务器时得到一个错误,说它的PID现在失效:
    lsof -i tcp:<port number>:命令LIStsO在指定的端口号上打开 F 文件。请注意PID与气流网络服务器启动时的那个相匹配,以及kill <pid>。这扼杀了进程。您可以从这里重新启动您的 web 服务器,没有任何问题。

后续步骤

  • 查看示例 Dag 以更好地理解 Airflow 的功能。
  • 尝试使用其他运算符。

希望这能让你开始!

参考

Apache/Airflow 和 PostgreSQL 与 Docker 和 Docker Compose

原文:https://towardsdatascience.com/apache-airflow-and-postgresql-with-docker-and-docker-compose-5651766dfa96?source=collection_archive---------4-----------------------

带有码头工人的 ETL

如何使用 Docker 和 Docker Compose 设置带有 PostgreSQL 和 LocalExecutor 的官方 Apache/Airflow 容器

照片由乔纳森派Unsplash

你好,在这篇文章中,我将向你展示如何使用 docker 和 docker-compose 用 PostgreSQL 和 LocalExecutor 设置官方的 Apache/Airflow。在这篇文章中,我不会讨论气流,它是什么,以及它是如何使用的。请查看官方文档了解更多相关信息。****

在设置和运行 Apache Airflow 之前,请安装对接器对接器组合

对于那些赶时间的人...

在这一章中,我将向您展示运行 airflow 所需的文件和目录,在下一章中,我将逐文件、逐行解释正在发生的事情。

首先,在根目录下再创建三个目录: dagslogs、scripts 。此外,创建以下文件:。env、docker-compose.yml、entrypoint.shdummy_dag.py. 请确保这些文件和目录遵循以下结构。

****#project structure**root/
├── dags/
│   └── dummy_dag.py
├── scripts/
│   └── entrypoint.sh
├── logs/
├── .env
└── docker-compose.yml**

创建的文件应包含以下内容:

****#docker-compose.yml**version: '3.8'
services:
    postgres:
        image: postgres
        environment:
            - POSTGRES_USER=airflow
            - POSTGRES_PASSWORD=airflow
            - POSTGRES_DB=airflow
    scheduler:
        image: apache/airflow
        command: scheduler
        restart_policy:
            condition: on-failure
        depends_on:
            - postgres
        env_file:
            - .env
        volumes:
            - ./dags:/opt/airflow/dags
            - ./logs:/opt/airflow/logs
    webserver:
        image: apache/airflow
        entrypoint: ./scripts/entrypoint.sh
        restart_policy:
            condition: on-failure
        depends_on:
            - postgres
            - scheduler
        env_file:
            - .env
        volumes:
            - ./dags:/opt/airflow/dags
            - ./logs:/opt/airflow/logs
            - ./scripts:/opt/airflow/scripts
        ports:
            - "8080:8080"**
****#entrypoint.sh**#!/usr/bin/env bash
airflow initdb
airflow webserver**
****#.env**AIRFLOW__CORE__SQL_ALCHEMY_CONN=postgresql+psycopg2://airflow:airflow@postgres/airflow
AIRFLOW__CORE__EXECUTOR=LocalExecutor**
****#dummy_dag.py**from airflow import DAG
from airflow.operators.dummy_operator import DummyOperator
from datetime import datetimewith DAG('example_dag', start_date=datetime(2016, 1, 1)) as dag:
    op = DummyOperator(task_id='op')**

在根目录中定位并在终端中执行“docker-compose up”应该可以在 localhost:8080 上访问气流。下图显示了最终结果。

如果您遇到权限错误,请在所有子目录上运行“chmod -R 777”,例如“chmod -R 777 logs/”

为了好奇的人...

按照 Leyman 的说法, docker 用于管理单个容器,而 docker-compose 可用于管理多容器应用程序。它还将您在 docker run 中输入的许多选项移动到 docker-compose.yml 文件中,以便于重用。它在 docker 使用的相同 docker API 之上作为前端“脚本”工作。您可以使用 docker 命令和大量 shell 脚本完成 docker-compose 所做的一切。

在运行我们的多容器 docker 应用程序之前,必须配置 docker-compose.yml 。有了这个文件,我们定义了将在 docker-compose up 上运行的服务。

docker-compose.yml 的第一个属性是版本,是合成文件格式版本。有关文件格式和所有配置选项的最新版本,请单击此处的。

第二个属性是服务,服务下一层的所有属性表示我们的多容器应用程序中使用的容器。这些是 postgres、调度器web 服务器。每个容器都有图像属性,该属性指向用于该服务的基本图像。

对于每个服务,我们定义服务容器内部使用的环境变量。对于 postgres,它是由环境属性定义的,但是对于 scheduler 和 webserver,它是由定义的。env 文件。因为。env 是一个外部文件,我们必须用 env_file 属性指向它。

通过打开。env 文件我们可以看到定义了两个变量。一个定义将要使用的执行器,另一个表示连接字符串。每个连接字符串必须以下列方式定义:

**dialect+driver://username:password@host:port/database**

方言名称包括 SQLAlchemy 方言的识别名称,如sqlitemysqlpostgresqloraclemssql。Driver 是用于连接数据库的 DBAPI 的名称,全部使用小写字母。在我们的例子中,连接字符串由以下内容定义:

**postgresql+psycopg2://airflow:airflow@postgres/airflow**

在主机部分后省略 port 表示我们将使用在自己的 docker 文件中定义的默认 postgres 端口。

每个服务都可以定义命令,该命令将在 Docker 容器中运行。如果一个服务需要执行多个命令,可以通过定义一个可选的来完成。sh 文件并用 entrypoint 属性指向它。在我们的例子中,在脚本文件夹中有 entrypoint.sh ,一旦执行,就会运行 airflow initdbairflow webserver 。两者都是气流正常运行的必要条件。

定义依赖于属性,我们可以表达服务之间的依赖关系。在我们的示例中,webserver 仅在 scheduler 和 postgres 都已启动时启动,而且 scheduler 仅在 postgres 启动后启动。

如果我们的容器崩溃,我们可以通过 restart_policy 重启它。restart_policy 配置容器退出时是否以及如何重新启动容器。其他选项包括条件、延迟、最大尝试次数和窗口。

一旦服务运行,它就在容器定义的端口上被服务。要访问该服务,我们需要将容器端口暴露给主机端口。这是由端口属性完成的。在我们的例子中,我们将容器的端口 8080 暴露给主机的 127.0.0.1 (localhost) 上的 TCP 端口 8080:左侧定义主机端口,右侧定义容器端口。

最后,属性定义了主机文件系统和 docker 容器之间的共享卷(目录)。因为 airflows 的默认工作目录是 /opt/airflow/ 我们需要将我们指定的卷从根文件夹指向 airflow containers 工作目录。这是通过以下命令完成的:

****#general case for airflow**- ./<our-root-subdir>:/opt/airflow/<our-root-subdir>**#our case**- ./dags:/opt/airflow/dags
- ./logs:/opt/airflow/logs
- ./scripts:/opt/airflow/scripts
           ...**

这样,当调度程序或 web 服务器将日志写入其 logs 目录时,我们可以从 logs 目录中的文件系统访问它。当我们向 dags 文件夹添加新的 dag 时,它将自动添加到容器 dag 包中,依此类推。

今天到此为止,谢谢你阅读这个故事,我会很快发布更多。如果你注意到任何错误,请让我知道。

编辑:“重新启动 _ 策略:”前需要“部署:”根据意见

Apache Arrow:用零内存读取数据帧

原文:https://towardsdatascience.com/apache-arrow-read-dataframe-with-zero-memory-69634092b1a?source=collection_archive---------4-----------------------

Arrow 文件格式的理论和实践介绍

上周,我看到了韦斯·麦金尼(Wes McKinney)的一条推文,他最出名的可能是令人敬畏的熊猫套餐的创造者:

所以,当我看到他引用威廉·吉布森的话时,我想一定有什么惊人的事情发生了。我没有失望。

这个链开始的推文是关于拥抱脸,一个自然语言处理库。该项目收集数据集,可用于培训模型的&基准。其中一些数据集非常庞大。在最初的推文中,托马斯·沃尔夫指出,通过一种特殊的文件格式,他和昆廷·洛斯特现在能够在不到一分钟的时间内迭代 17GB 的数据,内存占用为 9MB🤯

我想到的第一个问题:怎么做?这是什么魔法?

这就是韦斯·麦金尼所说的未来。

由于网上没有太多实际的例子,我决定写一篇介绍性的博文,用实际的例子讲述我到目前为止学到的东西。我不属于拥抱脸或皮阿罗项目。在本文的结尾,你会找到所有资料的链接。

任何足够先进的技术都和魔法没什么区别。 ——**亚瑟·C·克拉克,第三定律

Joshua SortinoUnsplash 上拍摄的照片

流畅地交换数据

第一条线索是托马斯·沃尔夫参考阿帕奇箭头。Apache Arrow 是由 Wes McKinney 发起的一个项目,旨在创建一个数据交换接口:

Apache Arrow 是一个针对内存数据的跨语言开发平台。它为平面和层次数据指定了一种标准化的独立于语言的列内存格式,为现代硬件上的高效分析操作而组织。它还提供计算库和零拷贝流消息和进程间通信。[1]

这是什么意思?

在 Arrow 之前,任何应用程序或库之间交换数据的标准方式是以某种方式将数据存储到磁盘。因此,如果. NET 核心库想要将数据传递给 Python 进行数据分析,很可能有人会写出一个文件(例如 csv、json、Parquet 等等),然后用 Python 再次读入。写入(序列化)和读取(反序列化)这两个步骤成本高且速度慢,而且数据集越大,完成每个步骤所需的时间就越长。

如果有一种通过握手和零拷贝直接交换数据的方法会怎么样?它可能看起来像这样:。NET 会开始和 Python 聊天,指着内存中的一堆数据说:嘿,伙计,这是你的了。Python 可以直接跳到它上面,而不用把它从一个地方带到另一个地方。那不是很棒吗?

这就是阿帕奇之箭的意义。

镶木地板是秘密吗?

这让我想知道——我如何使用 Arrow?通过查看拥抱脸的源代码,我了解到该项目使用 PyArrow 来读取数据。在此之前,我将 PyArrow 与 Parquet 联系在一起,Parquet 是一种高度压缩的柱状存储格式。那么,Parquet 是 Arrow 交换数据的方式吗?(剧透:不是)

传统上,数据以逐行的方式存储在磁盘上。列存储诞生于分析大型数据集并高效聚合它们的需要。数据分析对数据行不太感兴趣(例如,一个客户交易、一个通话记录等),而是对其聚合感兴趣(例如,客户的总消费金额、按地区划分的总通话时间等)。

面向行和列的存储(改编自[4]和帕尔默站企鹅数据集)

这导致了方向的改变:列存储不是一行一行地存储,而是一列一列地排列数据。

拼花地板是一种柱状文件格式,它有两个主要优点[4]:

  1. 高压缩性: While。json 或者。csv 文件默认是未压缩的,Parquet 压缩数据,因此节省了大量磁盘空间。表通常混合了具有大量唯一值的列(高基数;想想唯一的用户 ID )和只有几个唯一值的列(基数低;想到)。基数越低,压缩(可能)工作得越好——下一节将详细介绍这一点
  2. 文件查询/过滤器下推:在读入之前,删除不必要的数据。这缩短了加载时间,优化了资源消耗。如果您只需要一个千列表中的两列,那么您不需要扫描所有行来获得这两个属性—您可以直接获取完整的列

压缩

为了更好地理解镶木地板和 Arrow 之间的区别,我们需要绕道而行,获得一些压缩的直觉。文件压缩本身就是一个巨大的课题。以下是一个简化的叙述,通过我对这个话题的理解过滤出来。这段题外话将有助于回答这两个问题:

  • 镶木地板是如何缩小到如此小的尺寸的?
  • 镶木地板和 Arrow 有什么不同?

掷硬币

想象您抛硬币十次并记录结果:

[Head, Head, Head, Head, Tail, Tail, Tail, Head, Tail, Tail]

现在,试着大声说出结果?很可能,您会缩短它并说类似“4 次头,3 次尾,头 2 次尾”:

[4 x Head, 3 x Tail, Head, 2 x Tail]

这就是实际的压缩(所描述的算法被称为游程编码【8】)。我们倾向于自然地看到一个模式并将其缩写。压缩算法也可以做到这一点——只不过需要更多的原始计算能力和复杂的规则。但是这个例子应该足以帮助我们理解一个关键的区别:虽然。csv 采用文字的方法,并阐明每一个单一的记录,Parquet 缩写(没有失去任何信息)。

这个简单的例子足以让我们直观地了解压缩比为什么会有很大的差异。例如,如果排序顺序无关紧要,并且您只对头尾的总出现次数感兴趣,您可以先对列表进行排序,然后压缩版本如下所示:

[5 x Head, 5 x Tail]

也就是说,如果我们在将数据集保存到 Parquet 之前先按所有列对其进行排序,那么与未排序的数据集相比,文件大小会更小。基数越低,压缩率就越高。每列的压缩率预计会随着其在排序顺序中的位置越来越低而缩小。

箭头是否压缩?

有了这个,我们获得了一些关于为什么拼花文件比未压缩文件小的直觉。csv 文件。但这与 Arrow 有什么关系呢?

事实证明,这正是关键的区别之一。镶木地板是以一种高效的方式存储在磁盘上的。使用过滤器下推,您可以减少读入的数据量(即只选择您实际需要的列)。但是,当您想要对数据执行操作时,您的计算机仍然需要打开压缩信息的包装并将其保存到内存中。[2]

另一方面,Arrow 是一种内存映射格式。韦斯·麦金尼在博客中总结道:

“Arrow 序列化设计提供了一个‘数据头’,它描述了表中所有列的所有内存缓冲区的确切位置和大小。这意味着您可以内存映射巨大的、比内存大的数据集,并在这些数据集上就地评估熊猫式的算法,而不必像现在对熊猫那样将它们加载到内存中。您可以从 1tb 的表中读取 1 兆字节,而您只需支付执行这些总计 1 兆字节的随机读取的费用。”【6】

简而言之,应用程序可以直接对存储在磁盘上的数据集进行操作,而无需将其完全加载到内存中。如果你还记得最初的推文——那正是当时的情况。

动手操作:性能比较

现在让我们来探索这些数据格式。作为样本数据集,我使用的是帕尔默站企鹅数据集。因为它只包含 350 行,所以我将它重采样为 100 万行,以便性能差异变得更加明显:

写文件

下一步,我将文件以三种格式写入磁盘:

  • csv(缺少值的数据帧)
  • 拼花地板(缺少值的数据框)
  • 箭头(有缺失值和无缺失值的数据框)

请注意,在某些情况下,Arrow 只能在不分配内存(=零拷贝)的情况下转换为 pandas。其中之一:不能有 NaN 值。为了比较有和没有零复制的性能,我编写了一次有和没有丢失数值的箭头文件。

产生的文件大小为:

文件大小比较

正如所料,Parquet 是最小的文件——尽管是随机序列(在写出文件之前没有进行排序),但它的压缩率高达 80%。Arrow 只是比 csv 稍微小一点。原因是 csv 存储字符串形式的数值会消耗更多的磁盘空间。在所有情况下,丢失值的文件和没有丢失值的文件之间的大小差异是微不足道的(< 0.5 MB)。

阅读时间

现在关键部分:阅读表现。计算平均脚蹼长度需要多长时间?

  • 战斗支援车
  • 镶木地板
  • 带文件 API ( OSFile(...))的箭头
  • 箭头作为内存映射 API ( memory_map(...))带有缺失值/NaN
  • 作为内存映射 API ( memory_map(...))的箭头,没有缺失值

对这三个函数中的每一个进行计时,会产生以下结果:

性能比较:读取列和计算平均值所需的时间

不出所料,csv 是最慢的选择。它需要读取 200MB,解析文本,丢弃除 flipper 长度之外的所有列,然后计算平均值。

Parquet 的速度提高了 60 倍,因为不需要解析整个文件——只读入所需的列。

带有缺失值的箭头比拼花快 3 倍,比 csv 快 200 倍。和 Parquet 一样,Arrow 可以限制自己只读取指定的列。更快的原因是不需要对列进行解压缩。

请注意,使用和不使用零复制读取内存映射 Arrow 文件之间的差异意味着另外约 3 倍的性能提升(即零复制总共比 csv 快约 600 倍,比 Parquet 快约 9 倍)。

令人惊讶的是:带有文件 API 的 Arrow 甚至比 Parquet 还慢。这是怎么回事?

内存消耗

要回答这个问题,让我们看看内存消耗。如果我们读取一列,每个文件消耗多少 RAM?

结果如下:

性能比较:读取列消耗的内存

最值得注意的是:带有文件 API 的 Arrow 消耗了 189 MB——这几乎是整个文件的大小(尽管我们只读取了一列?!).答案就在文档:

“[…]OSFile 每次读取都会分配新的内存,就像 Python 文件对象一样。”[3]

通过使用OSFile,整个文件首先被读入内存。现在很明显为什么这个操作比 Parquet 慢并且消耗了大部分内存!

然而,通过使用内存映射函数和填充的 NaN 值,pandas 数据帧被直接创建在存储的 Arrow 文件之上。无复制:0 MB 内存!难怪这是最快的选择。

你可以在这里找到整个 Jupyter 笔记本📝

结论

对我来说,关于 Arrow 还有很多要学的。到目前为止,我学到的是:鱼和熊掌不可兼得。在[7]之间有一个权衡:

  • 优化磁盘空间/磁盘上的长期存储→拼花地板
  • 优化数据交换和快速检索→箭头

与 csv 相比,Parquet 和 Arrow 的性能都有显著提高。将 Arrow 存储到磁盘时,它比 Parquet 消耗更多的存储空间。然而,Arrow 在读取性能方面击败了 Parquet 无论是在时间还是内存消耗方面。给出的示例(计算一列/读取列的平均值)只是皮毛,我的期望是,随着查询越来越复杂,数据集越来越大,Arrow 会更加出色。

只要用内存映射函数读取 Arrow,读取性能就是不可思议的。最好的情况是数据集没有缺失值/nan。然后 PyArrow 可以施展魔法,让你在桌子上操作,几乎不消耗任何内存。

未来确实已经到来,这太神奇了!

在 Twitter 上关注我

我会继续写关于 Python、数据和技术的文章——我很高兴能在✨推特上与你见面

非常感谢颖颖的全面审查和巨大的反馈!👏

来源

[1]阿帕奇箭头,登陆页面 (2020),阿帕奇箭头网站

[2] Apache Arrow,常见问题解答 (2020),Apache Arrow 网站

[3] Apache Arrow,磁盘和内存映射文件 (2020),Apache Arrow Python 绑定文档

[4] J. LeDem, Apache Arrow 和 Apache Parquet:为什么我们需要不同的项目用于磁盘和内存中的列数据 (2017),KDnuggets

[5] J. LeDem,柱状路线图:阿帕奇拼花地板和阿帕奇箭头 (2017),德雷米奥

[6] W. McKinney, Apache Arrow 和“我讨厌熊猫的 10 件事” (2017),博客

[7] W. McKinney,对丹尼尔·阿巴迪关于阿帕奇之箭的博客的一些评论 (2017),博客

[8]维基百科,游程编码 (2020),维基百科

用 Apache Beam、Dataflow 和 BigQuery 构建数据处理管道

原文:https://towardsdatascience.com/apache-beam-pipeline-for-cleaning-batch-data-using-cloud-dataflow-and-bigquery-f9272cd89eba?source=collection_archive---------5-----------------------

beam 管道的实现,它清理数据并将数据写入 BigQuery 进行分析。

市场上有各种与大数据相关的技术,如 Hadoop、Apache Spark、Apache Flink 等,维护这些技术对开发人员和企业来说都是一个巨大的挑战。哪个工具最适合批量和流式数据?在我们的用例中,某个特定工具的性能和速度是否足够?您应该如何集成不同的数据源?如果这些问题经常出现在您的业务中,您可能需要考虑 Apache Beam。

Apache Beam 是一个开源的统一模型,用于构建批处理和流数据处理管道。Beam 支持针对 Beam 模型编写管道的多种语言特定的 SDK,如 JavaPythonGo 以及在分布式处理后端执行它们的 Runners,包括 Apache FlinkApache SparkGoogle Cloud data flowHazelcast Jet

我们将使用谷歌云平台产品来运行这一管道,所以你需要利用你的免费提供来使用这些产品,直到他们指定的免费使用限制,新用户也将获得 300 美元,在你的免费试用期间花在谷歌云平台产品上。

这里我们要用 Python SDK云数据流来运行管道。

剖析数据管道

管道的关键概念

  • 管道:管理准备执行的 PTransforms 和 PCollections 的有向无环图(DAG)。
  • PCollection: 表示有界或无界数据的集合。
  • p 转换:将输入 p 集合转换成输出 p 集合。
  • PipelineRunner: 表示管道应该在哪里以及如何执行。
  • I/O transform: Beam 附带了许多“io”,即从各种外部存储系统读取数据或向其写入数据的库 p transform。

我在下面剪辑了一些常用的高级转换(Ptransforms ),我们将在我们的管道中使用其中一些。

管道中的常见转换

ParDo 是普通并行处理的主波束变换,不在上图中。ParDo 处理范例类似于 Map/Shuffle/Reduce 风格算法的“Map”阶段:ParDo 转换考虑输入 PCollection 中的每个元素,对该元素执行一些处理,并向输出 PCollection 发出零个或多个元素。

Pipe '|' 是应用转换的操作符,每个转换可以有选择地提供一个唯一的标签。转换可以被链接,我们可以组成任意形状的转换,在运行时,它们将被表示为 DAG。

上述概念是创建 apache beam 管道的核心,所以让我们进一步创建第一个批处理管道,它将清理数据集并将其写入 BigQuery。

管道的基本流程

管道流量

  1. 从 google 云存储桶(Batch)中读取数据。
  2. 应用一些转换,例如通过逗号分隔符分割数据,删除不需要的列,转换数据类型等。
  3. 将数据写入数据宿( BigQuery )并进行分析。

这里我们将使用 Kaggle 的精酿啤酒数据集

啤酒数据集的描述

abv :酒精体积含量,0 为不含酒精,1 为纯酒精
ibu :国际苦味单位,规定一种饮料有多苦
名称:啤酒名称
风格:啤酒风格(lager、ale、IPA 等。)
brewery_id :生产这种啤酒的啤酒厂的唯一标识符
盎司:以盎司为单位的啤酒大小

我们将把这个数据集上传到 google cloud bucket。

在运行管道之前,我们需要启用数据流和大查询 API。在 GCP 搜索框中键入数据流 API 并启用它。

启用 API 按作者分类的图像

同样,您需要启用 BigQuery API。

数据流将使用云桶作为暂存位置来存储临时文件。我们将创建一个云存储桶,并选择最近的位置(区域)。

例如,如果您在亚洲,您必须为计算(数据流作业)的速度和性能选择亚洲地区。

创建 GCS 存储桶-按作者分类的图像

我们将使用适当的模式创建 BigQuery 数据集和表,作为 data sink,来自 dataflow 作业的输出将驻留在其中。数据集区域将是离您最近的位置。在我们的例子中是南亚 1(孟买)。在 BigQuery 中创建表时,需要提供输出模式(已经在 batch.py 中给出)。

接下来打开 cloud shell 编辑器,设置您的项目属性(如果尚未设置),它将克隆包含所有支持文件和数据的 GitHub 存储库。

git clone [https://github.com/aniket-g/batch-pipeline-using-apache-beam-python](https://github.com/aniket-g/batch-pipeline-using-apache-beam-python)

完成后,切换到所有文件所在的目录。

现在使用下面给出的命令将 beer.csv 文件复制到我们的 bucket 中。

gsutil cp beers.csv gs://ag-pipeline/batch/

或者,您可以通过转到存储桶来上传该 CSV 文件。

要运行管道,您需要在虚拟机上安装 Apache Beam 库。

sudo pip3 install apache_beam[gcp]

仅此而已。

现在我们将浏览管道代码,了解它是如何工作的。我们将主要关注管道中的 Ptransforms。

def discard_incomplete(data):
    """Filters out records that don't have an information."""
    return len(data['abv']) > 0 and len(data['id']) > 0 and len(data['name']) > 0 and len(data['style']) > 0

我们已经过滤掉了那些没有信息或空值的数据。

def convert_types(data):
    """Converts string values to their appropriate type."""
    data['abv'] = float(data['abv']) if 'abv' in data else None
    data['id'] = int(data['id']) if 'id' in data else None
    data['name'] = str(data['name']) if 'name' in data else None
    data['style'] = str(data['style']) if 'style' in data else None
    data['ounces'] = float(data['ounces']) if 'ounces' in data else None
    return data

上面的函数将把字符串值转换成适当的数据类型。

def del_unwanted_cols(data):
    """Deleting unwanted columns"""
    del data['ibu']
    del data['brewery_id']
    return data

在上面的函数中,我们删除了不需要的列,这些列最终成为干净的数据。

p = beam.Pipeline(options=PipelineOptions())(p | 'ReadData' >> beam.io.ReadFromText('gs://purchases-3/beers.csv', skip_header_lines =1)
       | 'Split' >> beam.Map(lambda x: x.split(','))
       | 'format to dict' >> beam.Map(lambda x: {"sr": x[0], "abv": x[1], "id": x[2], "name": x[3], "style": x[4], "ounces": x[5]}) 
       | 'DelIncompleteData' >> beam.Filter(discard_incomplete)
       | 'Convertypes' >> beam.Map(convert_types)
       | 'DelUnwantedData' >> beam.Map(del_unwanted_cols)
       | 'WriteToBigQuery' >> beam.io.WriteToBigQuery(
           '{0}:beer.beer_data'.format(PROJECT_ID),
           schema=SCHEMA,
           write_disposition=beam.io.BigQueryDisposition.WRITE_APPEND))
    result = p.run()

beam.io.ReadFromText —将数据从外部来源读入 PCollection

beam.map — 的工作方式类似于 ParDo,以多种方式应用 Map 来变换p 集合中的每一个元素。Map 接受为 PCollection 中的每个输入元素返回单个元素的函数。

光束。Filter —接受一个函数,该函数保留返回 True 的元素,并过滤掉剩余的元素。

beam . io . writetobigquery—对 BigQuerySink 的写转换接受字典的 p 集合。它需要以下参数

  • TableReference 可以是 PROJECT:DATASET。表或数据集。表格字符串。
  • TableSchema 可以是 NAME:TYPE{,NAME:TYPE}* string(例如' month:STRING,event_count:INTEGER ')。

现在,我们使用以下语法,使用数据流运行器运行管道。

python3 batch.py --runner DataFlowRunner --project aniket-g --temp_location gs://ag-pipeline/batch/temp --staging_location gs://ag-pipeline/batch/stag --region asia-east1 --job_name drinkbeer

目前,Dataflow 为一些不包括亚洲-南方 1 的地区提供地区端点,因此我在地区中选择了亚洲-东方 1。

  • 项目 —您的谷歌云项目的 ID。
  • 运行器 —管道运行器,它将解析你的程序并构建你的管道。如果你想调试你的管道,也可以直接运行。这里我们使用数据流运行器。
  • staging_location —数据流的云存储路径,用于存放执行作业的工人所需的代码包。
  • temp_location —数据流的云存储路径,用于存放在流水线执行期间创建的临时作业文件。
  • 区域 —您可以指定想要运行数据流运行程序的区域。
  • job_name (可选)—给数据流管道起任何名字。

现在转到数据流,您可以看到您的作业正在以批处理类型运行。

管道状态—按作者分类的图像

DAG —管道步骤—按作者分类的图像

一旦完成并成功,您将在 BigQuery beer_data 表中看到结果。

大查询表-按作者排序的图像

现在我们可以查询数据来获得一些见解。

# Beer style with highest alcohol by volume
SELECT
  style,
  SUM(abv) AS volume
FROM
  `aniket-g.beer.beer_data`
GROUP BY
  style
ORDER BY
  volume DESC

Bigquery Insights —按作者分类的图片

查看代码:

本文的主要目的是演示如何使用 apache beam 创建清理管道。我只使用了一个包含啤酒信息的数据集,而另一个包含啤酒厂信息的数据集可以提供更多的见解。

参考资料:

http://shzhangji . com/blog/2017/09/12/Apache-beam-quick-start-with-python/

https://beam.apache.org/documentation/programming-guide/

阿帕奇水槽

原文:https://towardsdatascience.com/apache-flume-71ed475eee6d?source=collection_archive---------29-----------------------

使用 Apache Flume 将非结构化数据涓滴输入 HDFS

非结构化日志——照片由Joel&Jasmin frest birdUnsplash 上拍摄

我们已经讨论了如何使用 Apache Sqoop 从我们的关系数据库MySQL(RDBMS)中提取结构化数据,以及如何将这些数据推入 HDFS 并返回

现在的问题是,我们如何让非结构化数据进入 HDFS?我们用阿帕奇卡夫卡,不不不…水槽。阿帕奇水槽。

Apache Flume 是一个开源、强大、可靠和灵活的系统,用于通过与 Hadoop 集群的强耦合,以分布式方式从多个数据源收集、聚合和移动大量非结构化数据到 HDFS/Hbase(例如)。

体系结构

Apache 水槽架构,水槽用户指南

Flume 的架构有 3 个主要组件,即源、通道和接收器,按此顺序排列。这三者共同构成了一个“水槽代理”。

来源

“源”是从一个或多个应用程序/客户端提取非结构化数据(也称为“事件”)的组件。提取过程可以是事件驱动的,也可以通过频繁的轮询来完成,由 source-runners 来执行。“源流道”和“汇流道”是 Flume 的底层不可见组件,分别驱动源和汇。可以将运行器视为控制源或接收器的包装器。
Flume 内置了与 Avro、Thrift、Exec ( /bin/sh -c举例)、Spooling directory、Taildir、JMS、Syslog 等多个数据源的兼容性。

频道

“信道”是信源和信宿之间的缓冲器。缓冲区的属性基于所使用的通道类型,即内存通道、文件通道或 JDBC 通道(Kafka 通道不是内置的)。所使用的信道类型基于单一因素,即事件可靠性。例如,内存通道缓冲事件具有更高的性能,但也非常不稳定,容易发生故障。文件通道的性能一般,对故障的安全性一般。最后,JDBC 通道的性能最慢,但它对故障的安全性最高。因此,我们必须根据我们的使用情形非常谨慎地选择,但通常情况下,文件通道在生产中用于平衡性能和可靠性。
内存通道和文件通道的组合,又名可溢出内存通道目前正处于开发阶段(不建议用于生产),它将在达到最大内存容量后在内存和文件系统中存储事件,从而提供文件通道的可靠性和内存队列(内存通道)的性能。
最后,Flume 遵循一种事务性的方法,在事件被移动到接收端并收到后续接收运行器的确认之前,它不会从通道中移除事件。这一点非常重要,因为就处理速度而言,一些接收器可能比源慢,并可能导致数据损坏。

“接收器”是提取的数据的最终转储或源事件的目的地。通常,我们在这里使用 HDFS,但也可以使用其他选项,如 Hbase、Hive、Elasticsearch 等等。

有趣的是,只需通过代理配置文件和网络就能处理所有这一切,不需要复杂的编程。

只讲理论不实践,聪明的孩子也会变傻

照片由 Unsplash 上的磁杆拍摄

可选先决条件(不适用于生产系统)

HDFS 是一个坚持许可的人。它不认为 unix“根”用户是超级用户,因此我们简单地禁用权限检查,以防止在 HDFS 上运行作业时出现不必要的错误。虽然这对于我们的培训环境来说没问题,但是对于生产环境来说就不好了,在生产环境中,将为每个用户分配适当的权限。

Cloudera 经理— HDFS

导航至 Cloudera Manager 中的 HDFS 服务信息。点击配置选项卡。

HDFS 权限检查

搜索“检查 HDFS 权限”并取消复选框,以禁用 HDFS 的权限。接下来,重启您的集群,以便推进更改(通常它会询问,如果没有,请执行)。

让我们直接看一个简单的例子

可视化下面的配置文件

我们只有一个源、一个通道和一个接收器。有很多其他选项或组合可供选择,可以在 Apache Flume 的用户指南中找到(参考下文)。

下面是我们的简单配置文件:

确保在执行代码之前删除行内注释,以避免错误。以下是概念的更多细节。

准备好这个配置文件后,将它加载到 instance-1 VM 中,并执行下面的命令,开始通过 Flume 处理该文件:

cd /var/lib
flume-ng agent --conf conf --conf-file /root/flume_1.conf --name flume_1

经过几次迭代后,请使用 control+c 退出该过程。让我们深入了解一下日志文件:

水槽日志——简单示例

即使在用 control+c 停止流之后,数据仍然被加载到目标接收器中。这是因为通道,通道是内存中保存的数据的缓冲区(在这种情况下)。

让我们快速检查一下/var/log/flume-ng/下的 taildir_position.json 文件的内容

cat taildir_position.json
[{"inode":193087000,"pos":94026,"file":"/var/log/hadoop-hdfs/hadoop-cmf-hdfs-NAMENODE-instance-1.us-central1.internal.log.out"}]94026 - last position of file - source_1 file

最后,让我们检查数据,确保到最小的单元

hdfs dfs -ls /flume/events/20-05-23/0420/00

通过 Apache Flume 记录在 HDFS 接收器中捕获的事件

另一个选项是通过 :8889 上的色调

选择“以文本形式查看”(非二进制),以文本格式获取事件的详细信息

扇入与扇出

扇入或整合架构,Flume 用户指南

任何大规模日志记录系统的最基本要求是它能够整合来自多个来源的数据或扇入即时,Flume 作为默认 OOTB 提供了这一功能。事实上,我看到过这样的用例,数百个客户端将日志推送到几个代理,只是为了经过多次迭代进行整合。基本上,创建一个水槽代理链也是允许的。

水槽支持水流分叉吗?是的,正如下图所示:

扇出(复制或多路复用)架构,Flume 用户指南

扇出特性有两个选项,即复制和多路复用。例如,在复制过程中,源事件被复制到三个通道中,并被推送到下游的三个接收器中。另一方面,多路复用类似于过滤,例如,如果事件属于“错误”类型,则将事件推送到通道 1 和 3,否则推送到通道 2。请注意,这也是在配置文件中设置的。

使用拦截器可以在复用或不复用的情况下进行过滤。让我们用这个美丽的主旨来快速理解它:

水槽拦截器

故障转移和负载平衡

借助这种扇出特性,可以实现故障转移和负载平衡属性。当然,使用多路复用,这是不可能的,因为事件是被“过滤”的,然而,理论上可以在他们的水槽流中结合复制和多路复用来实现属性。

所以对于一个复制的接收器,故障转移机制 保证只要有一个接收器可用,事件就会被消耗。任何失败的接收器都将从流中删除,并发送回池中。交易完成后,只有在交易正常的情况下,才能重新分配交易。如果有多个失败的接收器,则为它们分配一个优先级,基于此,首先重新分配优先级较高的接收器,如果正常工作,则再次重新分配。优先级在配置文件中分配。

负载平衡机制允许流过多个接收器。Flume 中的负载平衡提供了两种机制,即循环和随机。很少使用随机调度,实际上,如果不喜欢循环调度,就使用定制调度程序。
一般来说,循环调度用于生产,并且是 Flume 的默认设置,因此理解它的工作原理是非常必要的。

这个例子并没有无耻地抄袭维基百科这里🤐

循环负载平衡为每个要由接收器处理的事件提供特定的时隙或“量程”。如果事件到那时还没有完成,它将被暂停,并为下一个事件提供相同的量程,循环重复,直到所有事件都已执行。在上图中,您将看到 E0 是第一个在时间 0 ms 到达的事件。完全执行大约需要 250 ms。我们的量程设置为 100 毫秒,所以 E0 不会完成。接下来,在 50 毫秒,E1 到达,等待 50 毫秒,等待资源变得可用。在 100 毫秒时,E0 被强制停止,E1 执行。它看起来是这样的:

这也来自维基百科这里

让我们加大赌注吧!

现在就设想一下吧——扇出(复制或多路复用)体系结构,Flume 用户指南

*# Let’s create a new folder in our root (I know it’s frowned upon). # This will be our new file system sink
mkdir -m 777 fs_store# Also, for check-pointing
mkdir -m 777 -p flume/checkpoint flume/data# Delete the taildir_position.json
rm /var/log/flume-ng/taildir_position.json*

以下是更新后的配置文件:

扇出配置,这次不加评论—你成功了!

现在运行它:

*cd /var/lib
flume-ng agent --conf conf --conf-file /root/flume_2.conf --name flume_2*

一些日志:

水槽日志—扇出示例

我们得到了一些输出:

通过色调查看新添加的水槽事件

让我们快速看一个,好吗:

事件的快速查看

快速查看同一个 taildir_position.json 文件:

*cat taildir_position.json 
[{"inode":193087000,"pos":94026,"file":"/var/log/hadoop-hdfs/hadoop-cmf-hdfs-NAMENODE-instance-1.us-central1-270403.internal.log.out"}]*

所以,一切看起来都很方便,确实如此:

  • 拥有大量文档的开源技术。
  • 易于配置、高吞吐量、分布式和可扩展的容错框架,具有多种数据流功能。

如果我不提及 Flume 死亡的原因,那将是我的失职:

  • 性能 Apache Flume 的处理速度取决于缓冲通道,在大多数用例中,缓冲通道实际上不能处理超过一百万的事件。当然,负载平衡是有潜力的,但是当我们将事件数量增加 100 倍时会发生什么呢?即使你想追求,也不值得去努力。
  • 事件的顺序——不能保证接收器接收事件的顺序与客户端发送事件的顺序相同,这对于某些用例来说是绝对不可接受的。
  • 重复数据—有些事件可能会重复发送多次,这种情况最多可以忽略不计,但另一方面,也可能是灾难性的。

更新:一定要查看下面布雷特 M 的有趣评论。

***# Be a good samaritan and clean up your workspace**
rm /home/x/flume_1.conf
rm /home/x/flume_2.conf
rm /root/flume_1.conf
rm /root/flume_2.conf
cd /root/flume/checkpoint
rm -R *
cd /root/flume/data
rm -R *
cd /root/fs_store
rm -R **

参考资料:

[1] 阿帕奇水槽用户指南,阿帕奇水槽,ASF

[2]非结构化日志—JoelJasmin fr estbirdUnsplash 上拍摄的照片

[3] 循环调度,维基百科

* [## Apache Sqoop

RDBMS 到 HDFS 并返回

towardsdatascience.com](/apache-sqoop-1113ce453639) [## 阿帕奇纱线和动物园管理员

关于资源分配和高可用性的一切

towardsdatascience.com](/apache-yarn-zookeeper-61e17a958215) [## 使用 Hadoop 生态系统的大数据分析渠道

登录页面

medium.com](https://medium.com/@prathamesh.nimkar/big-data-analytics-using-the-hadoop-ecosystem-411d629084d3)*

阿帕奇猪

原文:https://towardsdatascience.com/apache-pig-1dd61d2ead31?source=collection_archive---------53-----------------------

最简单的 MapReduce 方法

照片由费边布兰克Unsplash 上拍摄

2006 年,雅虎的好人们研究开发了一种简单直观的方法来创建和执行大型数据集上的 MapReduce 作业。第二年,该项目被 Apache 软件基金会接受,此后不久,作为 Apache Pig 发布。

Apache Pig 在 Hadoop 生态系统中的位置

上图简单展示了 Apache Pig 在 Hadoop 生态系统中的位置。Pig 不仅与 MapReduce 兼容,还与 Tez 和 Spark 处理引擎兼容,从而显著提高了性能。对于外行来说,Tez 可以被认为是 MapReduce 框架的一个高效版本。最后,存储层默认是 HDFS,但是 Pig 也支持 HBase、Hive 等等。

有趣的事实 : Pig 在本地模式下工作,也就是说,它通过脚本中的简单配置设置在本地 RAM 和本地文件系统上运行。很酷吧?

体系结构

架构流程

  • 猪拉丁语:是与猪一起工作时使用的语言。Pig Latin 语句是加载、处理和转储数据的基本结构,类似于 ETL。它们是以“;”结尾的多行语句并跟随懒评
  • Grunt Shell:Apache Pig 提供的原生 Shell,其中写入/执行所有 Pig 拉丁脚本。然而,我们也可以在 Hue 中执行同样的操作。
  • 解析器:一旦脚本被执行,它就到达解析器,顾名思义,它被解析为语法和类型检查等。解析器生成一个 DAG 或有向无环图,它基本上是一个逻辑数据处理流程图。我们将在下面看到。
  • 优化器:DAG 被传递到优化器,在那里发生以下优化:

阿帕奇猪优化器

请注意,上述自动优化是默认启用的。

  • 编译器:优化后的脚本被组合成几个作业,无论是 MapReduce、Tez 还是 Spark。
  • 执行引擎:根据选择的处理引擎,任务现在被发送执行。

数据模型

时钟:raphal Biscaldi、Alexis Antoine、Lina Verovaya、Priscilla Du Preez 和 Louis Hansel @shotsoflouis 在 Unsplash 上拍摄的照片

让我们使用上面任意的图片来理解 Apache Pig 的数据模型,按照时钟顺序

第一张图片是原子,这是 Apache Pig 中可用的最小数据单元。它可以是任何数据类型,例如,int、long、float、double、char 数组和 byte 数组,它们携带单个信息值。比如【普拉塔梅什】或者 30 或者【medium 22】

接下来,我们有一组有序的任意数据类型的“字段”,用逗号作为分隔符。把它想象成 csv 文件中的一行。这个数据结构被称为一个元组。比如 ('Prathamesh ',30,' Medium22')

简单地说,一个是一个无序的元组集或集合。可以将其视为 csv 中的单个/多个非唯一记录。然而,与 csv 不同,它没有固定的结构,即它具有灵活的模式,例如第一行可以有 5 个字段,第二行有 30 个字段,依此类推。比如 {('Prathamesh ',30,' Medium22 '),(' Nimkar ',700)}

包有两种类型,外层和内层。外袋是关系,是元组的袋子。可以把它想象成关系数据库中的一个表,只是没有固定的模式。一个内袋是另一个袋内的一个关系,一个嵌套袋结构或袋-如果你愿意。

Pig 还通过一个 Map 数据结构支持【char array to element】形式的键值对。该元素可以是任何 Pig 数据类型。例如, ["Name"#"Prathamesh "," Year"#2020] ,其中括号分隔映射,逗号分隔对,哈希分隔键和值。

秀!不要说

如果你一直关注我的帖子,你已经将结构化非结构化数据导入 HDFS。让我们进入 Hue,打开 Pig 编辑器,探索一些常用的 Pig 拉丁文命令。

结构化数据集—脚注中的详细信息

装载、描述、说明&转储

加载、描述、说明和转储

你现在可能已经注意到,猪拉丁语是懒惰的评价。这意味着,它等待某些特定的关键字,以便实际执行手头的任务。描述、说明和转储就是几个例子。

工作状态

该作业分为两种类型,“oozie launcher”作为该作业的父包装器,而“piglatin*”。猪”实际上运行 MapReduce 代码。作业被初始化,资源被授予,作业被监控到完成,输出被显示为 oozie 启动器的一部分。

描述一下 DESC

图解和转储

以上是传递给 Pig 服务器的各个语句的输出。如果您注意到,转储输出反映正确,但插图视图不正确。这是因为 Pig 不能排除引号中的逗号分隔值,就像我们数据集中的城市名称一样。为了纠正这一点,我们使用存钱罐用户自定义函数。Piggy bank 是为 Apache Pig 编写的 Java UDFs 的常见存储。我们现在要忽略这个错误,所以,想了解更多关于小猪扑满的信息,最好访问这个网址

过滤器

过滤器

过滤输出

上面的要点只有 3 行代码,只需通过多个城市名加载和过滤数据集。你认为 Apache Pig 非常容易使用,非常直观,你说得对,确实如此。这些内置函数中的一些就像口语单词一样。

FOREACH,组和商店

FOREACH,组和商店

基于同一个示例,对于每一行,只选择感兴趣的几列。然后,我们根据唯一的运营商 id 对数据集进行分组,最后,我们将数据集存储回 HDFS。就这样,只需 3 行代码就可以完成这个任务。

最终输出

您可能已经注意到,我们不仅执行了提取,还执行了转换和加载,从而完成了 Map & Reduce。

我对 Apache Pig 的简短介绍到此结束,这是最简单的 MapReduce 方法,尤其是如果你不是计算机出身的话。希望您已经发现它很有用,并且渴望尝试一下。最后,如果你有任何问题,请随时提问。

参考文献:

[1] Apache Pig 概述,Apache Hadoop,ASF

[2] 入门,阿帕奇猪,ASF

[3] 性能和效率,Apache Pig,ASF

数据集

请注意,我没有下载所有的列来创建数据集。但是你可以。

结构化清洗数据集可以在这里找到
用一些数据解释?
给你
原始数据集的首页— 此处
原始数据——此处
有关字段的更多详细信息— 此处为
加载数据?— 此处

[## MapReduce

简化 MapReduce 框架

towardsdatascience.com](/simplifying-the-mapreduce-framework-20915f13ebd3) [## 阿帕奇水槽

使用 Apache Flume 将非结构化数据涓滴输入 HDFS

towardsdatascience.com](/apache-flume-71ed475eee6d) [## 使用 Hadoop 生态系统的大数据分析渠道

登录页面

medium.com](https://medium.com/@prathamesh.nimkar/big-data-analytics-using-the-hadoop-ecosystem-411d629084d3)

Apache Spark:缓存

原文:https://towardsdatascience.com/apache-spark-caching-603154173c48?source=collection_archive---------11-----------------------

Apache Spark 提供了一个重要的特性来缓存中间数据,并在对相同数据运行多个查询时提供显著的性能改进。在本文中,我们将比较不同的缓存技术、缓存的好处以及何时缓存数据。

如何缓存

参考 DataSet.scala

df.cache

cache 方法使用默认存储级别 MEMORY_AND_DISK 调用 persist 方法。其他存储级别将在后面讨论。

df.persist(StorageLevel.MEMORY_AND_DISK)

何时缓存

缓存的经验法则是识别将在 Spark 应用程序中重用的数据帧并缓存它。即使你没有足够的内存来缓存所有的数据,你也应该继续缓存。Spark 会在内存中缓存所有它能缓存的内容,并将其余的内容缓存到磁盘中。

缓存数据帧的好处

  • 从源(hdfs://或 s3://)读取数据非常耗时。因此,在从源中读取数据并应用所有常见操作后,如果要重用数据,请缓存它。
  • 通过缓存,您可以在 spark 应用程序中创建一个检查点,如果进一步执行应用程序的任何任务失败,您的应用程序将能够从缓存中重新计算丢失的 RDD 分区。
  • 如果没有足够的内存,数据将被缓存在 executor 的本地磁盘上,这也比从源代码读取要快。
  • 如果您只能缓存一小部分数据,这也将提高性能,其余的数据可以由 spark 重新计算,这就是 RDD 的弹性。

Spark 中的缓存方法

我们可以使用不同的存储级别来缓存数据。参考: StorageLevel.scala

  • DISK_ONLY:仅以序列化格式将数据保存在磁盘上。
  • MEMORY_ONLY:只以反序列化的格式将数据保存在内存中。
  • MEMORY_AND_DISK:将数据保存在内存中,如果没有足够的内存可用,被逐出的块将存储在磁盘上。
  • OFF_HEAP:数据保存在堆外内存中。参考火花文件中的 spark.memory.offHeap.enabled。

我们可以通过使用 DISK_ONLY_2、MEMORY_AND_DISK_2 等方法明确指定在缓存数据时是否使用复制。

我们还可以指定在存储时是否序列化数据。像 MEMORY_ONLY_SER 等方法。使用序列化格式会增加处理时间,但会减少内存占用。

还提供带复制的序列化,例如 MEMORY_ONLY_SER_2

不同存储级别之间的比较

以下测试在本地机器上运行,驱动内存为 12GB,输入数据为 14GB,每次迭代使用 spark.time 记录时间。它输出在一个循环中执行了 10 次的查询的运行时间。

import org.apache.spark.storage.StorageLevelval df = spark.read.option("header",true).csv("/test/test.csv")
df.persist(StorageLevel.DISK_ONLY)
for(_ <- 0 until 10)
{
    spark.time(df.filter($"category_code" like  "%electronics%").count)
}

注意:记住 spark 中的 cache()是延迟计算的。所以当第一个动作被调用时,数据将被缓存。缓存将花费额外的时间,这可以在上图中看到。对于重新运行,我们观察到缓存带来了显著的性能优势。

结论

在这篇文章中,我们了解到缓存是优化 spark 作业的一项重要技术。如果我们要在代码中多次使用数据,我们应该缓存数据。它可以给我们带来显著的性能优势。

Docker 上的 Apache Spark 集群(英尺。JupyterLab 接口)

原文:https://towardsdatascience.com/apache-spark-cluster-on-docker-ft-a-juyterlab-interface-418383c95445?source=collection_archive---------5-----------------------

Docker 上的集群应用

使用 JupyterLab 接口在 Docker 上以独立模式构建自己的 Apache Spark 集群

UnsplashJez Timms 的火花

Apache Spark 可以说是最受欢迎的大数据处理引擎。GitHub 上有超过 25k 颗星星,该框架是学习使用 Python、Scala 和 r 在分布式系统中进行并行计算的绝佳起点。

首先,您可以在您的机器上运行 Apache Spark,方法是使用市面上许多优秀的 Docker 发行版中的一个。 Jupyter 提供了一个优秀的dockeredApache Spark,带有一个 JupyterLab 接口,但是缺少框架分布式核心,因为它在一个容器上运行。一些 GitHub 项目提供了分布式集群体验,但是缺少 JupyterLab 接口,削弱了 IDE 提供的可用性。

我相信,学习和实践 Apache Spark 代码的综合环境必须保持其分布式性质,同时提供令人敬畏的用户体验。

这篇文章就是关于这个信念的。

在接下来的小节中,我将向您展示如何构建自己的集群。最后,您将拥有一个用 Docker 构建的功能完整的 Apache Spark 集群,附带一个 Spark 主节点、两个 Spark 工作节点和一个 JupyterLab 接口。它还将包括 Apache Spark Python API (PySpark)和一个模拟的 Hadoop 分布式文件系统(HDFS)。

TL;速度三角形定位法(dead reckoning)

本文展示了如何使用 Docker 作为基础设施层在独立模式下构建 Apache Spark 集群。它附带了以下内容:

  • Python 3.7 配 PySpark 3.0.0 和 Java 8;
  • Apache Spark 3.0.0,有一个主节点和两个工作节点;
  • JupyterLab IDE 2 . 1 . 5;
  • 模拟 HDFS 2.7。

要创建集群,我们需要为 JupyterLab 和 Spark 节点创建、构建和组合 Docker 映像。你可以通过使用托管在我的 GitHub 上的开箱即用的发行版来跳过教程。

更新# 1:2020 年 8 月 9 日,我们通过杏仁 Jupyter Scala 内核发布了对 Spark Scala API 的支持。谢谢杏仁太棒了。✨

更新# 2:2020 年 8 月 19 日,我们通过 IRkernel Jupyter R 内核发布了对 Spark R API (SparkR)的支持。谢谢伊克内尔这么棒。✨

要求

  • Docker1 . 13 . 0+;
  • Docker 作曲 1.10.0+。

目录

  1. 集群概述;
  2. 创建图像;
  3. 构建图像;
  4. 组成集群;
  5. 创建 PySpark 应用程序。

1.集群概述

该集群由四个主要组件组成:JupyterLab IDE、Spark 主节点和两个 Spark workers 节点。用户连接到主节点,并通过 Jupyter notebooks 提供的漂亮 GUI 提交 Spark 命令。主节点处理输入并将计算工作负载分配给工作节点,然后将结果发送回 IDE。这些组件使用本地主机网络连接,并通过模拟 HDFS 的共享装载卷在彼此之间共享数据。

Apache Spark 集群概述

如前所述,我们需要为 JupyterLab 和 Spark 节点创建、构建和组合 Docker 映像来构建集群。我们将使用以下 Docker 图像层次结构:

Docker 图像层次结构

集群基础映像将下载并安装通用软件工具(Java、Python 等)。)并将为 HDFS 创建共享目录。在 Spark 基本映像上,Apache Spark 应用程序将被下载并配置给主节点和工作节点。Spark 主映像将配置框架作为主节点运行。类似地,Spark 工作节点将配置 Apache Spark 应用程序作为工作节点运行。最后,JupyterLab 映像将使用集群基础映像来安装和配置 IDE 和 PySpark(Apache Spark 的 Python API)。

2.创建图像

2.1.聚类基础图像

对于基本映像,我们将使用 Linux 发行版来安装 Java 8(或 11), Apache Spark only 需求。我们还需要安装 Python 3 来支持 PySpark,并创建共享卷来模拟 HDFS。

集群基础映像的 docker 文件

首先,让我们选择 Linux 操作系统。Apache Spark 官方 GitHub repository 有一个用于 Kubernetes 部署的 Dockerfile ,它使用一个小型 Debian 映像,内置 Java 8 运行时环境(JRE)。通过选择相同的基础映像,我们解决了操作系统选择和 Java 安装的问题。然后,我们从 Debian 官方软件包库中获得了最新的 Python 版本(目前是 3.7),并创建了共享卷。

2.2.火花基础图像

对于 Spark 基本映像,我们将在独立模式下获取并设置 Apache Spark,这是其最简单的部署配置。在这种模式下,我们将使用它的资源管理器来设置容器,使其作为主节点或工作节点运行。相比之下,资源管理器如 Apache YARN 根据用户工作负载动态分配容器作为主节点或工作节点。此外,我们将获得一个支持 Apache Hadoop 的 Apache Spark 版本,允许集群使用在基本集群映像中创建的共享卷来模拟 HDFS。

Spark 基本图像的 docker 文件

让我们先从官方 Apache 资源库下载 Apache Spark 最新版本(目前为 3.0.0)支持 Apache Hadoop。然后,我们用下载的包玩一会儿(解包,移动,等等。)我们已经为设置阶段做好了准备。最后,我们配置主节点和工作节点共有的四个 Spark 变量:

  1. SPARK_HOME 是框架用来设置任务的已安装的 Apache Spark 位置;
  2. SPARK_MASTER_HOST 是工作节点用来连接的主节点主机名
  3. SPARK_MASTER_PORT 是主节点端口,供工作节点连接使用;
  4. PYSPARK_PYTHON 是 Apache Spark 用来支持其 Python API 的 PYTHON 安装位置。

2.3.Spark 主图像

对于 Spark 主映像,我们将设置 Apache Spark 应用程序作为主节点运行。我们将配置网络端口,以允许与工作节点的网络连接,并公开主 web UI,一个监视主节点活动的网页。最后,我们将设置容器启动命令来启动作为主实例的节点。

Spark 主映像的 docker 文件

我们首先公开在 SPARK_MASTER_PORT 环境变量中配置的端口,以允许工人连接到主节点。然后,我们公开 SPARK_MASTER_WEBUI_PORT 端口,让我们访问主 WEBUI 页面。最后,我们将容器启动命令设置为运行 Spark 内置部署脚本,并将主类作为其参数。

2.4.星火工作者形象

对于 Spark worker 映像,我们将设置 Apache Spark 应用程序作为 worker 节点运行。与主节点类似,我们将配置网络端口以公开 worker web UI,一个监视 worker 节点活动的网页,并设置容器启动命令以将节点作为 worker 实例启动。

Spark 工人图像的 worker 文件

首先,我们公开了 SPARK_WORKER_WEBUI_PORT 端口,以允许访问 worker web UI 页面,就像我们对主节点所做的那样。然后,我们设置容器启动命令来运行 Spark 内置部署脚本,使用工作类和主网络地址作为参数。这将使工作节点在启动过程中连接到主节点。

2.5.木星图像

对于 JupyterLab 映像,我们稍微后退一点,从集群基础映像重新开始。我们将安装和配置 IDE 以及与 Spark 节点上安装的略有不同的 Apache Spark 发行版。

JupyterLab 图像的 docker 文件

我们首先安装 pip(Python 的包管理器)和 Python 开发工具,以允许在映像构建期间和容器运行时安装 Python 包。然后,让我们从 Python 包索引(PyPI)中获取 JupyterLabPySpark 。最后,我们公开默认端口以允许访问 JupyterLab web 界面,并设置容器启动命令以运行 IDE 应用程序。

3.构建图像

Docker 图像已经准备好了,让我们建立它们。注意,因为我们在 Dockerfiles 上使用了 Docker arg 关键字来指定软件版本,所以我们可以很容易地为集群更改默认的 Apache Spark 和 JupyterLab 版本。

构建集群映像

4.构成集群

Docker 合成文件包含我们集群的配方。在这里,我们将创建 JuyterLab 和 Spark 节点容器,向本地主机网络公开它们的端口,并将它们连接到模拟的 HDFS。

集群的 Docker 合成文件

我们首先为模拟的 HDFS 创建 Docker 卷。接下来,我们为每个集群组件创建一个容器。 jupyterlab 容器公开 IDE 端口,并将其共享工作区目录绑定到 HDFS 卷。同样, spark-master 容器公开了它的 web UI 端口和它的 master-worker 连接端口,并且也绑定到 HDFS 卷。

我们通过创建两个名为 spark-worker-1spark-worker-2 的 Spark worker 容器来完成。每个容器都公开其 web UI 端口(分别映射到 8081 和 8082 ),并绑定到 HDFS 卷。这些容器有一个指定其硬件分配的环境步骤:

  • SPARK_WORKER_CORE 为核心数;
  • SPARK_WORKER_MEMORY 是 RAM 的大小。

默认情况下,我们为每个容器选择一个内核和 512 MB RAM。您可以随意使用硬件分配,但要确保尊重您的机器限制,以避免内存问题。另外,为 Docker 应用程序提供足够的资源来处理选定的值。

要编写集群,请运行 Docker 编写文件:

构成集群

完成后,查看组件 web 用户界面:

5.创建 PySpark 应用程序

集群启动并运行后,让我们创建第一个 PySpark 应用程序。

创建 PySpark 应用程序

打开 JupyterLab IDE 并创建一个 Python Jupyter 笔记本。通过使用具有以下参数的 Spark 会话对象连接到 Spark 主节点来创建 PySpark 应用程序:

运行该单元,您将能够在 Spark master web UI 的“运行应用程序”下看到该应用程序。然后我们从 UCI 库下载虹膜数据集到模拟的 HDFS。最后,用 PySpark 读取并打印数据。

这是所有的乡亲。我希望我已经帮助您更多地了解了 Apache Spark 的内部结构以及分布式应用程序是如何工作的。快乐学习!

Apache Spark 数据集编码器揭秘

原文:https://towardsdatascience.com/apache-spark-dataset-encoders-demystified-4a3026900d63?source=collection_archive---------12-----------------------

Spark Logo:https://commons . wikimedia . org/wiki/File:Apache _ Spark _ Logo . SVG

编码器就像 Spark 数据集 API 的秘方,它正在成为 Spark 作业的默认范例,本文试图揭示其基本成分。

Spark 中的 RDD、数据帧和数据集是数据记录集合的不同表示,每一个都有自己的一组 API 来对集合执行所需的转换和操作。在这三者中,RDD 形成了最古老和最基本的这种表示,并伴随着 Spark 1.6 中的数据帧和数据集。

然而,在 Spark 2.0 中,数据集的使用已经成为 Spark 程序员在编写 Spark 作业时的默认标准。在 Spark 2.0 中,Dataframe 的概念(以表格形式表示记录集合)与 Dataset 合并。在 2.0 中,Dataframe 只是某种类型的数据集的别名。数据集的这种流行是由于它们被设计成提供 RDD 和数据帧世界的最佳、灵活性、rdd 的编译类型安全性以及数据帧的效率和性能。

数据集概念的核心是一个编码器框架,与 rdd 相比,它为数据集提供了存储和执行效率增益。理解编码器框架对于编写和调试基于数据集的 Spark 作业非常重要。由于每个数据集必须与一个类型相关联,所以每当创建某种类型的数据集时(从文件、RAM 中的对象集合、RDD 或数据集),必须在数据集创建 API 中指定相同类型的对应编码器。然而,编码器的规范在某些情况下可能是隐含的,例如装箱的原始类型。

特定类型的编码器将 Java 对象(编码器类型)或数据记录(符合编码器类型的数据模式)编码为原始内存支持的二进制格式,反之亦然。编码器是 Spark 的 tungusten 框架的一部分。在原始内存的支持下,从编码的二进制文本中更新或查询相关信息是通过 Java 不安全 API 完成的。

Spark 提供了一个通用的编码器接口和一个实现该接口的通用编码器,称为expression Encoder。这个编码器通过表达式编码和解码(也可以理解为序列化和反序列化)一个 JVM 对象(类型 T)。此外,还有一个工厂可供用户使用,即。,编码器。**

编码器工厂为类型提供存储高效的 ExpressionEncoders,例如 Java boxed 原语类型(Integer、Long、Double、Short、Float、Byte 等。)、字符串、日期、时间戳、Java bean 等。此外,该工厂还提供了通用的基于 Java/Kryo 序列化的 ExpressionEncoder,它可用于任何类型,以便可以为存储高效的 ExpressionEncoder 未涵盖的自定义类型创建编码器。

下面是一个声明为 TestWrapper 的 Java bean 的示例

import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;public class TestWrapper implements Serializable {
	private static final long serialVersionUID = 1L; 
        private Map<String, String> zap;
	private ArrayList<String> strA;
	private String name;
	private String value;

	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getValue() {
		return value;
	}
	public void setValue(String value) {
		this.value = value;
	}
	public Map<String, String> getZap() {
		return zap;
	}
	public void setZap(Map<String, String> zap) {
		this.zap = zap;
	}
	public ArrayList<String> getStrA() {
		return strA;
	}
	public void setStrA(ArrayList<String> strA) {
		this.strA = strA;
	}	
}

存储/性能高效表达式 TestWrapper Java bean 的编码器声明为:

ExpressionEncoder<TestWrapper> en = Encoders.bean(TestWrapper.class) 

TestWrapper 的 ExpressionEncoder 也可以使用 Java/kryo 序列化来声明,如下所示:

ExpressionEncoder<TestWrapper> en = Encoders.javaserialization(TestWrapper.class)
or
ExpressionEncoder<TestWrapper> en = Encoders.kryo(TestWrapper.class)

创建 ExpressionEncoder 后,可以使用它将 TestWrapper 的实例编码/解码为二进制格式,如下所示:

TestWrapper tw = new TestWrapper()/* To encode tw into binary format */
InternalRow row = en.toRow(tw)/* To decode tw from binary format */
Seq<Attribute> attr = en.resolveAndBind$default$1();
TestWrapper tw = 
en.resolveAndBind(attrs,sparkSession.sessionState().analyzer()).fromRow(row);

InternalRow 抽象存储的二进制编码格式,并公开查询和更新以二进制格式存储的字段的方法,如下所示:

row.numFields() /* Outputs number of fields stored in the binary format */
row.getString(0) /* Outputs the value of the name field in the TestWrapper instance tw stored in the binary format row */

二进制格式的字段数量与特定 ExpressionEncoder 中解释的架构一致。例如,当 Java bean ExpressionEncoder 用于 TestWrapper 时,字段的数量是 4,而如果使用基于 Java/kyro 序列化程序的 ExpressionEncoder,则只有 1 个字段。

在基于串行化的 ExpressionEncoders 中,基于 Java 或 Kryo 串行化来串行化整个对象,并且串行化的字节串作为编码的二进制格式中唯一的单个字段来保存,因此这些缺乏存储效率,并且不能直接从编码的二进制格式中直接查询对象的特定字段。

另一方面,

在基于 Java bean 的 ExpressionEncoders 中,bean 对象通过保持其字段为二进制格式而被映射为二进制格式,从而提供了存储效率和快速查询单个字段的双重好处。因此,对于由复杂数据类型组成的 Datatsets,应该始终将 datatype 构造为由字段组成的 Java bean,对于这些字段,编码器工厂支持基于非序列化的 ExpressionEncoders。

下面是编码器输出的二进制格式的图片

数据集编码器的行二进制格式

如上所示,开头有一个空位图,可以有效地检测某些字段的空值。接下来是固定长度条目部分,其中每个条目对应于一个字段,条目的数量等于字段的数量。这些条目要么包含固定长度字段的值,要么包含值偏移量(在变量部分)和值长度。最后的变量部分包含可变长度字段的值。

下面给出了通过存储高效编码器获得的空间节省的示例。

内存中缓存 RDD 的大小(_ u)

内存中缓存数据集的大小

在上图中,首先,从 Person bean 的 10 万个对象(由 Id、Name、Age 字段组成)中创建了一个 RDD,并将其缓存在内存中以测量内存大小。其次,使用 bean encoder (ExpressionEncoder)从 Person bean 的相同的 1 万个对象中创建一个数据集,然后将其缓存在内存中以测量内存大小。对比两者的内存大小,Dataset 明显体现了内存优势。

概括来说,编码器提供了 3 个广泛的好处,使数据集获得了现在的荣耀:

存储效率:数据集编码器为广泛使用的 Java 类型提供存储效率。因此,数据集可以很容易地缓存在内存中以提高性能。此外,现成的二进制格式支持在磁盘上高效存储数据集(无需 JAVA/Kryo 序列化程序)。

查询效率:由于数据字段的布局是在编码器的二进制格式中定义好的,所以可以直接从存储在磁盘上的高效编码的二进制格式的对象中查询数据字段。另一方面,如果对象以序列化的形式存储在磁盘上,那么为了查询它的字段,对象必须首先在内存中被反序列化,这是非常低效的。

洗牌效率:在洗牌过程中,数据对象通过网络从一个分区移动到另一个分区。这种转变通常需要通过 Java/Kryo 序列化器对数据进行序列化和反序列化。但是,由于 Dataset encoder 已经将对象编码成一种紧凑的、定义明确的二进制格式,因此不需要进一步的显式序列化。由于数据集编码器的存储效率远远领先于传统的序列化器,因此在数据集范围转换的情况下,混洗数据的数量要比 rdd 的情况少。混洗数据量的减少又增加了混洗阶段的可靠性和性能。

如果你对编码器有更多的疑问,请在评论区写下。

另外,这里有我最近出版的深入讨论 Spark 分区的书的链接:

https://www . Amazon . com/Guide-Spark-Partitioning-Explained-Depth-ebook/DP/b 08 kjct 3 xn

使用 HTTP REST 端点服务 JSON 数据的 Spark 流

原文:https://towardsdatascience.com/apache-spark-stream-reading-data-from-local-http-server-d37e90e70fb0?source=collection_archive---------13-----------------------

使用 HTTP REST 端点作为流源,加速结构化流管道的开发和测试。

照片由迈克尔·泽兹奇Unsplash 上拍摄

编写分布式应用程序可能是一个耗时的过程。虽然在本地机器上运行简单的spark.range( 0, 10 ).reduce( _ + _ )(Spark 的一个“Hello World”示例)代码非常容易,但当您遇到更复杂的真实世界用例时,它最终会变得复杂,特别是在结构化的流世界中,您希望进行流聚合、与其他流或静态数据集连接。

主要的挑战不仅仅是设置 Spark 或编写代码,而是在将它推向远程环境之前,在本地机器上有效地测试它。

编写代码时,在本地机器上处理来自 Kinesis、Kafka 或 S3 的流数据可能不可行,原因有很多:1)您没有足够的计算能力。2)您必须从消息队列的“最早”偏移量开始处理数据,但是要处理的数据太多了。即使你有等级限制,也可能需要几个小时来处理数据。

在这种情况下,如何在本地机器上开发生产就绪的 spark 应用程序呢?常规做法是针对假数据进行开发。内存流速率流可以帮助你用一些伪数据复制结构化的流行为。让我们来看看这个单元测试:

它只是从 JSON 文件中读取假数据,并将其添加到内存流中(第 18 行)。当你写单元或者集成测试时,内存流工作得很好,但是你真的不能在一个特别的基础上添加更多的数据,然后看着你的流作业处理它。这使得内存流的交互性有所降低(除非您在 REPL 环境中运行它)。

如果 spark 在开发期间有某种更具交互性的方式将数据发送到结构化的流管道,那就太好了。这可以节省很多时间。您可以从套接字流式读取数据,但通过 TCP 套接字作为单个有效负载发送的数据量是有限的(除非您更改操作系统设置)。这可能会变得复杂。

是的,这是 spark 没有内置的东西,实际上,实现非常简单:将一个简单的 HTTP web 服务器与 spark 管道集成。服务器可以将有效负载放在 MemoryStream 上,您的 spark 应用程序可以从中读取。

注意 这仅用于本地测试和运行。因为它在底层使用内存流,所以它是不容错的。参考结构化流中的 容错语义

现在,我们该怎么做呢?您可以在 Scala 中创建一个简单的 HTTP 服务器:

scala 中一个简单的 HTTP 服务器

上面的代码创建了一个简单的 HTTP 服务器,它打印请求负载,并总是将{ “success" : true }响应发送回客户机。我们可以通过将这个payload数据放在一个流源(内存流)上,将它流式传输到 spark 应用程序中。所以,当然,这个服务器必须用 spark 应用程序启动。

为了使其可重用并能够创建多个 HTTP 流源,我们可以将该代码放在一个包装类中:

从本地 HTTP 端点读取数据,并将其放入内存流

创建的本地 HTTP 服务器将被 spark 应用程序终止。您可以简单地启动服务器,并使用以下命令从 HTTP 端点读取流数据:

scala> val httpDF = new HttpServerStream( port = 9999 ).toDF
httpDF: org.apache.spark.sql.DataFrame scala> httpDF.printSchema()
root
|-- value: string (nullable = true)
|-- timestamp: timestamp (nullable = true) scala> httpDF.isStreaming()
res1: Boolean = true

数据帧的value列是 HTTP 端点上接收的字符串化 JSON 有效负载。这里有一个完整的例子:

启动 Spark App(主要方法)

现在,您可以使用您喜欢的 REST API 工具将数据发送到这个 HTTP 端点HTTP://localhost:9999并观察您的应用程序处理它。

演示和示例代码

https://github.com/cchandurkar/spark-http-streaming

看看它的实际效果

类似地,可以在同一个 spark 应用程序的不同端口上创建多个 HTTP 流。让我们以来自流-流连接的点击流为例。

val impressions = new HttpServerStream( port = 9997 ).toDF
   .withColumn( "i", from_json($"value" ..... )
   .select( $"i.impressionAdId", $"i.impressionTime" )
   .withWatermark( "impressionTime", "5 minutes" )val clicks = new HttpServerStream( port = 9998 ).toDF
   .withColumn( "c", from_json($"value" ..... ) )
   .select( $"c.clickAdId", $"c.clickTime" )
   .withWatermark( "clickTime", "10 minutes" )val joinedDF = impressions.join( 
   clicks,
   expr("""
     clickAdId = impressionAdId AND
     clickTime >= impressionTime AND
     clickTime <= impressionTime + interval "5 minutes"
   """)
)

G G 试试看,让我知道你对此的看法。请随意提出改进建议或指出注意事项。在 ** LinkedIn ** 上联系我。干杯!

阿帕奇火花与 Kubernetes 和快速 S3 访问

原文:https://towardsdatascience.com/apache-spark-with-kubernetes-and-fast-s3-access-27e64eb14e0f?source=collection_archive---------7-----------------------

为了充分利用 Apache Spark,我们需要两样东西:

  • 存储数据的分布式存储器
  • 跨计算集群运行 Spark 执行器的调度程序

人们一直在以不同的方式在内部和云上实现这一点。对于内部部署,大多数使用 Spark 和 Hadoop,尤其是 HDFS 用于存储,YARN 用于调度。而在云中,大多数使用像亚马逊 S3 这样的对象存储作为存储,并使用一个单独的云原生服务,如亚马逊 EMR 或 Databricks 作为调度。如果我们可以在单个架构 on-promise 或云中使用 Spark 会怎么样?

与库伯内特斯和 S3 一起进入火花。该架构的亮点包括:

  • 跨混合云运行 Spark 的单一架构。
  • 独立扩展、操作计算和存储。
  • 快速供应、部署和升级。
  • 不需要 Hadoop,使用和操作复杂。

在这篇博客中,我将解释如何在 Kubernetes 上使用 Spark 操作符来运行 Spark。我还将描述使用 S3A 连接器S3A 提交器的快速 S3 数据访问的配置。这种架构既适用于云对象存储,也适用于本地 S3 兼容对象存储,如 FlashBlade S3

安装 Spark Kubernetes 操作器

按照本快速入门指南安装操作器。确保在安装中启用了 webhook。

helm repo add incubator [http://storage.googleapis.com/kubernetes-charts-incubator](http://storage.googleapis.com/kubernetes-charts-incubator)helm install incubator/sparkoperator --namespace spark-operator --set enableWebhook=true

使用这个清单在 Kubernetes 默认名称空间中创建 spark 服务帐户和 roll 绑定。

kubectl create -f manifest/spark-rbac.yaml

运行 Spark Pi 示例来测试安装。这将在 Kubernetes 中创建两个 Spark pods:一个用于驱动程序,另一个用于执行程序。

kubectl apply -f examples/spark-pi.yaml

使用 S3A 连接器在 S3 访问数据

多亏了 Spark 操作者,通过几个命令,我能够部署一个简单的 Spark 作业在 Kubernetes 上运行。我的下一个任务是在我的 Spark 工作中访问 S3 的数据。Hadoop 的 S3A 连接器针对亚马逊 S3 和兼容的对象存储实现(包括 FlashBlade S3)提供高性能 I/O。

使用最新的 S3A 连接器构建 Docker 映像

Spark Operator 使用来自 Google Cloud 的预建 Spark docker 映像。但是,该图像不包括 S3A 连接器。虽然可以定制和添加 S3A,但默认的 Spark 映像是针对 Hadoop 2.7 构建的,众所周知,Hadoop 2.7 的 S3A 实现效率低且速度慢。所以我决定用 Spark 和最新的 S3A 连接器构建自己的 Docker 映像。

我将省略构建过程的细节,因为它很简单,但关键是使用预构建的 Spark-without-Hadoop 二进制文件和用户提供的 Hadoop。我的 Docker 文件可以在 my Github 上找到。

我的 Docker 映像包含 Spark 2.4.5、Hadoop 3.2.1 和最新的 S3A,可从 Docker Hub 获得:

docker pull uprush/apache-spark:2.4.5

S3A 连接器配置

Spark 在 S3 访问数据的最低 S3A 配置如下:

"spark.hadoop.fs.s3a.endpoint": "192.168.170.12"
"spark.hadoop.fs.s3a.access.key": "S3_ACCESS_KEY"
"spark.hadoop.fs.s3a.secret.key": "S3_SECRET_KEY"
"spark.hadoop.fs.s3a.connection.ssl.enabled": "false"

在常规的 Spark 集群中,如果运行 Hadoop,这将放在spark-default.conf,core-site.xml文件中。对于 Spark 操作符,它被配置在应用程序 YAML 文件的spec.sparkConf部分下。参考 API 文档获取火花操作器 API 的完整描述。

火花和 S3:爱狗人士的例子

一个工作示例是展示其工作原理的最佳方式。这里有一个示例代码,用于在 S3 从名为 DogLover 的 Spark 程序中读写数据。我使用 twitter API 从 twitter 上收集了爱狗人士的推文,并将它们作为 JSON 文件存储在 FlashBlade 的 S3 桶中。DogLover Spark 程序是一个简单的 ETL 作业,它从 S3 读取 JSON 文件,使用 Spark Dataframe 进行 ETL,并将结果作为 Parquet 文件写回 S3,所有这些都通过 S3A 连接器完成。

为了管理 Kubernetes 中 Spark 应用程序的生命周期,Spark 操作者不允许客户直接使用spark-submit来运行作业。相反,我将 jar 文件上传到 S3,在我的doglover.yaml spec 文件中,我让 Spark 操作员从那里下载并在 Kubernetes 上运行程序。

spec:
  type: Scala
  mode: cluster
  image: "uprush/apache-spark:2.4.5"
  imagePullPolicy: Always
  mainClass: com.uprush.example.DogLover
  mainApplicationFile: "s3a://deephub/doglover/doglover_2.12-0.1.0-SNAPSHOT.jar"
  sparkVersion: "2.4.5"

然后,我可以像这样提交 Spark 作业:

kubectl create -f doglover.yaml

几秒钟后,我的 Spark 作业在 Kubernetes 上运行。

在 Kubernetes 上运行的 Spark 作业

一旦作业完成,我们应该在输出 S3 目录中看到一个零字节的_SUCCESS文件和多个拼花文件。

我的建筑看起来是这样的:

阿帕奇与库伯内特和 S3 的火花

在我看来,这是一种更好的提交 Spark 作业的方式,因为它可以从任何可用的 Kubernetes 客户端提交。它让我的工作更少依赖于基础设施,因此更便于携带。例如,要在 AWS 中运行相同的作业,我可以首先使用 FlashBlade 对象复制将我的数据从 FlashBlade S3 复制到亚马逊 S3。然后,我可以在 AWS cloud 的 Kubernetes 集群中以同样的方式轻松运行同样的 Spark 作业。

快速 S3 与 S3A 提交者一起写作

当使用 S3 时,Spark 依靠 Hadoop 输出提交器将输出可靠地写入 S3 对象存储。传统的 FileOutputCommitter 是为 HDFS 设计的,因此当与 S3 一起使用时,它被认为是低效、缓慢和不太可靠的,因为它依赖于原子的“重命名”HDFS 操作,而这对于对象存储是不可用的。在 Hadoop 3 中,新的“零重命名”S3A 提交器通过利用云原生 S3 特性来解决这些问题。查看更多关于与 S3A 提交者一起将工作提交给 S3 的信息

网飞贡献了一个名为 Staging Committer 的 S3A committer,它有许多吸引人的特性

  • 对目标对象存储没有任何要求。
  • 已知有效。

提交器将任务输出写入本地文件系统上一个名为任务尝试目录的临时目录。在任务提交时,提交者枚举任务尝试目录中的文件。使用多部分上传 API 将每个文件上传到 S3。提交上传所需的信息保存在 HDFS 暂存目录中,并通过该协议提交:当作业提交时,成功任务的未决上传部分将全部提交。

Kubernetes 上 Spark 的最低 S3A 阶段提交器配置(不含 HDFS):

"spark.hadoop.mapreduce.outputcommitter.factory.scheme.s3a":"org.apache.hadoop.fs.s3a.commit.S3ACommitterFactory"
"spark.hadoop.fs.s3a.committer.name": "directory"
"spark.sql.sources.commitProtocolClass": "org.apache.spark.internal.io.cloud.PathOutputCommitProtocol"
"spark.sql.parquet.output.committer.class": "org.apache.spark.internal.io.cloud.BindingParquetOutputCommitter"

S3A 登台提交器的永久卷

事实上,暂存目录不一定要在 HDFS,它也可以是所有 Spark pods 共享的 NFS 卷。在我的情况下,我使用 NFS,因为我不想有任何 HDFS 依赖。

通过使用Pure Service Orchestrator(PSO),创建一个分段 PV 并将其安装到所有 Spark pods 很容易。

要将 FlashBlade NFS 用作 PV,请创建一个staging-pvc.yaml规范文件并将存储类指定给pure-file

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: data-staging-share
spec:
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 1Ti
  storageClassName: **pure-file**

应用等级库文件创建 PVC。

kubectl create -f staging-pvc.yaml

然后,我创建一个 PV,并将其安装到提交者工作目录下的所有 Spark pods 中,在我的例子中是/home/spark/tmp

spec:
  volumes:
    - name: "staging-vol"
      persistentVolumeClaim:
        claimName: data-staging-share
  driver:
    volumeMounts:
      - name: "staging-vol"
        mountPath: "/home/spark/tmp"
  executor:
    instances: 2
    volumeMounts:
      - name: "staging-vol"
        mountPath: "/home/spark/tmp"

最后,我配置 S3A 来使用这个 PV。

"spark.hadoop.fs.s3a.committer.tmp.path": "file:///home/spark/tmp/staging"

缓冲目录的永久卷

这不是必需的,但是通常最好使用 PV 作为 S3A 提交者的缓冲区目录。缓冲区目录是提交者正在写入的数据的本地文件系统目录。因为暂存提交器将其输出写入本地文件系统,并且仅在任务提交时上传数据,所以确保有足够的本地存储空间来存储主机上运行的所有未提交任务生成的输出非常重要。小型主机/虚拟机可能会耗尽磁盘空间。为了避免这种情况,我将 S3A 配置为对缓冲区目录使用与上面相同的 PV。即使是远程存储,这里的性能也没有问题,因为 FlashBlade 非常快。

"spark.hadoop.fs.s3a.buffer.dir": "/home/spark/tmp/buffer"

有了这些,我可以使用高效、快速和可靠的 S3A staging committer 将数据从 Kubernetes 上运行的 Spark 写入 S3。

S3A 提交者的爱狗者示例

通过以上设置,我的架构变为:

Apache 与 Kubernetes 和 S3A Committer 的火花

将以上所有内容放入 doglover.yaml spec 文件,重新运行作业。与之前不同的是,这次作业创建的_SUCCESS文件不是零字节。它包含来自 S3A 提交器的指标和计数器。

cat _SUCCESS
{
  "name" : "org.apache.hadoop.fs.s3a.commit.files.SuccessData/1",
  "timestamp" : 1588673361792,
  "date" : "Tue May 05 10:09:21 UTC 2020",
  "hostname" : "doglover-driver",
  "committer" : "directory",
  "description" : "Task committer attempt_20200505100916_0000_m_000000_0",
  "metrics" : {
    "stream_write_block_uploads" : 0,
    "files_created" : 1,
...

这表明 S3A 提交器的配置是正确的,Spark 可以更有效地写 S3。请参考我的卡片组了解 S3A 提交者及其表演角色的详细信息。

结论

通过 Kubernetes 上的 Spark,并通过将数据放入 S3,我能够以一种可移植的方式轻松快速地上下旋转 Spark jobs。我还能够在同一个 Kubernetes 集群中使用相同的 FlashBlade 存储运行我的 Spark 作业以及许多其他应用程序,如 PrestoApache Kafka 。我不需要为所有这些管理 Hadoop 集群。Kubernetes、FlashBlade 和 PSO 共同带来了一个简单、可扩展和高性能的分解解决方案,用于运行现代分析系统,如 Spark like a service。

将 Spark 与 Kubernetes 和 S3 一起使用的好处肯定是巨大的,但是,它也有局限性。这种架构非常适合 ETL 批处理等一次性工作负载类型。虽然,对于商业智能(BI)和笔记本后端之类的东西来说,它可能不是最好的 Spark 架构,因为我找不到一种简单的方法来通过 Spark 操作符保持 Thrift 服务器或 Spark 会话运行。但是我知道在 Kubernetes 上有更好的方法来实现这些,比如 Spark 和其他解决方案。敬请关注。

只需要一点火花!

Apache Sqoop

原文:https://towardsdatascience.com/apache-sqoop-1113ce453639?source=collection_archive---------18-----------------------

RDBMS 到 HDFS 并返回

在全球范围内,最流行的数据库是基于 SQL 的。考虑到这一点,对于任何数据湖来说,能够从 RDBMS 数据库中提取数据是至关重要的。于是,Apache Sqoop 诞生了。

Apache Sqoop 高级数据流

Apache Sqoop 支持任何 RDBMS 和 HDFS、Hive 或 HBase 等之间的双向数据移动。但是,仅限于结构化数据。它以批量加载格式工作(类似于 ETL 应用程序中的提取),支持大量数据的提取,达到 RDBMS 根本无法处理的数 TB。

它是如何工作的?

稍加修改的 Apache Sqoop 工作流, Apache Sqoop Wiki

上面的 GIF 是将数据从 RDBMS 加载到 HDFS 的 Sqoop 过程的工作流。反之亦然。它是这样工作的:

发起方:客户端向 Sqoop 服务器提交一个任务,将数据从源加载到目标(在本例中,就是将 RDBMS 加载到 HDFS)。连接池、模式和元数据验证在这个阶段完成。

分割器:现在要提取数据。但是,例如,1 TB 大小的表不能作为一个数据块来处理。因此,我们有一个分割器,其中,数据被分解为可消化的数据块/区块,以便并行提取。请注意,这里没有存储数据。

提取器:在框架的这一部分,数据实际上是以特定的块/块从源移动的。需要注意的是,并不是所有的数据都要一次加载到内存中进行提取。我们可以把它看作是微批量处理。同样,这里没有存储任何数据。

Loader :提取的数据现在通过框架的 Loader 阶段推送到目标。现在数据实际上被存储了。

Cleaner :这只是一个清理活动,以释放所使用的资源。

Sqoop 2.x 是使用 Map Reduce 作为其主要处理框架推出的。 Sqoop with Spark 是一种可能性,可以通过上面讨论的 Sqoop 的即插即用或模块化框架轻松配置。这就是这种通用工作流程的美妙之处/本质。

如果您还没有完成安装,现在是时候了!

[## Google Cloud 上的 Cloudera 管理器

GCP 的 CM 6.3.1

medium.com](https://medium.com/@prathamesh.nimkar/cloudera-manager-on-google-cloud-3da9b4d64d74) [## 安装 MySQL 数据库

安装 MySQL 数据库并加载数据

medium.com](https://medium.com/@prathamesh.nimkar/install-mysql-database-7d64f0207cf9)

将数据从 RDBMS 导入 HDFS

**# Quick check of HDFS folders on instance-1**
sudo su -
hdfs dfs -ls /user/root/projects

让我们使用 Sqoop 自动创建一个名为structuredFlightDataset的新 HDFS 文件夹,并将数据导入其中。

Sqoop 导入命令

  1. sqoop 导入触发对 sqoop 服务器的请求,以启动流程。
  2. connect 参数使用 jdbc 协议接受数据库连接池信息。
  3. driver 参数(可选)接受在源、目标和 sqoop 服务器之间建立连接的默认驱动程序。请注意,这个参数是为流行的 OLTP 数据库(如 mysql、oracle、db2、postgresql 和 sql server)提供的默认值。
  4. 用户名/密码在此输入参数进行授权。
  5. 从参数导入。
  6. 数据将被推送到的目标目录参数。
  7. 使用标准 OOTB mysql 分隔符来处理数据集列类型。

请注意,上面的导入命令图和标签也适用于导出命令,只是有微小的明显区别。

**# Sqoop import from MySQL database on instance-2 to HDFS**
sqoop import --connect jdbc:mysql://instance-2/flight_data --driver com.mysql.jdbc.Driver --username root -P --table flightrepdata --target-dir /user/root/projects/structuredFlightDataset --mysql-delimiters
**# Prompt to enter password**
Enter password:

这将抛出一个小错误,因为 mysql 的 java 连接器在 instance-1 上丢失了。 Cloudera 表示,Sqoop 不附带第三方 JDBC 驱动程序,必须单独安装。此外,它需要一个无头的开放 JDK 和一些对 MySQL 数据库前端的访问权限。

**# Download the rpm for j-mysql-connector (your url may vary)**
wget [https://dev.mysql.com/get/Downloads/Connector-J/mysql-connector-java-8.0.19-1.el7.noarch.rpm](https://dev.mysql.com/get/Downloads/Connector-J/mysql-connector-java-8.0.19-1.el7.noarch.rpm)**# Prerequisite: java-openjdk-headless rpm installed via yum**
yum install java-1.8.0-openjdk-headless**# Install the rpm now**rpm -ivh mysql-connector-java-8.0.19-1.el7.noarch.rpm**# copy to the required folders**cd /usr/share/java
cp mysql-connector-java.jar /var/lib/sqoop/**# Without adding to oozie, it would throw a java exception
# Oozie is an orchestration tool** sudo -u hdfs hadoop fs -copyFromLocal mysql-connector-java.jar /user/oozie/share/lib/lib_20200429072044/sqoop/

现在,我们需要在 MySQL 上授予来自远程服务器/主机的访问权限:

**# Login to instance-2 and make the changes in mysql config file**ssh instance-2
vim /etc/my.cnf**# Change/add the binding address, save & close the file**
bind-address = 0.0.0.0**# Restart mysql services**
service mysqld restart

登录 MySQL,创建具有特定 IP 的用户,并提供适当的授权。请注意,MySQL 不再允许直接从 GRANT 命令创建用户。只要主机名不同,用户名和密码可以相同。
快速提示:主机名可以在这里找到cloud era Manager->Host->所有主机

CREATE USER ‘username’@’instance1_hostname’ IDENTIFIED BY ‘new_password’;
CREATE USER ‘username’@’instance2_hostname’ IDENTIFIED BY ‘new_password’;
CREATE USER ‘username’@’instance3_hostname’ IDENTIFIED BY ‘new_password’;
CREATE USER ‘username’@’instance4_hostname’ IDENTIFIED BY ‘new_password’;
**# Now let's grant the required privileges:**
GRANT ALL PRIVILEGES ON *.* TO ‘username’@’instance**1**_hostname’;
GRANT ALL PRIVILEGES ON *.* TO ‘username’@’instance**2**_hostname’;
GRANT ALL PRIVILEGES ON *.* TO ‘username’@‘instance**3**_hostname’;
GRANT ALL PRIVILEGES ON *.* TO ‘username’@‘instance**4**_hostname’;
**# Flush the privileges to "activate" them:**FLUSH PRIVILEGES;
**# Quick test:**
SELECT * from information_schema.user_privileges where grantee like “‘username’@’instance**1%**’”;**# Repeat for all other hosts**

完成后,您可以重新运行 Sqoop import 命令。首先,让我们深入分析日志:

Apache Sqoop 日志的详细浏览

让我们检查导入 HDFS 的数据:

hdfs dfs -ls /user/root/projects/structuredFlightDataset

这应该给你 5 个文件。您可以从 HDFS 的 Name Node Web UI 或 Hue 的 Web UI—http://instance-1:8889/查看。您可能需要下载文件才能查看。请注意 _SUCCESS 文件不包含任何内容,它只是一个标志值。part-m-00000/1/2/3文件以一个 csv 格式的实际数据出现。

为了控制创建和执行多少并行流程(也称为映射器/分割),我们可以这样调整导入命令:

***# Sqoop import using** 1 mapper **only**
sqoop import --connect jdbc:mysql://instance-2/flight_data --driver com.mysql.jdbc.Driver --username root -P --table flightrepdata --target-dir /user/root/projects/structuredFlightDataset2 --mysql-delimiters **-m 1***

映射器数量参数只是对纱线的建议。YARN 可能会选择完全忽略这个建议。**

***# Pretty much the same logs using a single mapper except:**
INFO mapreduce.ImportJobBase: Transferred 57.1199 MB in 38.2969 seconds (1.4915 MB/sec)*

它给出了两个输出文件,即 part-m-00000 和 _SUCCESS 标志。你会注意到它有点慢,但这只是 60 万记录。想象一下,如果是 1 亿条记录,性能会有多大的不同。

既然映射器是线程,那么如果您提供两倍于主机的映射器会怎么样呢?这能进一步提高性能吗?

*sqoop import --connect jdbc:mysql://instance-2/flight_data --driver com.mysql.jdbc.Driver --username root -P --table flightrepdata --target-dir /user/root/projects/structuredFlightDataset3 --mysql-delimiters **-m 8
# Notable differences in the log generated:**
INFO db.IntegerSplitter: Split size: 75918; Num splits: 8 from: 1 to: 607346
INFO mapreduce.JobSubmitter: number of splits:8
INFO mapreduce.Job:  map 0% reduce 0%
INFO mapreduce.Job:  map 13% reduce 0%
INFO mapreduce.Job:  map 25% reduce 0%
INFO mapreduce.Job:  map 50% reduce 0%
INFO mapreduce.Job:  map 75% reduce 0%
INFO mapreduce.Job:  map 88% reduce 0%
INFO mapreduce.Job:  map 100% reduce 0%
INFO mapreduce.ImportJobBase: Transferred 57.1199 MB in 38.247 seconds (1.4934 MB/sec)*

正如您将注意到的,所用的时间没有差异,但它会产生 8 个输出文件(part-m-00000–8)和 1 个 _SUCCESS 标志。

最好保持映射器的数量与数据节点的数量相同,处理可以/将要在数据节点上运行。

这里出现了一个有趣的问题,我们能否使用 SQL 查询将数据加载到 HDFS 中?为什么是的,当然我们可以。事实上,我们可以让复杂的查询按照我们认为合适的方式连接多个表。

额外提示:该查询被提交到数据库层,因此,如果您可以在该层过滤所需的数据,就可以节省下游的 Map Reduce 或 Spark 处理。但是,这不是最佳实践。数据湖的概念是首先获取所有的原始数据。

*sqoop import --connect jdbc:mysql://instance-2/flight_data --driver com.mysql.jdbc.Driver --username root -P --target-dir /user/root/projects/structuredFlightDataset3 --mysql-delimiters --query **"SELECT UID, OP_UNIQUE_CARRIER FROM flightrepdata_exp WHERE 1=1 AND FL_DATE = '2020-01-01'"** -m 2*

从 RDBMS 到 HDFS 的增量导入

每天从 OLTP 数据库批量加载需要能够执行增量加载,即只加载上次执行的增量。基本上有两种方法可以实现这一点:

LastModified

***# Let's create a last modified date column** ALTER TABLE `flightrepdata` 
ADD `LAST_MODIFIED_DATE` TIMESTAMP NOT NULL DEFAULT NOW();
**# Quick import into HDFS** sqoop import --connect jdbc:mysql://instance-2/flight_data --driver com.mysql.jdbc.Driver --username root -P --table flightrepdata --target-dir /user/root/projects/structuredFlightDataset4 --mysql-delimiters
**# Quick HDFS Check** hdfs dfs -ls /user/root/projects/structuredFlightDataset4
**# Quick insert now** INSERT `flightrepdata` 
SELECT NULL,
FL_DATE,
OP_UNIQUE_CARRIER,
ORIGIN_AIRPORT_ID,
ORIGIN_AIRPORT_SEQ_ID,
ORIGIN_CITY_MARKET_ID,
ORIGIN_CITY_NAME,
DEST_AIRPORT_ID,
DEST_AIRPORT_SEQ_ID,
DEST_CITY_MARKET_ID,
DEST_CITY_NAME,
DEP_TIME,
ARR_TIME,
NOW()
FROM `flightrepdata`
WHERE 1=1
AND uid IN (1, 2, 3, 4, 5);
COMMIT;
**# Quick data test for the 5 new records** SELECT uid, OP_UNIQUE_CARRIER, LAST_MODIFIED_DATE FROM flightrepdata ORDER BY 1 DESC LIMIT 5;+--------+-------------------+---------------------+
| **uid** | **OP_UNIQUE_CARRIER** | **LAST_MODIFIED_DATE** |
+--------+-------------------+---------------------+
| 607352 | EV                | 2020-04-27 16:49:36 |
| 607351 | EV                | 2020-04-27 16:49:36 |
| 607350 | EV                | 2020-04-27 16:49:36 |
| 607349 | EV                | 2020-04-27 16:49:36 |
| 607348 | EV                | 2020-04-27 16:49:36 |
+--------+-------------------+---------------------+*

现在,对于“lastmodified”模式下的增量导入,我们需要提供以下参数:
—增量 —实例化增量提取过程及其模式
—检查列 —应检查日期的日期列
—最后值 —应提取数据的值位
—追加 —追加新数据

*sqoop import --connect jdbc:mysql://instance-2/flight_data --driver com.mysql.jdbc.Driver --username root -P --table flightrepdata --target-dir /user/root/projects/structuredFlightDataset4 --mysql-delimiters --incremental lastmodified --check-column LAST_MODIFIED_DATE --last-value "2020-04-27 16:49:36" --append
**# Additional logs** INFO mapreduce.ImportJobBase: Transferred 611 bytes in 26.6098 seconds (22.9615 bytes/sec)
INFO mapreduce.ImportJobBase: **Retrieved 5 records**.
INFO util.AppendUtils: **Appending** to directory structuredFlightDataset4
INFO util.AppendUtils: Using found partition 4
**# Quick HDFS Check** hdfs dfs -ls /user/root/projects/structuredFlightDataset4*

追加

***# Let's run the same INSERT query as above and test** SELECT uid, OP_UNIQUE_CARRIER, LAST_MODIFIED_DATE FROM flightrepdata ORDER BY 1 DESC LIMIT 5;
+--------+-------------------+---------------------+
| **uid** | **OP_UNIQUE_CARRIER** | **LAST_MODIFIED_DATE** |
+--------+-------------------+---------------------+
| 607359 | EV                | 2020-04-27 17:05:19 |
| 607358 | EV                | 2020-04-27 17:05:19 |
| 607357 | EV                | 2020-04-27 17:05:19 |
| 607356 | EV                | 2020-04-27 17:05:19 |
| 607355 | EV                | 2020-04-27 17:05:19 |
+--------+-------------------+---------------------+*

现在,对于“追加”模式下的增量导入,我们需要提供以下参数:
增量 —实例化增量提取过程及其模式
检查列 —需要检查值的列
最后一个值 —需要拾取数据的值位

***# Incremental Import** sqoop import --connect jdbc:mysql://instance-2/flight_data --driver com.mysql.jdbc.Driver --username root -P --table flightrepdata --target-dir /user/root/projects/structuredFlightDataset4 --mysql-delimiters --incremental append --check-column uid --last-value 607352
**# Additional Logs** INFO tool.ImportTool: **Lower bound value: 607352**
INFO tool.ImportTool: **Upper bound value: 607359** INFO mapreduce.ImportJobBase: Transferred 611 bytes in 25.496 seconds (23.9646 bytes/sec)
INFO mapreduce.ImportJobBase: **Retrieved 5 records.**
INFO util.AppendUtils: **Appending** to directory structuredFlightDataset4
INFO util.AppendUtils: Using found partition 9*

将数据从 HDFS 导出到 RDBMS

Sqoop 不会自动创建一个表,因此我们必须创建一个具有底层结构(即列和数据类型)的表。

***# Login to instance-2 MySQL and appropriate database instance**
use flight_data
**# Create the table**
CREATE TABLE flightrepdata_exp
(UID INT PRIMARY KEY, 
FL_DATE DATE,
OP_UNIQUE_CARRIER VARCHAR(10),
ORIGIN_AIRPORT_ID INT,
ORIGIN_AIRPORT_SEQ_ID INT,
ORIGIN_CITY_MARKET_ID INT,
ORIGIN_CITY_NAME VARCHAR(300),
DEST_AIRPORT_ID INT,
DEST_AIRPORT_SEQ_ID INT,
DEST_CITY_MARKET_ID INT,
DEST_CITY_NAME VARCHAR(300),
DEP_TIME INT,
ARR_TIME INT);
**# Sqoop export to MySQL database on instance-2 from HDFS**
sqoop export --connect jdbc:mysql://instance-2/flight_data --username root -P --export-dir /user/root/projects/structuredFlightDataset/ --table flightrepdata_exp --mysql-delimiters
**# Pretty much the same logs really***

请注意,导出命令也可以在多个映射器上运行。就像导入命令一样,最好限制映射器数量=主机数量

优步模式

MapReduce 作业的 Mapper 和 Reducer 任务由 YARN 的资源管理器(RM)在分布于几个节点的两个独立容器中运行。但是,如果您的数据集很小,或者您的作业包含小型制图工具任务,或者您的作业仅包含一个缩减器任务,我们可以将优步模式设置为 TRUE。这迫使 RM 在一个容器或 JVM 中顺序运行 mapper 和 reducer 任务,从而减少了启动新容器和跨多个节点为一个小任务建立网络的开销。工作完成得更快。

如果您在 Sqoop 中遇到任何问题,可以在这里找到日志:
http://:8088/proxy/<YARN 的作业申请 id > / 或者更好的是,所有作业都可以在这里找到: http://:8088/cluster

对于更多的命令,也许你想访问 Apache Sqoop 的用户指南

快速清理:

***# Be a good Samaritan and clean up your workspace**
hdfs dfs -rm -R /user/root/projects/structuredFlightDataset2
hdfs dfs -rm -R /user/root/projects/structuredFlightDataset3
hdfs dfs -rm -R /user/root/projects/structuredFlightDataset4drop table flightrepdata_exp;
ALTER TABLE `flightrepdata` DROP `LAST_MODIFIED_DATE`;
DELETE FROM `flightrepdata` WHERE uid >= 607347; COMMIT;*

参考文献:

[1] Sqoop 用户指南,Apache Sqoop,ASF

[2] 管理 Sqoop 客户端,Apache Sqoop,Cloudera 文档

[3] C. Chaudhari,什么是优步模式? (2018),Cloudera 社区

[4] A. Elmahrek,J. Cecho (2014), Sqoop2 新一代大数据— Apache Sqoop 工作流,Apache 软件基金会

数据集

请注意,我没有下载所有的列来创建数据集。但是你可以。

结构化清洗数据集可以在这里找到。
用一些数据解释?给你。
原始数据集的首页— 此处
原始数据— 此处
字段的更多详细信息— 此处

* [## 阿帕奇纱线和动物园管理员

关于资源分配和高可用性的一切

towardsdatascience.com](/apache-yarn-zookeeper-61e17a958215) [## HDFS 擦除编码

通过利用擦除编码,显著降低 HDFS 集群的存储开销

towardsdatascience.com](/simplifying-hdfs-erasure-coding-9d9588975113) [## 使用 Hadoop 生态系统的大数据分析渠道

登录页面

medium.com](https://medium.com/@prathamesh.nimkar/big-data-analytics-using-the-hadoop-ecosystem-411d629084d3)*

阿帕奇纱线和动物园管理员

原文:https://towardsdatascience.com/apache-yarn-zookeeper-61e17a958215?source=collection_archive---------21-----------------------

Hadoop 中的资源分配和高可用性

纱线结构

建筑和工作

YARN 或“又一个资源谈判者”正如它的名字所表示的那样,它为运行一个任务进行资源谈判。

YARN 就像任何其他 Hadoop 应用一样,遵循“主-从”架构,其中资源管理器是主节点,而节点管理器是从节点。主设备将作业和资源分配给从设备,并监控整个周期。从机接收作业并请求(额外的)资源来完成作业,并实际承担作业的执行。

  1. 客户机向资源管理器(RM)发送一个作业(大多数情况下是 jar 文件)。
  2. RM 包含两个部分,即调度程序和应用程序管理器(AM)。调度器接收作业请求,并请求 AM 搜索可用的节点管理器(NM)。所选的 NM 产生应用程序主机(App Master)。请注意,RM 调度程序只调度作业。它不能监视或重新启动失败的作业。AM 监控作业的端到端生命周期,如果 NM 出现故障,可以重新分配资源。它还可以在 App Master 中重新启动失败的作业。
  3. App Master 检查容器中为作业提供的资源。该作业现在驻留在 App Master 中。请注意,它将作业的状态传达给 AM。
  4. 需要注意的是,只有当应用程序管理员提供了容器启动上下文(CLC)证书时,才能使用 Yarn 容器。它就像一把打开容器资源的钥匙。这是纱线的内部。App Master 作业现在在容器中执行。但是,如果提供的资源不够,那么
  5. App Master 创建一个请求列表
  6. 该列表直接发送给 RM 调度程序(不通过 NM)。RM 再次向 AM 请求更多的资源。
  7. 一个新的容器通过一个新的 NM 启动,没有应用程序主。作业成功执行,资源被释放。

RM 是一个主进程,这是一个单点故障,因此让我们在 GCP 的 Cloudera Manager 设置中为 YARN 的资源管理器添加高可用性。

[## 纱线高可用性—启用/禁用

纱线资源管理器高可用性

medium.com](https://medium.com/@prathamesh.nimkar/yarn-high-availability-enable-disable-59375159c182)

纱线调度器

不同类型的纱线调度器

先进先出调度

顾名思义,先进先出或 FIFO 是 YARN 中提供的最基本的调度方式。目前(很可能)在 Hadoop 3.x 中停止使用,FIFO 将客户端提交的作业放在队列中,并根据先来先服务的原则按顺序执行。

先进先出调度

作业 1、2 和 3 具有不同的存储和内存要求。虽然我们可以一起运行多个任务,但在 FIFO 中,它们将按顺序运行。这就是浪费。因此,这种调度方法在生产/共享集群上并不可取,因为它的资源利用率很低。

由于 FIFO 是按顺序工作的,所以存在大量未充分利用的资源。现实生活中的一个缺点是,由于其顺序排队,客户/用户不得不进行不必要的等待,因此违反了关键的客户服务级别协议。这导致在一个组织内创建私有集群,其中每个部门都从中央系统(数据湖)断开连接以避免等待。这进一步降低了利用率,并大幅增加了运营成本,而这些成本本来是可以节省下来的。

引入了容量调度程序以最大化利用率。

产能调度

产能调度

中心思想是多个部门资助中央集群或“根”,表示为 100%的可用资源,如上图顶部所示。每个部门或“叶子”都保证有一个特定的能力范围,以便在需要时执行其工作,而无需等待。这可以是百分比或绝对数字。这是层次结构中的第二行,其中,最小值得到保证,最大值受到限制,并根据整个群集中可用的空闲资源进行分配。因此,最小范围在每个层级上总是等于 100%。自然,每片叶子的最大值也不能超过 100%。此外,我们总是将每个叶片的最大值限制在 100%以下。80%似乎是生产中首选的最大值。

我们可以通过在层次结构中创建一个新的子级别来隔离叶子,以便更准确地划分资源。这支持详细的容量规划,例如,上图中的“ETL”获得 60%的根资源,即所有资源的 36%作为最低保证。在这一级,最小值加起来也是 100%。最后,如果有 2 个“ETL”作业,优先级基于 FIFO。取决于具体情况的优先级功能是产能调度的一个缺点。

公平调度

您需要理解公平和容量调度是如何工作的,因为公平调度是建立在它的缺点之上的。

如果单个作业正在运行,公平调度(FS)将会投入所有的资源。如果添加另一个作业,它将确保添加“公平”数量的资源来完成该作业。因此,FIFO 调度问题在这里得到了解决。它基本上防止了新工作缺乏资源。

如果进行了配置,FS 还可以执行容量计划,它会自动在叶/部门级别添加资源。此外,它可以根据可在叶或作业级别添加的“权重”或优先级来确定作业的优先级,从而实现最大的容量利用率。

中心思想是确保所有的作业都能获得“公平”的资源来成功执行。当有 2 个工作,其中一个是小工作时,尤其如此。即使小作业属于同一个叶/队列/部门,小作业也会获得足够的资源来执行,而不是被搁置,直到大作业完成。这可以被认为是动态资源平衡。

最后,您可能还应该在 HDFS 上启用高可用性:

[## HDFS 高可用性—启用/禁用

HDFS 哈

medium.com](https://medium.com/@prathamesh.nimkar/hdfs-high-availability-enable-disable-41f31b8824f0)

阿帕奇动物园管理员

动物园管理员是在动物园里管理动物的人。你可能已经注意到,大多数 Hadoop 应用程序图标都以某种方式与动物相关。无论如何,Apache Zookeeper 类似地是一个管理“动物园”(即 Hadoop 生态系统)中的“动物”(即应用程序)的应用程序。

那么,它到底是做什么的?

协调

任何大规模分布式应用的一个痛点是大规模的协调。例如,如果您有 100 个节点,并且您希望在所有节点中实施配置更改,则必须在所有节点上手动执行。
你能想象在 100 个节点上编写配置更改吗?可行吗?什么事?

那么这个怎么样——雅虎一度有 40,000 个 Hadoop 节点。你能想象为他们所有人写一个配置变更吗?你会做吗?

您可能正在考虑编写一段代码,将配置更改部署到所有节点上。虽然这是一个很好的想法,但是也有很多问题,比如对脚本的每一个小的改变都要进行繁琐和频繁的更新/定制,等等。,我们不会深入讨论,但我相信你已经明白了。

这就是 Zookeeper 的用武之地,其中更改/更新可以很容易地应用到所有节点,而不会产生任何开销。它是无缝的,必须在主节点上完成一次,然后自动应用到所有节点。

Zookeeper 的主要用途不是协调。它保持高可用性。

保持高可用性

Apache Zookeeper 基本架构

它通过一个简单的反馈回路做到这一点。Zookeeper 通过其故障转移控制器来监控领导者和追随者/备用节点。

它接收每个节点当前健康/状态的心跳或即时通知。当一个领导者失败时,Zookeeper 会立即选举一个备用节点作为新的领导者。选举完成后,新领导人向底层应用程序发送消息,应用程序向新领导人“报告”该消息。

这就把我带到了阿帕奇纱线和动物园管理员的结尾。

有问题吗?不要犹豫地问!

参考资料:

[1]Apache Hadoop YARN(2019),Apache Hadoop,ASF

[2] 项目描述— Apache Zookeeper ,Apache Hadoop,ASF

[## GCP 上的 Cloudera Manager 启动/关闭

断断续续地

medium.com](https://medium.com/@prathamesh.nimkar/cloudera-manager-startup-shutdown-869482034f0) [## 使用 Hadoop 生态系统的大数据分析渠道

登录页面

medium.com](https://medium.com/@prathamesh.nimkar/big-data-analytics-using-the-hadoop-ecosystem-411d629084d3)

API 作为一种产品。当你只知道一个后端时,如何销售你的作品

原文:https://towardsdatascience.com/api-as-a-product-how-to-sell-your-work-when-all-you-know-is-a-back-end-bd78b1449119?source=collection_archive---------5-----------------------

关于如何在不管理网站、服务器、用户和支付的情况下发布和销售代码的指南。前期成本为零。

戴维·兰格尔在 Unsplash 上的照片

在这篇文章中,我将讲述我通过 API 市场开发、部署和销售我的 API 的经历。我不必建立一个网站,也不必考虑如何整合支付处理解决方案。我只是写了代码并部署了它。

创业需要团队。一个由几个各行各业的人组成的团队:程序员、市场营销、销售。而且,这是一条漫长而令人疲惫的道路,因此,成功的机会很低。

你不需要启动一个公司来开始你自己的事情。我相信有一种方法可以在只做后端工作的同时做出产品。

在过去的一年里,我想出了一个计划,如何发布我自己的产品,而不必处理用户管理和/或支付处理。

这是一个 3 步程序:

  1. 制作一个解决问题的 API
  2. 使用无服务器架构进行部署
  3. 通过 API 市场分发

1.制作一个解决问题的 API

大约 6 个月前,我不得不执行一个“程序”,从网上发布的新闻文章中提取信息。一个脚本,将文章 URL 作为输入,并返回结构化记录,如标题、出版日期、作者、媒体链接等。

我的功能的可视化。【https://kuchaiev.com/

我花了几分钟才发现有一个 Python 库可以完成所有这些工作。再花几分钟编码,我就有了一个工作原型。

一旦你有了做某事的代码,把它包装成 API 并不困难。可能困难的是部署和托管它。

2.使用无服务器架构进行部署

只是一个无服务器功能

我不得不每小时几次处理数百篇文章。都在同一时刻。需要分析 200-800 个新闻 URL 的高峰,然后在不确定的时间内没有任何东西。

我们已经在 AWS 上构建了我们的解决方案,所以我认为 AWS Lambda 应该是这种情况下的最佳选择。

AWS Lambda 是一个无服务器的功能即服务工具,它运行您的代码来响应事件。您不必维护服务器。您只需为执行该功能的时间付费。

我部署了 Lambda 函数。它的工作方式与预期的一样:它通过并发调用来处理数百个并发调用。而且,你只需为它被执行的时间付费。

那一刻我想:“嗯,这很容易。有没有公司卖这样的 API?”

谷歌搜索“文章提取 API”

是的,他们存在。他们似乎在做和我做的副业项目完全一样的事情。

这是好消息,因为:

  1. 这种东西有市场
  2. 我有几个例子来比较

无服务器 API

现在,我们必须将无服务器功能转换为无服务器 API。

我使用了 Zappa python 包,它为我完成了所有繁重的工作。

我用 Flask 写了一个 API,然后通过 Zappa 用 AWS Lambda 和 API Gateway 部署。完整的 API 代码在我的 GitHub 页面上开源。

另一个很棒的包是无服务器,它可以部署你的无服务器代码,可以与许多编程语言和云提供商一起工作。

https://www.cbc.ca/dragonsden/pitches/column-cozy

平均来说,我的 API 调用 50,000 次的 AWS 账单大约是 0.6 到 0.8 美元 T11。不包括自由层!

类似解决方案的 50,000 个 API 调用在 30-250 美元范围内

所以,问题是,我应该暗示的其他成本是什么,来分发、推广,更重要的是,为我的 API 收费。

最有可能的是,这不足以让我变得富有,但足以支付我在网飞、Spotify、Leetcode 和其他网站的订阅费用。

3.通过 API 市场分发

根据 RapidAPI :

API 市场的工作方式与其他在线市场相同,允许提供商列出 API,开发者使用它们。像其他类型的市场一样,典型的 API 市场有几个组件,包括开发者门户和 API 提供商门户。

免责声明:在我写这篇文章的时候,我还没有加入 RapidAPI 从 RapidAPI 获得任何特别的折扣。我选择了这个平台,因为我相信它是最适合我的。

为了在 RapidAPI Marketplace 上发布我的 API,我遵循了以下步骤:

  1. 我用 API Gateway 和 Lambda 在 AWS 上部署了一个 API
  2. 在 api 网关上创建了一个 x-api-key 来限制访问
  3. 将我的 API 连接到 RapidAPI 市场
  4. 与 RapidAPI 共享了 x-api-key

当最终用户调用 RapidAPI endpoint(从我的 API 页面)时,它负责从用户的计划中扣除令牌。然后,RapidAPI 用我提供的 x-api-key 调用我的 API。

不管有多少不同的用户通过 RapidAPI 使用我的 API。对于我的后端,总是用相同的 x-api-key 进行相同的调用。

因此,我不必以任何方式管理用户:付款收集、使用计划、使用仪表板—所有这些都不再是我的负担。

价格是任何交易的 20%。没有交易,就没有报酬。例如,如果我以 50 美元的价格出售每月 100,000 次通话的计划,我将获得其中的 80%(40 美元)。

如果没有人购买任何计划,我就不必向 RapidAPI 付费。

发布会

请随意访问我的 API 的页面,在那里您可以免费测试它。

在我的 GitHub 库上获得 API 的完整代码。包括如何设置它的描述。

我今天(2020 年 4 月 20 日)在 ProductHunt 上,所以有很大的机会看看这样的产品是否能得到社区的关注。

你可以做的 API

很可能,你已经有了函数本身。这可能是一个简单的事情,结合了几个 Python 库(像我的)。如果您认为您认识的任何开发人员都可以复制您的代码,这并不重要。仍然有成千上万的人可能愿意购买它。对他们来说,付钱给你比写代码、部署和维护代码更便宜。

许多开发人员都需要的 API 列表:

  • 文本摘要 API
  • 文本释义 API
  • 文本分类 API
  • 图像分类 API
  • 用于从文本/网页中提取和标准化位置的 API
  • API 从任何网址抓取电子邮件

您使用开源库来处理核心功能。组合它们并获得唯一的代码。

此外,如果你已经开发了这样的代码并使用过,那么你就知道你的受众,这对产品开发来说是非常好的。

交付端到端解决方案

让你的简历脱颖而出的最好方法是展示你有能力做出最终决定。寻找作为软件工程师/数据科学家/数据工程师/后端工程师的第一份工作可能非常困难,但是拥有自己完全发布和维护的 API 应该会让你进入前 1%。

许多招聘人员会更愿意打开你的产品页面,而不是你的 GitHub 库。

结论

我在 API 市场上发现的 API 大多是由公司生产的,而不是个人。公司生产的软件产品更不容易出错。但是,价格高。从事自举项目的独立黑客更喜欢看一些不太复杂但便宜的东西。

全球经济正在进入衰退时期。公司的预算将会缩减。那些能够提出现有解决方案的廉价替代品的人应该会看到需求的上升。所以,也许现在是时候了。

About meMy name is Artem, I build [newscatcherapi.com](https://newscatcherapi.com/) - ultra-fast API to find news articles by any topic, country, language, website, or keyword.I write about Python, cloud architecture, elasticsearch, data engineering, and entrepreneurship.

区块链上的 API 调用;数据收集的最佳实践

原文:https://towardsdatascience.com/api-calls-on-blockchain-best-practice-for-data-collection-11f1fc86a2be?source=collection_archive---------10-----------------------

如何进行 API 调用,HTTP GET/POST 请求,获取外部数据,oracle 是什么,如何编码。以太坊/实体的例子。

原图来自 metamorworks Getty Images Pro

*本文已过时。要了解更多最新信息,您可以查看关于进行 API 调用的 Chainlink 文档

大多数软件工程师在从他们的程序之外获取数据时,会从 API 调用HTTP GET/POST 请求中获取数据。类似地,在区块链上,人们也希望从外部 API 获取数据。本文将教你如何做到这一点!让我们开始吧,这是第一步:

— — — — — —你不能— — —

抓住你了。

实际上,您不能像从普通软件应用程序中获取数据那样从 API 调用中获取数据。然而,为了让我们的智能合约做任何有价值的事情,我们希望它们与外部世界进行交互。那么是什么原因呢?

以太坊区块链被设计成完全确定性的。这意味着,如果我获取网络的全部历史,然后在我的计算机上重放它,我应该总是以正确的状态结束。

由于互联网是不确定的,并且随着时间的推移而变化,所以每次我重放网络上的所有事务时,我都会收到不同的答案。

stack overflow 上的 Tjaden Hess

代替契约直接调用 API,我们需要让它调用一个可以与外界交互的应用程序,并让它在链上记录它的发现。这些被称为 神谕 。Oracles 是与外部世界的数据交互并将其报告回链上的任何应用程序。

如果我们在没有 oracles 的情况下重放这个链,并使用常规的 API 调用,那么 API 调用可能已经改变,我们会得到不同的结果。

感谢技术上的定义更新,但是我如何将我的数据放入我的 solidity 应用程序?

好吧,好吧,让我们进入你来这里的真正目的。在我们的示例中,我们将只尝试获取 ETH 的美元价格。

对于初学者:

现在,如果你对这个混音、可靠性、Chainlink 一窍不通,想要一步一步的指南,请随意查看我更深入的指南:写你的第一个可靠性合同Chainlink 的文档。我们都曾是骗子,😄

对于稍有经验或高级用户,包括混音:

如果您想跟随,只需按下按钮,而不必选择自己的 oracles,您可以跟随所有代码,并在 Remix here (代码包含在超链接中)中与我一起部署它。然而,该代码仅用于演示,您不应该将其用于生产。您应该将 JobIDs 和地址作为参数。你可以在这里和我们一起关注更多产品化的版本。你会注意到少了两个步骤。这是故意的;)

转到 gists 部分。请确保在部署后将 ROPSTEN 链接发送到您的合同,否则,您将得到一个气体估计错误。你可以在这里得到 ropsten 链接。测试时不要发送实际的链接。求你了。求你了。求你了。如果不小心发送了链接,可以使用withdrawLink功能。

另一个注意事项:我倾向于交替使用 oracle 和 node,虽然从技术上来说这是不正确的,但对于本文来说,这两者之间的区别可以忽略不计。

1.天真的方法

大多数区块链工程师开始的第一种方法是找到一种 oracle 技术,给它我们 API 调用的 URL,然后让它报告链上的数据,让我们与之交互。我们可以使用许多神谕来做到这一点。我们将使用链节的原因你很快就会看到。

要使用 Chainlink,我们首先必须选择一个 Chainlink oracle/node。这些是区块链上独立运作的智能合约,让你与世界互动。要找到一个,我们可以去像 Linkpool 的市场这样的节点列表服务。链接,选择一个节点即可。然后,我们必须确保该节点有一个“http Get > uint256”作业。不是每个节点都可以进行 URL 调用并返回一个 Uint256(基本上是一个 int),但是大多数都可以!

我们选择的节点是位于 ORACLE 变量中的地址的一个连接池节点。所有的神奇都发生在requestEthereumPrice函数中。如果你以前学习过 Chainlink 的教程,你会知道这是使用 Chainlink 获取数据的最简单的方法。

这太棒了!我们可以得到数据。如果您只是测试并希望快速开发您的代码,这很好,但是请不要将它用于生产智能合同。为什么?它伴随着一个主要问题:

原始图片来自 spainter_vfx Getty Images Pro

您将从一个 oracle 和一个数据提供者那里获取数据。这不是分散数据吗!你需要在你的 solidity 智能合同中分散数据,否则你会失去应用程序的主要优势。

现在 Linkpool 是最受信任的链接节点服务之一,但是你的应用程序需要无信任。不仅如此,您还从一个来源获取数据,现在您的应用程序中有两个故障点。CryptoCompare 和 LinkPool 都是很大的负担,很容易补救。这是一个值得大谈特谈的观点,我将不再赘述,但让我再重复一遍。

您的智能合约需要永远不会有单点故障,因为该点可能会被贿赂、被黑客攻击、在执行时停止服务,或者许多其他原因使您的智能合约变得一文不值。

那么我们如何解决这个问题呢?

2.自聚集方法

我们现在已经向前迈进了一步,选择了 3 个节点,我们将把我们的 API 调用路由到这 3 个节点,这大大减轻了这种幼稚方法带来的问题。现在为了得到价格,我们添加了一个median函数,它取所有提供的答案的中间值。这样,如果一个或两个给出完全不同的答案,我们更有可能得到 ETH 的真实值。您可以看到添加更多的节点会增加您对每个节点的信任。

理想情况下,我们也会选择不同的数据提供者,但是为了简单起见,我在示例中保留了相同的数据提供者。理想情况下,您甚至希望超过 3 个节点,但至少现在没有单点故障。目前,7 或 21 个节点似乎被认为是“体面的”。为什么是 7 和 21?不知道。

所以这要好得多!我们现在在 solidity 中有分散的数据!尽管有很多代码要处理这个问题,但是有没有更简单的方法将我的 API 调用路由到多个节点呢?

很高兴你问了。

3.使用预协调器

如果这一部分让你感到困惑,可以直接跳到 3.5

这需要一个额外的步骤。你会注意到在 remix 链接中,有一个只有两行代码的文件。

pragma solidity ^0.5.0;import "github.com/smartcontractkit/chainlink/evm-contracts/src/v0.5/PreCoordinator.sol";

就是这样。

这就产生了所谓的服务协议。这需要您给它一个 oracle 地址和作业 id 的列表,并将您的 API 调用分散到所有这些 Oracle,取中间值,并使用简单方法的语法为您处理一切!

部署precoordinator.sol,并在 GUI 中添加您的 oracle 地址、jobIDpayments(10000000000000 = 0.1 LINK)和_minResponses。这是每个 oracle 需要的最小响应数。

假设你有 5 个神谕,只有 2 个回应。如果您将最小响应设置为 3,这将否定您的智能合约的其余部分。这是很理想的,因为如果我们没有足够的神谕来回应,它就不会那么分散。

图片来自作者

启动服务协议后,您可以将服务协议的地址用作您的 Oracle 地址,将服务协议的 ID 用作jobID。您会注意到这一步的语法几乎与天真的方法相同。这样,一旦你用一个 oracle 完成了所有测试,并准备好进入生产阶段,你就可以启动一个服务协议,更改 oracle 和jobID,但保持你的所有代码不变。

如果您发现您正在使用的一些 Oracle 给了您很差的响应,那么使用不同的 Oracle 创建新的服务协议就很容易了。您可以做到这一点,而不必重写任何现有的代码,只需为新的服务协议交换 id。

我不知道你怎么想,但是我喜欢用更少的工作来完成更多的工作。

这一步是你对链式网络的最大控制。您可以选择您的节点、数据提供者,并轻松地集成和更改它们。

关于数据提供者的注意:目前,precoordinator 只能向每个提供者发送相同的数据包,这意味着如果不同数据 API 的 JSON 输出不同,您必须发送不同的复制路径。使用不同的数据提供者发送相同信息的方法是将它们都包装在一个 外部适配器 中,该适配器将它们的输出格式化为相同的格式。请继续关注如何做到这一点的更新!

3.5 使用他人的预协调器合同

有时你必须先学习困难的方法,然后才能学会简单的方法,对吗?
address PreCoordinator= 0x11db7845a757041F5Dad3fAf81d70ebAd394e9A2;

bytes32 constant patrick_service_agreement = 0xcb08d1dbcc1b0621b4e8d4aea6bafc79f456e12332c782b4258c7e83bcedb74c;

您可以在 Ropsten 上使用上面的PreCoordinator地址来部署任意服务协议。您可以用这个地址向我挑选的几个节点发送 httpget 请求。

理想情况下,不要在代码中存储PreCoordinator patrick_service_agreementpayment,而是接受它们作为函数的参数。

如果这让你困惑,我希望在底部得到问题。

4.使用参考合同

许多 oracles 技术也有现成的解决方案。Chainlink 也不例外。他们花时间审查多个节点和数据提供商,以提供大量高使用价格的数据馈送,供任何人使用。

我以前在构建智能合同,聪明地中写过关于它们的文章,它们是简单和去中心化的顶峰。

你可以在他们的订阅页面上找到他们的参考合同列表。唯一使它更集中的部分是 Chainlink 选择了节点列表,这意味着你必须相信 Chainlink 团队的节点选择能力。然而,如果你想要一个预先准备好的答案,去中心化的维护外包给 Chainlink 团队,这是一个巨大的优势。否则,你总是可以建立你自己的链式节点网络来获得你的分散数据!

图片来自链节馈送。您甚至可以查看节点正在响应什么,以获得它们的响应。

另一个最好的部分是,它目前是免费的,由像 T2 合成技术公司 T4 这样的优秀公司赞助。我相信未来这种情况会改变,因为这不是一种可持续的模式。

摘要

类似“位置,位置,位置”原图来自 spainter_vfx Getty Images Pro

咻。这里涉及的内容很多。希望我钻了分权点。区块链的主要优势之一是我们可以拥有不可信的应用程序。

用集中式数据源和 oracles 构建区块链应用程序就像买了一辆自行车,这样你就不必再步行去上班,然后继续骑着自行车步行去上班。

你在一个解决方案上投入了时间,但是仍然没有解决原来的问题,即使你已经有了解决方案。

现在,您已经有了工具并理解了这里的重要性,让我们看看您构建了什么吧!对于所有新的和有经验的区块链工程师来说,有很多方法可以加入。AlphaVHack 是一个面向新工程师的黑客马拉松。你可以加入 Chainlink discord ,用 Udemy 的这个牛逼类构建你的第一个端到端区块链解决方案。或者在下面给我留言,询问如何找到更多关于上面的信息,下次再聊!

觉得我错过了什么吗?你能听懂混音代码吗?什么都不懂?请在下面留下您的问题、评论或见解!

此处所有观点均为本人观点。

关注我上 推特 githubLinkedIn 不和 ,获取更多内容和见解。在这里查看我关于 介质的最新文章

# financer evolution #区块链# ETH # chain link # smart contracts

API 爬网:遵守速率限制

原文:https://towardsdatascience.com/api-crawls-respecting-rate-limits-30cc0f76ca98?source=collection_archive---------33-----------------------

这叫爬行是有原因的。

任何通过网络 API 公开信息的人都有可能对 API 客户端实施速率限制。这降低了拒绝服务攻击的风险,帮助 API 提供商控制和预测他们的基础设施成本,并限制 API 故障的严重性。

爬行通常可以,跑步通常不行。怎么爬得快?

维达尔·诺德里-马西森Unsplash 上拍摄的照片

第一步:了解自己的极限

一些 API 使得调用者很容易检查他们的速率限制。GitHub API 是我最喜欢的例子。它在响应报头中返回速率限制信息,并提供一个 /rate_limit 端点,该端点不计入调用者的速率限制。

对于其他 API,您负责跟踪自己的使用情况。通读 API 文档,注意您将访问的端点上的速率限制。

此外,知道你将如何被处罚超过率限制。搜索谷歌太快会屏蔽超过 24 小时。太快抓取 GitHub 通常会锁定你最多一个小时。

第二步:预算

确定爬网中将涉及多少个进程,计算每个进程在爬网的单个步骤中将进行的速率受限 API 调用的数量,并确定每个进程的预算。

有几种方法可以强制执行预算:

  1. 让你的爬虫知道速率限制。生成每个 crawler 进程,其速率限制与相对于其群组中其余 crawler 的爬行量成比例。当您可以在开始爬网之前轻松地将输入划分到各个爬网程序时,这种方法非常有效。
  2. 使用速率受限队列将作业提供给 crawler 进程。如果每个爬行器进程的输入都是在爬行过程中动态生成的,那么这样做效果很好。
  3. 在出站代理中强制实施速率限制。像 nginx 这样的代理提供了现成的功能。下面是一个如何使用 nginx 来限制 API 请求的例子。

无论你选择做什么,做一些算术,确保不超出你的预算。

第三步:当风险很高时,增加一个关闭开关

有时,超出速率限制的成本高得惊人。如果 API 在您的应用程序中提供了一个关键功能,或者如果它的定价在无法承受的上层,就会发生这种情况。如果是这种情况,实现一个 kill switch,如果你太接近你的速率限制,它会关闭你所有的爬虫。

第四步:当风险很低时,慢慢后退

如果超过 API 速率限制不是世界末日,你的爬虫应该简单地重试他们的 API 调用,增加尝试之间的时间间隔,直到达到一定的尝试次数。这种技术的名称是后退重试。补偿通常是指数的,这意味着每次失败的尝试,API 调用之间的时间间隔都会被放大一个固定的倍数 r > 1 。这篇 AWS 文章是一个很好的战略指南。

这些是需要遵循的简单准则,遵循它们将使您轻松地将 API 抓取扩展到笔记本电脑之外。祝你好运,爬虫伙伴!

Neeraj Kashyap(跟我上Twitter和上GitHub)

用于机器学习平台的 API 网关

原文:https://towardsdatascience.com/api-gateway-for-a-machine-learning-platform-d66bdfe07192?source=collection_archive---------40-----------------------

“不要根据封面来判断一本书”…或者他们是这么说的…在现实世界中,有着神圣的生产环境和截止日期,每个人都根据封面来判断你的平台,也就是它的 API…

斯坦纳·恩格兰在 Unsplash 上拍摄的照片

定义了 AWS 上一个自定义机器学习(ML)平台的 框架层 之后,就该详细说说这个层的一些具体范围了。

本系列文章的范围是框架的模型服务层。换句话说,它的“ API 网关”

本系列文章范围,由作者

这一层包含 4 个解决方案:大使、谢顿核心、普罗米修斯和格拉夫纳。在这个新的系列文章中,我将深入探讨这些解决方案中的每一个。

这将是一次伟大的旅程,所以请和我在一起吧!

1 |等等!你刚才是说“API 网关”吗!你确定不是“集群入口”网关!也可能是“API 管理层”!

这是揭开这个话题神秘面纱的第一步。

当在 Kubernetes 集群(在我的例子中是 EKS)中拥有服务时,在某个时间点,我们希望向外部世界公开其中的一些服务,以便我们的客户端可以与我们的服务进行交互。

公开这些服务意味着两件事:

  • 将外部流量路由到 Kubernetes 集群内部的服务:为了实现这一点,我们需要某种“网关”来接收外部流量,知道这个流量涉及哪个服务,然后将流量定向到那个服务。
  • 管理该流量的路由:在某些情况下,仅仅重定向流量是不够的。我们可能希望通过添加一些身份验证来保护对服务的访问,或者限制对流量所涉及的服务的授权请求的数量(速率限制),或者可能缓存来自该服务的一些响应,以避免为已经看到的请求召回它。

为了正确管理这种流量,出现了三个术语:API 网关、API 管理和入口网关。

我将从定义这些术语开始。

管理 API 流量的新兴术语,作者作者

1.1 |在此之前,什么是 API?

"应用编程接口(API)是一个计算接口,它定义了多个软件中介之间的交互."T1。开发人员使用这个接口以一种定义的、稳定的、文档化的方式访问其背后的服务。

一个 API 的简化视图,作者作者

1.2 | API 网关

面对大量的微服务,每个微服务都有其粒度 API,对于希望与这些微服务交互以获取一些数据的最终客户来说,有两种可能的方式可供选择:

1。 按照正确的顺序使用 API 向每个服务发送请求:每个请求必须遵守每个微服务的适当规范。毫无疑问,这是离用户友好应用程序最远的解决方案,并且只有一个结果:由于这种复杂性,客户永远不会使用我们面向微服务的应用程序。

用于访问数据的多个 API,由作者

2- 在我们所有的微服务前面放一层:这一层将只接收客户的一个请求,并负责处理其余的,以便返回最终的数据。这一层是“ API 网关”。
在这种情况下,API Gateway 就变成了最终客户使用的 API。

API 网关除了能够将客户与面向微服务的应用交互的复杂性隔离开来之外,还可以提供更多高级功能,例如:

  • 安全性 : API 网关提供了一个集中的代理服务器来管理速率限制、僵尸工具检测、认证、CORS 等等。许多 API 网关允许建立一个数据存储库(如 Redis)来存储会话信息。
  • 速率限制和计费:该功能可以是基于配额或使用量的。
  • 减少往返行程:某些 API 端点可能需要跨多个服务连接数据。API 网关可以执行这种聚合,这样客户端就不需要复杂的调用链,减少了往返次数。

更多 API 网关功能在这里

API 网关的简化视图,作者作者

我强烈推荐克里斯·理查森的这篇简短但非常有趣的文章:

1.3 | API 管理

“API 管理与 API 生命周期相关”。它只是创建和发布 web 应用程序编程接口(API)的过程*,执行它们的使用策略,控制访问,培育订户社区,收集和分析使用统计数据,并报告性能。
我会说这是与 API 网关共存的另一块。*

API 管理的简化视图,作者作者

1.4 |集群入口网关

简而言之,集群入口网关是一个具有某些规范的 API 网关:

  • 这是一个集群级 API 网关:每个集群都有自己的入口网关
  • 它不仅抽象了对底层 API(由微服务公开)的访问,还抽象了任何“gRPC 服务、缓存、消息队列、数据库等”

有些人也称之为 API 网关。引入“集群”这个词使它更接近其底层技术:集群、容器、主机、端口等。

集群入口网关的简化视图,作者作者

在基于 EKS 的机器学习平台的情况下,集群入口网关可能是 API 网关层的完美候选。
让我们更加关注这种类型的网关。

2 |从技术上讲,Kubernetes 集群的集群入口网关是什么

大使团队将其称为“Kubernetes ingress”,并对其进行了如下定义:“Kubernetes ingress 是一组路由规则,用于管理外部用户如何访问 Kubernetes 集群中运行的服务**。

这个网关由两部分组成:

  • 负载平衡器:这是一个外部*负载平衡器。它的作用是将外部流量重定向到 Kubernetes 集群。在云环境中,这个负载平衡器是特定于云的(例如 AWS 网络负载平衡器)。*
  • 边缘代理:当处理 Kubernetes 的入口资源时,也称为入口控制器*。这是大脑,它接收来自负载均衡器的流量,并知道调用哪个服务、API、数据库…来响应这个流量。*

来自https://www.getambassador.io/learn/kubernetes-ingress/的入口网关

在本系列文章的其余部分,我将互换使用术语 API 网关和入口网关。

结论

在这篇文章中,我试图为下一篇文章做准备。我首先介绍了这一系列文章的范围。然后,我试图定义我在研究 API 网关的主题时遇到的不同术语,如果没有很好地理解,这些术语可能会产生误导:API 网关、API 管理、入口网关。

在下一篇文章中,我将讨论这个 API 网关层与 AWS 服务和开源解决方案的不同设置。

如果您有任何问题,请通过 LinkedIn 联系我。

[1]https://en.wikipedia.org/wiki/API

[2]https://nordicapis . com/understanding-API-management-API-gateway-and-API-manager/

[3]https://blog . Christian posta . com/micro services/API-gateways-are-go-through-an-identity-crisis/

https://www.getambassador.io/learn/kubernetes-ingress/

API 管理平台如何增强您的数据科学项目

原文:https://towardsdatascience.com/api-management-platform-data-science-projects-43120558a000?source=collection_archive---------69-----------------------

数据科学家经常在他们的项目中使用 API。需要 API 管理平台来充分利用这些 API。

来源: Freepik Premium

数据科学家经常使用 API(应用编程接口)来实现高级功能,而不必从头开始构建,从外部源导入数据,公开数据供用户消费,或实现其他目标。

例如,如果您打算创建自己的分类器来对一段音乐的流派进行分类,您可以使用 Spotify API 来完成这项工作。

你将能够利用 Spotify 拥有的海量数据,快速构建分类器,而不必从头开始。在这种情况下,API 为您的数据科学项目提供了正确的解决方案。

而且,为了充分利用 API,需要加强 API 管理。借助 API 管理平台,如Rakuten RapidAPI Enterprise Hub,您可以抽象出创建或使用 API 的大部分日常管理难题,并提高工作效率。

这些解决方案帮助您设计、实现、监控、保护、分析、管理和发展您的 API 程序。

在本文中,我将讨论 API 管理平台如何帮助增强您的数据科学项目。

1。提供集中可见性

API 管理工具将为您提供内部 API 和外部 API 订阅性能的集中可见性。随着您在数据科学项目中使用越来越多的 API,跟踪它们变得越来越困难。

事实上,根据2017 年的一项研究,一家公司平均使用 300 多种不同的 API。有这么多的 API 要处理,需要简化它们的跟踪过程来优化它们的价值和可送达性。

否则,很难评估和改进他们的性能,增强开发人员的体验,或者做出明智的决策。

API 管理解决方案将为您提供增强的可见性和控制,您需要了解 API 的情况。

通过一个集中的位置来管理您的数据科学 API,您可以消除冗余的 API,识别性能不一致,并简化 API 的不同移动部分的编排。

2。改进 API 治理

API 治理是指建立各种控制 API 产品使用的策略的实践。

它涉及广泛的管理、行政、可见性、监控和其他简化和控制 API 采用的措施。

作为一名数据科学家,拥有一个 API 管理平台将让您概括出使您的 API 能够被企业内外的不同角色访问的条款和条件。

这是您实现访问管理和防止未授权使用、进行审计跟踪以发现异常使用趋势以及将 API 列入黑名单或白名单所需要的。

此外,管理您的 API 使得分析和监控它们的性能变得容易。

这些解决方案中的大多数都带有直观的仪表板,允许您快速了解团队使用的所有 API。这样,您就可以评估和维护 API 的健康状况。

你也可以使用这样的平台根据 API 的不同功能来整理 API。例如,如果您有不同的 API 为数据可视化提取数据集,您可以轻松地对它们进行分组并监控它们的性能。

此外,即使一个 API 有多个版本,这样一个平台将允许您毫不费力地跟踪和管理各个版本。

3。增强 API 发现

来源: Freepik Premium

大多数 API 管理平台都有一个内置的开发人员门户,允许用户找到 API,学习如何使用它们,以交互方式测试它们,并最终注册以获得 API 密钥。

这些门户充当与使用给定 API 相关的一切的存储库——从提供文档、代码片段、工具和其他助手资源。

如果您可以使用门户来搜索公共和内部 API,注册这些 API,并将其合并到您的应用程序中,那么您可以极大地提高数据科学项目的能力。

您可以使用 API 管理解决方案来发现新的 API 并节省开发时间、成本和精力,而不必从头开始创建功能。这将让你专注于改善你的核心产品。

更重要的是,利用 API 可以在数据科学项目的大部分关键元素上带来一些改进。根据最近的一项研究,采用 API 的用户实现了生产率(59%)、直接收入(43%)和创新(51%)的显著增长。

因此,找到并连接到 API 的能力是一个重要的特性,您可以利用它来提高项目的效率。

4。巩固 API 安全性

在数据科学应用程序中使用 API 时,安全性应该是一个重要的考虑因素。

目前,随着 API 使用的增加,攻击者正在将他们的注意力从传统目标转移到一个尚未被广泛利用的新领域:应用程序编程接口。

API 非常容易受到攻击,因为它们暴露了计算技术的底层实现。

由于它们提供对数据和服务的编程访问,这也扩大了攻击面,这与其他面向用户的应用程序不同。

根据最近的一份报告,API 漏洞的数量逐年大幅上升,从 2015 年到 2018 年增长了约 154%。

通过管理 API,您可以加强它们的安全性,并实现您最初在数据科学项目中设想的价值。

管理平台可以帮助您最大限度地提高 API 的安全性,防止各种类型的攻击,如中间人、拒绝服务(DoS)、注入或社会工程攻击。

除了认证和授权访问之外,API 管理工具还可以帮助为您的 API 设置各种安全措施。

这些措施包括配置 OAuth 令牌授权系统、制定阻止不良行为者的策略、撤销受损的 API、实现 API 密钥轮换、应用配额和速率限制以及设置访问 API 的权限。

此外,如前所述,这样的平台将增加 API 的可见性。这种增强的可见性使您能够根据正常使用模式分析 API 活动,发现基于奇怪行为的未经授权的入侵,并在损害程度扩大之前尽早识别威胁。

5。实现 API 货币化

API 货币化,顾名思义,指的是从你的 API 中赚取收入的过程。货币化允许 API 提供者发展他们的追求,并与开发者、合作伙伴和客户一起发现新的机会。

根据你的目标,有三种主要的盈利方式可供你选择:免费提供,消费者付费,或者消费者得到报酬。

例如,如果您创建了一个执行情感分析的算法,您可以将其作为 API 公开,并允许其他开发人员使用它来查找文档或文章中表达的观点。

为了通过使用你的情感分析 API 来赚钱,你可以通过向使用它的其他数据科学家收费来赚钱。

大多数 API 管理解决方案都提供了一种简单的方法来货币化和释放 API 产品的价值。

这些平台将为您的 API 提供一个集中的支付中心、实施订阅计划的能力、计费能力等等。

结论

API 广泛应用于数据科学项目中。然而,临时采用 API 通常会导致性能差异、安全问题和生产力降低。

API 管理计划提供了一个单一的事实来源,为您的 API 带来统一性、内聚性和多功能性。

有了管理和控制 API 的平台,您可以优化它们的价值,并充分利用您的数据科学项目。

那么,你正在使用(或计划使用)一个管理 API 的工具吗?

或者,你有什么意见或问题吗?

请把它们贴在下面。

API 私有信息分析器

原文:https://towardsdatascience.com/api-private-information-analyzer-b4e5bdc3f793?source=collection_archive---------81-----------------------

如何识别 API 中的私有信息

隐私就是数据管理。您需要知道您正在处理哪种类型的信息,以及它在后端究竟发生了什么。数据目录可以帮助您了解实际情况,并直观显示数据的变化。详情请阅读我就该主题发表的故事。今天,我想讨论在 API 的环境中如何识别 pii。

基于经济学家

API 来回传递信息,所以您想知道部分信息是否应该被视为私有。为了帮助你完成这项任务,我开发了一个 API 私有信息分析器,你可以用它来分析你完整的 API 负载。该工具将告诉您它在何处识别 pii,并解释它如何将它们分类。

PIIs 如何分类

这是我在设计工具之前必须回答的第一个问题。我应该使用文本分类器,或者命名实体识别(NER)是我们需要的任务吗?我看到了很多用 NLU 方法实现这种分类的尝试,比如 LSTM 或伯特。嗯,通过一个比较 NLU 技术(使用库,如 huggingface/transformers 、LSTM 和基于 BERT 的模型)与传统文本处理方法的性能的简短实验,我能够证明一个显而易见的事实:隐私信息主要按模式分类,较少按上下文分类。因此,基于简单正则表达式操作的解决方案比任何 NLU 分类模型都提供了更好的结果,而且它们显然做得更快。

简单第一

这个简单的实验是一个典型的例子,它表明有时简单、不重要的解决方案胜过最先进的技术。尽管在过去的几年中,随着 transformer 的引入,NLU 空间发生了革命性的变化,我们现在可以做以前从来没有做过的事情,但我们仍然应该小心,只有在传统的文本操作方法无法实现时才使用 NLU 方法。对于 PII 识别,NLU 方法优于传统正则表达式的唯一任务是名称实体识别(比如识别人名)。毫不奇怪,像 BigId 这样的专业工具使用正则表达式作为分类私人信息的第一种方法,聚类和其他花哨的 AI 紧随其后。

隐私 NER

在工具中,我选择使用斯坦福命名实体识别器。可以在这里下载或者在这里解压提供的 Stanford-ner-2018–10–16 . zip。这个 zip 文件包含 NER jar 文件(Stanford-ner.jar)和模型文件(English . all . 3 class . dist sim . CRF . ser . gz)。使用 NER 发动机不需要其他安装。这不是一个新的模型,它基于条件随机场(CRF)序列模型,但对于在隐私环境中识别人们姓名的任务来说,它表现得很好,并且比其他基于现代 BERT 的实现要轻得多。如果你想深入了解命名实体识别领域,我推荐下面的帖子作为起点。

API 私有信息分析器

工具收集输入条目;每个都包含 API 调用的所有数据元素(即请求和响应的 URI、头、有效负载)。它通过使用正则表达式、可疑属性名称黑名单和命名实体识别(NER)引擎来分析数据并搜索可能的私人信息(PII)。结果是一个 CSV 或 JSON 文件,其中包含每个 API 的调用分析,突出显示了所有可能的私有字段及其值。您可以从命令行使用该工具,也可以从 python 代码中调用它。

结论

并非所有的文本操作都需要 NLU 重炮。通常,简单就是胜利。如果您需要快速分析 API 的隐私问题,请查看 API 隐私信息分析器。它将告诉您您的 API 是否涉及应该被视为私有数据的数据,这意味着您必须确保您拥有管理此类私有数据的相关工具/功能。

posted @ 2024-10-15 13:45  绝不原创的飞龙  阅读(300)  评论(0)    收藏  举报