TowardsDataScience-博客中文翻译-2016-2018-一百九十六-

93 阅读1小时+

TowardsDataScience 博客中文翻译 2016~2018(一百九十六)

原文:TowardsDataScience Blog

协议:CC BY-NC-SA 4.0

用 Python 绘制地理数据

原文:towardsdatascience.com/mapping-geo…

在数据科学领域工作时,一个很大的帮助是在地理地图上可视化您的数据,为此,有几个包可以处理它,例如 GeoPandas

你可以学习如何使用 GeoPandas,阅读我的文章:圣地亚哥的街道有多安全

有时安装 Geopandas 包可能很复杂,这取决于您工作的环境。或者,简单地说,你需要控制你的代码!因此,在这篇文章中,我们将探索如何使用“形状文件”和基本的 Python 库来构建我们自己的“地理地图功能”。

1.形状文件

shapefile 格式由 Esri 作为(主要)开放规范开发和管理,在空间上将几何描述为“点”、“折线”或“多边形”。在 OpenStreetMap 术语中,这些可以分别被认为是'节点'、和'闭路'。每个几何体都有一组关联的属性。概括地说,这些有点像 OSM 的标签。

shapefile 实际上是几个文件的组合,这些文件的格式表示地理数据的不同方面:

  • 。shp —形状格式;特征几何本身。
  • 。shx —形状索引格式;要素几何的位置索引,允许快速向前和向后搜索。
  • 。dbf —属性格式;每个形状的列属性,dBase IV 格式。

还有几个 shapefile 格式的可选文件。其中最重要的是。描述所用坐标系和投影信息的 prj 文件。虽然不是 Esri shapefile 标准的一部分。lyr 文件通常包含在内,因为它包含如何在 ArcGIS 软件中显示数据(颜色、标签等)的规范。

更多信息见 维基百科

2.正在安装 Python 形状文件库(PyShp)

Python Shapefile 库(pyshp)为 Esri Shapefile 格式提供读写支持。Shapefile 格式是由 Esri 创建的一种流行的地理 信息系统矢量数据格式。

要安装 pyshp ,请在您的终端中执行以下指令:

pip install pyshp

3.导入和初始化主 Python 库

import numpy as np
import pandas as pd
import shapefile as shp
import matplotlib.pyplot as plt
import seaborn as sns

初始化虚拟化设置

sns.set(style=”whitegrid”, palette=”pastel”, color_codes=True)
sns.mpl.rc(“figure”, figsize=(10,6))

如果您使用的是 Jupyter 笔记本电脑:

%matplotlib inline

4.打开矢量地图

如 1 中所述。,矢量地图是一组由多个文件组成的文件,其中 name.shp 是主文件,用于保存地理要素。重要的是,所有其他文件如' name.shx ',' name.dbf '等。,必须在同一文件夹中。

在本教程中,我们将使用与城市相关的地图(“Comunas”),它们共同构成了圣地亚哥大都市地区。在 INE(智利国家统计局),可以下载一组与地图相关的 shapefiles,这些文件是为最近一次 2017 年全国人口普查创建的:

  • Comuna.cpg
  • Comuna.shp
  • Comuna.dbf
  • Comuna.shp.xml
  • Comuna.prj
  • Comuna.shx
  • Comuna.sbn
  • Comuna.sbx
shp_path = “./Comunas_RM_Mapas_Vectoriales/Comuna.shp”
sf = shp.Reader(shp_path)

让我们检查一下函数 shp 导入了多少不同的“形状”。读者:

len(sf.shapes())

结果将是:52

这意味着在我们的形状文件中存在 52 个形状,一旦圣地亚哥大都会区有 52 个“comunas”,如下图所示(不要担心,在本文结束之前,您将学习如何直接从您的数据创建这样的地图):

让我们也探索其中一个形状(或“记录”):

sf.records()[1]

结果将是一个包含 6 个元素的数组:

Out:['13',
 '131',
 '13115',
 'REGIÓN METROPOLITANA DE SANTIAGO',
 'SANTIAGO',
 'LO BARNECHEA']

元素[5]是“comuna”的名字,在这种情况下:“LO BARNECHEA”,一个位于安第斯山脉所在城市东部的“comuna”(也是我的家!;-)

你可以直接得到它的名字:

sf.records()[1][5]

圣地亚哥大都会区最中心的“comuna”正是圣地亚哥的 Comuna(小迷糊?),在那里你可以找到大都会歌剧院、拉莫内达总统府(73 年被猛烈轰炸)、巴勃罗·聂鲁达故居等。

但是,让我们结束观光,看看圣地亚哥的 comuna 数据结构(id: 25):

sf.records()[25]

Out[]:

[‘13’, ‘131’, ‘13101’, ‘REGIÓN METROPOLITANA DE SANTIAGO’, ‘SANTIAGO’, ‘SANTIAGO’]

我们可以看到一些数据发生了变化,最重要的是“comuna”的名称,也就是现在的“SANTIAGO”。

请注意,您可以将本教程中描述内容应用于任何 shapfile。

5.在 Pandas 数据帧上转换 shapefile 数据

在最后一个例子中,我之前知道 Santiago 的 id 是“25”。但是如何找到这样的 id,从一个 comuna 的名字开始?让我们首先创建一个有用的函数,将我们的“shapefile”格式转换为更常见的熊猫数据帧格式:

def read_shapefile(sf):
    """
    Read a shapefile into a Pandas dataframe with a 'coords' 
    column holding the geometry information. This uses the pyshp
    package
    """
    fields = [x[0] for x in sf.fields][1:]
    records = sf.records()
    shps = [s.points for s in sf.shapes()] df = pd.DataFrame(columns=fields, data=records)
    df = df.assign(coords=shps) return df

因此,让我们转换数据帧上的 sf 数据,看看它是什么样子:

df = read_shapefile(sf)
df.shape

数据帧具有(52,7)的形状。这意味着我们每行有 7 个不同的特征(列)。请记住,我们之前看到了其中的 6 个功能。好像现在多加了一个。让我们看一个例子:

df.sample(5)

最后一列是用于创建特定地图形状的每个点的坐标、纬度和经度。

迷惑?让我们再深入挖掘一下。

我们如何定位圣地亚哥·科穆纳的身份?现在用熊猫很简单:

df[df.NOM_COMUNA == ‘SANTIAGO’]

我们可以很容易地看到,25 正好是数据帧索引,也就是我们的 comuna 形状所在的位置。

通过简单的 Pandas 命令,您可以将索引(或 id)与 comuna 的名称相关联:

df.NOM_COMUNAOut:0 LAS CONDES
1 LO BARNECHEA
2 VITACURA
3 HUECHURABA
4 PUDAHUEL…49 ALHUÉ
50 LAMPA
51 TILTIL

6.绘制特定形状

最后,我们将看到什么是真正的形状。为此,我们应该创建一个函数来绘制它。我们将使用 Python MatPlotLib 库:

def plot_shape(id, s=None):
    """ PLOTS A SINGLE SHAPE """
    plt.figure()
    ax = plt.axes()
    ax.set_aspect('equal')
    shape_ex = sf.shape(id)
    x_lon = np.zeros((len(shape_ex.points),1))
    y_lat = np.zeros((len(shape_ex.points),1))
    for ip in range(len(shape_ex.points)):
        x_lon[ip] = shape_ex.points[ip][0]
        y_lat[ip] = shape_ex.points[ip][1] plt.plot(x_lon,y_lat) 
    x0 = np.mean(x_lon)
    y0 = np.mean(y_lat)
    plt.text(x0, y0, s, fontsize=10)
    # use bbox (bounding box) to set plot limits
    plt.xlim(shape_ex.bbox[0],shape_ex.bbox[2])
    return x0, y0

上面的函数做两件事:a)根据 comuna 的坐标绘制形状(多边形), b)计算并返回特定形状的中点(x0,y0)。这个中点也被用来定义在哪里印刷 comuna 的名字。

例如,对于我们著名的圣地亚哥的 comuna:

comuna = 'SANTIAGO'
com_id = df[df.NOM_COMUNA == comuna].index.get_values()[0]
plot_shape(com_id, comuna)

Santiago — Shape only

注意,我们必须知道形状 id(索引)才能绘制它,但是我们输入了 Comuna 的名称:SANTIAGO。使用 Pandas 很容易计算 id,正如您在前面代码的第二行所看到的。

7.绘制完整的地图

绘制单个形状基本上是为了解决这一小部分代码:

sf.shape(id)

现在,我们必须在同一张图上画出数据帧上的所有形状。为此,我们将使用以下函数:

def plot_map(sf, x_lim = None, y_lim = None, figsize = (11,9)):
    '''
    Plot map with lim coordinates
    '''
    plt.figure(figsize = figsize)
    id=0
    for shape in sf.shapeRecords():
        x = [i[0] for i in shape.shape.points[:]]
        y = [i[1] for i in shape.shape.points[:]]
        plt.plot(x, y, 'k')

        if (x_lim == None) & (y_lim == None):
            x0 = np.mean(x)
            y0 = np.mean(y)
            plt.text(x0, y0, id, fontsize=10)
        id = id+1

    if (x_lim != None) & (y_lim != None):     
        plt.xlim(x_lim)
        plt.ylim(y_lim)

默认情况下,上面的函数在一个给定的“df”文件上绘制所有的形状,包括它中间的形状 id。或者将绘制缩放的地图(无 id)。您可以更改打印或不打印 id 的功能。

绘制全图:

plot_map(sf)

Full Map

绘制地图:

y_lim = (-33.7,-33.3) # latitude 
x_lim = (-71, -70.25) # longitudeplot_map(sf, x_lim, y_lim)

Full Map with Zoom

8.在完整的地图上绘制单个形状

我们可以“合并”前面的两个功能,并在完整的地图中“绘制”一个单一的形状。为此,让我们编写一个新函数,其中形状 id 现在是一个输入参数:

def plot_map2(id, sf, x_lim = None, y_lim = None, figsize=(11,9)):
    '''
    Plot map with lim coordinates
    '''

    plt.figure(figsize = figsize)
    for shape in sf.shapeRecords():
        x = [i[0] for i in shape.shape.points[:]]
        y = [i[1] for i in shape.shape.points[:]]
        plt.plot(x, y, 'k')

    shape_ex = sf.shape(id)
    x_lon = np.zeros((len(shape_ex.points),1))
    y_lat = np.zeros((len(shape_ex.points),1))
    for ip in range(len(shape_ex.points)):
        x_lon[ip] = shape_ex.points[ip][0]
        y_lat[ip] = shape_ex.points[ip][1]
    plt.plot(x_lon,y_lat, 'r', linewidth=3) 

    if (x_lim != None) & (y_lim != None):     
        plt.xlim(x_lim)
        plt.ylim(y_lim)

密谋圣地亚哥的《红色》中的 comuna:

plot_map2(25, sf, x_lim, y_lim)

Santiago

如果我们想用特定的颜色“填充”一个形状呢?简单!为此,我们可以使用 plt.fill。该功能可以重写:

def plot_map_fill(id, sf, x_lim = None, 
                          y_lim = None, 
                          figsize = (11,9), 
                          color = 'r'):
    '''
    Plot map with lim coordinates
    '''

    plt.figure(figsize = figsize)
    fig, ax = plt.subplots(figsize = figsize) for shape in sf.shapeRecords():
        x = [i[0] for i in shape.shape.points[:]]
        y = [i[1] for i in shape.shape.points[:]]
        ax.plot(x, y, 'k')

    shape_ex = sf.shape(id)
    x_lon = np.zeros((len(shape_ex.points),1))
    y_lat = np.zeros((len(shape_ex.points),1))
    for ip in range(len(shape_ex.points)):
        x_lon[ip] = shape_ex.points[ip][0]
        y_lat[ip] = shape_ex.points[ip][1]
    ax.fill(x_lon,y_lat, color)

    if (x_lim != None) & (y_lim != None):     
        plt.xlim(x_lim)
        plt.ylim(y_lim)

用绿色(' g ')绘制“Las Condes”(id = 0)的 comuna:

plot_map_fill(0, sf, x_lim, y_lim, color='g')

Las Condes

9.在完整地图上绘制多个形状

我们“艰难的地图之旅”的下一个自然步骤是创建一个选择了几个形状的地图。为此,我们将有一个 id 列表,并使用 For 循环用颜色填充每个 id,而不是用 id 作为输入参数。修改后的函数如下所示:

def plot_map_fill_multiples_ids(title, comuna, sf, 
                                               x_lim = None, 
                                               y_lim = None, 
                                               figsize = (11,9), 
                                               color = 'r'):
    '''
    Plot map with lim coordinates
    '''

    plt.figure(figsize = figsize)
    fig, ax = plt.subplots(figsize = figsize)
    fig.suptitle(title, fontsize=16) for shape in sf.shapeRecords():
        x = [i[0] for i in shape.shape.points[:]]
        y = [i[1] for i in shape.shape.points[:]]
        ax.plot(x, y, 'k')

    for id in comuna:
        shape_ex = sf.shape(id)
        x_lon = np.zeros((len(shape_ex.points),1))
        y_lat = np.zeros((len(shape_ex.points),1))
        for ip in range(len(shape_ex.points)):
            x_lon[ip] = shape_ex.points[ip][0]
            y_lat[ip] = shape_ex.points[ip][1]
        ax.fill(x_lon,y_lat, color)

        x0 = np.mean(x_lon)
        y0 = np.mean(y_lat)
        plt.text(x0, y0, id, fontsize=10)

    if (x_lim != None) & (y_lim != None):     
        plt.xlim(x_lim)
        plt.ylim(y_lim)

在上面的函数中,“comuna”现在是一个 id 列表:

comuna_id = [0, 1, 2, 3, 4, 5, 6]
plot_map_fill_multiples_ids("Multiple Shapes", 
                            comuna_id, sf, color = 'r')

利用我们以前的 pandas 数据框架,让我们创建一个简单的函数,其中输入是 comuna 的名称,而不是它的 id:

def plot_comunas_2(sf, title, comunas, color):
    '''
    Plot map with selected comunes, using specific color
    '''

    df = read_shapefile(sf)
    comuna_id = []
    for i in comunas:
        comuna_id.append(df[df.NOM_COMUNA == i.upper()]
                         .index.get_values()[0])
    plot_map_fill_multiples_ids(title, comuna_id, sf, 
                                       x_lim = None, 
                                       y_lim = None, 
                                       figsize = (11,9), 
                                       color = color);

绘制圣地亚哥大都市区南部城市:

south = ['alhué', 'calera de tango', 'buin', 'isla de maipo', 'el bosque', 'paine', 'la granja', 'pedro aguirre cerda', 'lo espejo', 'puente alto', 'san joaquín', 'san miguel', 'pirque', 'san bernardo', 'san ramón', 'la cisterna', 'talagante', 'la pintana']plot_comunas_2(sf, 'South', south, 'c')

10.创建“热图”

一种非常有用的贴图类型是用颜色填充特定的形状,其“强度”与给定值成比例。这样,就有可能对特定地理区域的数据分布有一个总体的了解。例如,人口分布。

首先,我们将创建一个函数,一旦接收到一个数据列表,就会将它们拆分到“bin”上。每一颗豆子都会被赋予特定的颜色。就经验而言,通常 5 到 7 个箱对数据分布有很好的感觉。我们将使用 6 个箱子和与这些箱子相关的 4 个不同的调色板。您必须一次选择其中一个媒体夹。

def calc_color(data, color=None):
        if color   == 1: color_sq =  
                        ['#dadaebFF','#bcbddcF0','#9e9ac8F0',
                        '#807dbaF0','#6a51a3F0','#54278fF0']; 
                        colors = 'Purples';
        elif color == 2: color_sq = 
                        ['#c7e9b4','#7fcdbb','#41b6c4',
                        '#1d91c0','#225ea8','#253494']; 
                        colors = 'YlGnBu';
        elif color == 3: color_sq = 
                        ['#f7f7f7','#d9d9d9','#bdbdbd',
                        '#969696','#636363','#252525']; 
                        colors = 'Greys';
        elif color == 9: color_sq = 
                        ['#ff0000','#ff0000','#ff0000',
                        '#ff0000','#ff0000','#ff0000']
        else:            color_sq = 
                        ['#ffffd4','#fee391','#fec44f',
                        '#fe9929','#d95f0e','#993404']; 
                        colors = 'YlOrBr';
        new_data, bins = pd.qcut(data, 6, retbins=True,         labels=list(range(6)))
        color_ton = []
        for val in new_data:
            color_ton.append(color_sq[val]) 
        if color != 9:
            colors = sns.color_palette(colors, n_colors=6)
            sns.palplot(colors, 0.6);
            for i in range(6):
                print ("\n"+str(i+1)+': '+str(int(bins[i]))+
                       " => "+str(int(bins[i+1])-1), end =" ")
            print("\n\n   1   2   3   4   5   6")    
        return color_ton, bins;

函数 plot_comunas()plot _ map _ fill _ multiples _ ids都应该被修改以利用这种新的彩色方案:

def plot_comunas_data(sf, title, comunas, data=None, 
                      color=None, print_id=False):
    '''
    Plot map with selected comunes, using specific color
    '''

    color_ton, bins = calc_color(data, color)
    df = read_shapefile(sf)
    comuna_id = []
    for i in comunas:
        i = conv_comuna(i).upper()
        comuna_id.append(df[df.NOM_COMUNA == 
                            i.upper()].index.get_values()[0])
    plot_map_fill_multiples_ids_tone(sf, title, comuna_id, 
                                     print_id, 
                                     color_ton, 
                                     bins, 
                                     x_lim = None, 
                                     y_lim = None, 
                                     figsize = (11,9));

而且,

def plot_map_fill_multiples_ids_tone(sf, title, comuna,  
                                     print_id, color_ton, 
                                     bins, 
                                     x_lim = None, 
                                     y_lim = None, 
                                     figsize = (11,9)):
    '''
    Plot map with lim coordinates
    '''

    plt.figure(figsize = figsize)
    fig, ax = plt.subplots(figsize = figsize)
    fig.suptitle(title, fontsize=16)for shape in sf.shapeRecords():
        x = [i[0] for i in shape.shape.points[:]]
        y = [i[1] for i in shape.shape.points[:]]
        ax.plot(x, y, 'k')

    for id in comuna:
        shape_ex = sf.shape(id)
        x_lon = np.zeros((len(shape_ex.points),1))
        y_lat = np.zeros((len(shape_ex.points),1))
        for ip in range(len(shape_ex.points)):
            x_lon[ip] = shape_ex.points[ip][0]
            y_lat[ip] = shape_ex.points[ip][1]
        ax.fill(x_lon,y_lat, color_ton[comuna.index(id)])
        if print_id != False:
            x0 = np.mean(x_lon)
            y0 = np.mean(y_lat)
            plt.text(x0, y0, id, fontsize=10)
    if (x_lim != None) & (y_lim != None):     
        plt.xlim(x_lim)
        plt.ylim(y_lim) 

为了测试我们的新函数,让我们以之前圣地亚哥南部地区的形状列表为例,为每个形状关联一个常规值。我们将使用“调色板# 1(‘紫色’):

south = ['alhué', 'calera de tango', 'buin', 'isla de maipo', 'el bosque', 'paine', 'la granja', 'pedro aguirre cerda', 'lo espejo', 'puente alto', 'san joaquín', 'san miguel', 'pirque', 'san bernardo', 'san ramón', 'la cisterna', 'talagante', 'la pintana']data = [100, 2000, 300, 400000, 500, 600, 100, 2000, 300, 400, 500, 600, 100, 2000, 300, 400, 500, 600]print_id = True # The shape id will be printed
color_pallete = 1 # 'Purples'plot_comunas_data(sf, 'South', south, data, color_pallete, print_id)

酷!不是吗?;-)

11.绘制真实数据

为了完成关于如何使用 Python 处理地图的概述,让我们从 2017 年智利人口普查中获取一些真实数据,并应用在第 10 部分开发的那些函数。

读取数据集:

census_17 = pd.read_excel('./data/CENSO_2017_COMUNAS_RM.xlsx')
census_17.shapeOut:
(52,7)

我们的数据集有 52 行,每一行都包含与圣地亚哥的每一个 comunas 相关的数据。

查看数据集:

例如,“人物”一栏与居住在该特定社区的人数有关。“TOTAL_VIV”是那个 comuna 上的住宅总数,以此类推。

绘图:

让我们应用我们的地图功能来分析圣地亚哥大都市区的人口分布情况。

title = 'Population Distrubution on Santiago Metropolitan Region'
data = census_17.PERSONAS
names = census_17.NOM_COMUNAplot_comunas_data(sf, title, names, data, 4, True)

太好了!我们可以看到人口大量分布在都市圈的中心区域!东面(地图右边),人口稀少,这是合乎逻辑的,因为这是伟大的安第斯山脉!西边和南边是农业区。

再来一个!让我们画出圣地亚哥大都会区移民占总人口的百分比:

title = 'Percentual of immigrants over total population'
data = census_17.INM_PERC
names = census_17.NOM_COMUNAplot_comunas_data(sf, title, names, data, 2, True)

12.结论

您可以意识到,最终您可以使用本文开发的 3 个函数,只用一行代码创建一个地图:

plot_comunas_data() .... that calls:plot_map_fill_multiples_ids_tone() ... that calls:calc_color()

本文为 Santiago Matropolitan 地区开发的内容可以很容易地适用于互联网上的任何矢量地图!

例如,你可以去美国人口普查局下载美国各州的地图边界形状文件。继圣地亚哥之后,

shp_path = "./cb_2017_us_state_5m/cb_2017_us_state_5m.shp"
sf = shp.Reader(shp_path)# Continental US
y_lim = (23, 50) # lat 
x_lim = (-128, -65) # longstate_id = [0, 10, 3, 5, 6, 7, 8, 30]
plot_map_fill_multiples_ids("US - States", state_id, sf, x_lim, 
                             y_lim, color = 'r', figsize = (15,9))

您可以绘制:

那都是乡亲们!

希望您对数据科学的奇妙世界有了更多的了解!

Jupyter 笔记本和本文使用的所有数据可以从我的 GitHub 下载。

我的下一篇文章再见!

来自世界南部的 Saludos!

马塞洛

绘制我的脸书数据—第 1 部分:简单的 NLP

原文:towardsdatascience.com/mapping-my-…

NLP 看看我的写作风格(以及如何处理你自己的脸书数据!)

介绍

不久前,我被自己一天生成的文本数据量深深吸引住了。如果你像我一样,你可能会写很多东西。电子邮件。短信。脸书。也许你还有其他一些创造性的出路。也许你写日记或写音乐什么的。也许你是个学生,有一些写作作业。对我来说,我真的很想深入了解我正在生成的所有这些数据,并认为使用自然语言处理来分析这些数据会很酷。

这个系列将记录我是如何做的,如果你感兴趣的话,你也可以做同样的事情。

查找我的数据

当我仔细思考所有的数据时,我决定关注几个来源。他们是:

  • 我的书面学校作业
  • 我写的日记
  • 我写的歌曲集
  • 我的脸书数据(我的评论、帖子和聊天)

虽然对于我的整个项目,我使用了所有的资料来源,但对于这个系列,我只打算使用我在脸书的数据。

对于大多数数据,我只是把它放在一个文本文件中,然后就收工了。对于脸书的数据,我必须做更多的预处理。

如何获得你的脸书数据

我们如何真正得到我们的脸书数据?实际上,这比你想象的要简单。截至目前(2018 年 8 月 20 日),您可以通过以下方式下载数据:

  • 登录脸书
  • 点击右上角朝下的三角形
  • 单击设置
  • 在左上角,第三个选项是“你的脸书信息”点击这个
  • 下一个菜单中的选项#2 将是“下载您的信息”
  • 从这里,您可以决定想要什么数据,什么时间段,什么格式

我选择下载所有的东西,而且是 JSON 格式的。当我的下载准备好了,我得到了一堆这样的文件夹:

装满了我要求的 JSON。

预处理您的脸书数据

我想下载我所有的脸书数据,但实际上我并不想要这个项目的所有脸书数据。对于这个项目,我只关心我的帖子、评论和聊天记录。为了帮助解决这个问题,我为其中的每一个都编写了一个预处理脚本,以便将我想要的内容转储到文本文件中。

首先,我处理了我的信息:

import os
import json
import datetime

chat_root = 'messages'
writing_dir = 'data/facebook_chat'

for f in os.listdir(chat_root):
    file = os.path.join(chat_root, f, 'message.json')
    data = json.load(open(file, 'r'))

    for msg in data['messages']:
        try:
            if msg['sender_name'] == 'Hunter Heidenreich':
                if msg['content'] != "You are now connected on Messenger.":
                    content = msg['content']
                    ts = datetime.datetime.fromtimestamp(msg['timestamp_ms'] / 1000)

                    filename = os.path.join(writing_dir,
                                            str(ts.year) + '.' + str(ts.month) + '.' + str(ts.day) + '.txt')

                    with open(filename, 'a+') as out_file:
                        out_file.write(content + '\n')
                        print('wrote.')
        except KeyError:
            pass

您将看到,我正在遍历 messages 文件夹中的所有子文件夹。我在那里做的是读取消息 JSON。对于每条可用的消息,我都会检查它是否是我发送的消息。如果不是脸书默认的“您现在已在 Messenger 上连接”那我要了。我给消息打上时间戳,然后将它添加到一个采用year.month.day.txt格式的文件中,这是我给所有文本文件打上时间戳的格式,这样我就可以记录词汇随时间的变化。

如果由于某种原因 JSON 的键不起作用,我就忽略它。

我对我写的两个帖子做了非常相似的事情:

import os
import datetime
import json

in_data = 'posts/your_posts.json'
writing_dir = 'data/facebook_posts'

data = json.load(open(in_data, 'r'))

for post in data['status_updates']:
    try:
        ts = datetime.datetime.fromtimestamp(post['timestamp'])
        post_text = post['data'][0]['post']

        filename = os.path.join(writing_dir, str(ts.year) + '.' + str(ts.month) + '.' + str(ts.day) + '.txt')

        with open(filename, 'a+') as out_file:
            out_file.write(post_text + '\n')
    except KeyError:
        print(post)

我的评论是:

import os
import datetime
import json

comment_path = 'comments/comments.json'
writing_dir = 'data/facebook_comments'

data = json.load(open(comment_path, 'r'))
for comment in data['comments']:
    try:
        for d in comment['data']:
            if d['comment']['author'] == "Hunter Heidenreich":
                ts = datetime.datetime.fromtimestamp(d['comment']['timestamp'])
                com = d['comment']['comment']

                filename = os.path.join(writing_dir, str(ts.year) + '.' + str(ts.month) + '.' + str(ts.day) + '.txt')

                with open(filename, 'a+') as out_file:
                    out_file.write(com + '\n')
    except KeyError:
        print(comment)

从那里,我准备好了我的脸书数据。

加载我们的数据

首先,我们将编写一个简单的函数来获取某个类别中所有文件的列表。这将使我们能够轻松地跟踪哪个是哪个,并且我们将在处理和分析数据时保留这些命名方案。

import os

def get_file_list(text_dir):
    files = []
    for f in os.listdir(text_dir):
        files.append(os.path.join(text_dir, f))
    return files

comments = 'data/facebook_comments'
posts = 'data/facebook_posts'
chats = 'data/facebook_chat'

comment_files = get_file_list(comments)
post_files = get_file_list(posts)
chat_files = get_file_list(chats)

all_files = comment_files + post_files + chat_files

在我们实际读入数据之前,我们将编写一个函数,用几种不同的方式对数据进行预处理。

import string

import nltk
from nltk.corpus import stopwords
from nltk.stem import SnowballStemmer
from nltk.stem import WordNetLemmatizer

def preprocess_text(lines):

    def _flatten_list(two_list):
        one_list = []
        for el in two_list:
          one_list.extend(el)
        return one_list

    translator = str.maketrans('', '', string.punctuation)
    upd = []
    for line in lines:
        upd.extend(nltk.sent_tokenize(line))
    lines = [line.translate(translator) for line in upd]
    lines = [nltk.word_tokenize(line) for line in lines]
    lines = [[word.lower() for word in line if word not in [
        '\'', '’', '”', '“']] for line in lines]

    raw = lines

    stop = [[word for word in line if word not in set(
        stopwords.words('english'))] for line in raw]

    snowball_stemmer = SnowballStemmer('english')
    stem = [[snowball_stemmer.stem(word) for word in line] for line in stop]

    wordnet_lemmatizer = WordNetLemmatizer()
    lemma = [[wordnet_lemmatizer.lemmatize(
        word) for word in line] for line in stop]

    raw = _flatten_list(raw)
    stop = _flatten_list(stop)
    stem = _flatten_list(stem)
    lemma = _flatten_list(lemma)

    return raw, stop, stem, lemma

我们在这里所做的是产生我们文本的 4 个变体。我们正在生产:

  • 我们的原始数据去掉了标点符号,用了小写字母
  • 删除了停用词的数据
  • 我们的数据来源于
  • 我们的数据被假设了

考虑到这一点,我们现在可以创建一个基本的对象来保存我们的文件数据数据,并允许使用聚合来自脸书的不同来源的写作发生在同一天:

class FileObject:
    def __init__(self):
        self._datetime = None

        self._lines = []

        self._raw = []
        self._stop = []
        self._stem = []
        self._lemma = []

    @property
    def lines(self):
        return self._lines

    def preprocess_text(self):
        self._raw, self._stop, self._stem, self._lemma = preprocess_text(
            self._lines)

现在让我们加载数据并对其进行预处理。我将演示聚合数据上的代码,但它也适用于其他输入文件列表:

all_text = {}

for f in all_files:
    base = os.path.basename(f)

    if base not in all_text:
        all_text[base] = FileObject()

    with open(f, 'r') as in_file:
        all_text[base]._lines += in_file.readlines()

for k, v in all_text.items():
    v.preprocess_text()

这可能需要很短的时间,但当我们完成后,我们将能够开始看看关于我们的文本的一些基本的东西!

我最喜欢的词是什么?

让我们从最基本的开始。我们把这些单词列表加载到各种资源中。让我们做一个统计,看看我们最常用的词是什么。让我们看看我们的 20 强。

我们可以这样写:

from collections import Counter

def file_objects_to_words(fo_text):
    raw, stop, stem, lemma = [], [], [], []
    for key, value in fo_text.items():
        raw.append(value._raw)
        stop.append(value._stop)
        stem.append(value._stem)
        lemma.append(value._lemma)
    return raw, stop, stem, lemma

def top_20_words(list_of_word_lists):
    full_count = Counter()

    for word_list in list_of_word_lists:
        for word in word_list:
            full_count[word] += 1

    return full_count.most_common(20)

raw, stop, stem, lemma = file_objects_to_words(all_text)
print('All words -- raw')
print(top_20_words(raw))
print('All words -- stop')
print(top_20_words(stop))
print('All words -- stem')
print(top_20_words(stem))
print('All words -- lemma')
print(top_20_words(lemma))

print('Chat words -- lemma')
_, _, _, lemma = file_objects_to_words(chat_text)
print(top_20_words(lemma))
print('Post words -- lemma')
_, _, _, lemma = file_objects_to_words(post_text)
print(top_20_words(lemma))
print('Comment words -- lemma')
_, _, _, lemma = file_objects_to_words(comment_text)
print(top_20_words(lemma))

我们将得到一个很好的输出:

All words -- raw
[('i', 47935), ('you', 43131), ('to', 24551), ('and', 20882), ('the', 18681), ('that', 16725), ('a', 15727), ('it', 15140), ('haha', 13516), ('me', 12597), ('like', 10149), ('so', 9945), ('im', 9816), ('but', 8942), ('do', 8599), ('is', 8319), ('of', 8266), ('just', 8154), ('be', 8092), ('my', 8017)]
All words -- stop
[('haha', 13516), ('like', 10149), ('im', 9816), ('na', 6528), ('yeah', 5970), ('dont', 5361), ('okay', 5332), ('good', 5253), ('know', 4896), ('would', 4702), ('think', 4555), ('thats', 4163), ('want', 4148), ('cause', 3922), ('really', 3803), ('get', 3683), ('wan', 3660), ('hahaha', 3518), ('well', 3332), ('feel', 3231)]
All words -- stem
[('haha', 13516), ('like', 10580), ('im', 9816), ('na', 6528), ('yeah', 5970), ('think', 5772), ('want', 5396), ('dont', 5364), ('okay', 5333), ('good', 5261), ('know', 5078), ('would', 4702), ('get', 4535), ('feel', 4270), ('that', 4163), ('caus', 3992), ('go', 3960), ('realli', 3803), ('make', 3727), ('wan', 3660)]
All words -- lemma
[('haha', 13516), ('like', 10207), ('im', 9816), ('na', 6530), ('yeah', 5970), ('dont', 5361), ('okay', 5333), ('good', 5254), ('know', 5038), ('would', 4702), ('think', 4631), ('want', 4419), ('thats', 4163), ('cause', 3934), ('get', 3901), ('really', 3803), ('wan', 3660), ('hahaha', 3518), ('feel', 3358), ('well', 3332)]
Chat words -- lemma
[('haha', 13464), ('like', 10111), ('im', 9716), ('na', 6497), ('yeah', 5957), ('okay', 5329), ('dont', 5316), ('good', 5226), ('know', 4995), ('would', 4657), ('think', 4595), ('want', 4384), ('thats', 4150), ('cause', 3913), ('get', 3859), ('really', 3760), ('wan', 3649), ('hahaha', 3501), ('feel', 3336), ('well', 3299)]
Post words -- lemma
[('game', 68), ('video', 41), ('check', 38), ('like', 36), ('birthday', 36), ('happy', 34), ('im', 33), ('noah', 29), ('song', 28), ('play', 24), ('music', 21), ('one', 20), ('get', 19), ('guy', 18), ('time', 18), ('made', 18), ('evans', 18), ('hey', 17), ('make', 17), ('people', 16)]
Comment words -- lemma
[('noah', 120), ('im', 67), ('like', 60), ('evans', 60), ('thanks', 51), ('game', 50), ('haha', 47), ('cote', 44), ('one', 43), ('coby', 40), ('wilcox', 38), ('would', 33), ('man', 31), ('dont', 29), ('really', 29), ('know', 28), ('think', 28), ('na', 24), ('want', 23), ('get', 23)]

我喜欢只看我的词条,所以这就是为什么我只记录我个人的来源。我觉得有趣的是,我在聊天中经常使用“哈哈”的变体。我的大部分评论都是某人的名字。

我的单个单词用法是什么样的?

那么,如果我们想用图表来表示我们的单个单词,看看我们的用法是如何从顶部单词到底部单词衰减的呢?我们可以编写一个一般的条形图函数,如下所示:

from matplotlib import pyplot as plt

def plot_bar_graph(x, y, x_label='X', y_label='Y', title='Title', export=False, export_name='default.png'):
    plt.bar(x, y)
    plt.xlabel(x_label)
    plt.ylabel(y_label)
    plt.title(title)
    plt.xlim(0, len(x))
    plt.ylim(0, max(y))

    if export:
        plt.savefig(export_name)

    plt.show()

从那里,我们可以修改我们的前 20 个函数,并可以将我们的注释引理列表直接输入到图形中:

def aggregate_words_counts(list_of_word_lists):
    full_count = Counter()

    for word_list in list_of_word_lists:
        for word in word_list:
            full_count[word] += 1

    return full_count

counter = aggregate_words_counts(lemma)
word_counts_raw = list(counter.values())
word_counts_sorted = sorted(word_counts_raw)

cap = len(word_counts_raw)

plot_bar_graph(range(len(word_counts_raw[:cap])), word_counts_raw[:cap],
                      x_label='Words', y_label='Counts', title='Word Frequencies (Raw)',
                      export=True, export_name='visualizations/word_freq_bar_raw.png')
plot_bar_graph(range(len(word_counts_sorted[:cap])), word_counts_sorted[-cap:],
                      x_label='Words', y_label='Counts', title='Word Frequencies (Sorted)',
                      export=True, export_name='visualizations/word_freq_bar_sorted.png')

我们得到了两张非常好的图表:

我的数据有哪些基本统计数据?

让我们根据数据生成一些基本的统计数据。让我们设置一个函数来创建一个表:

def plot_table(cell_data, row_labels=None, col_labels=None, export=False, export_name='default.png'):
    _ = plt.figure(figsize=(6, 1))

    _ = plt.table(cellText=cell_data, rowLabels=row_labels,
                  colLabels=col_labels, loc='center')

    plt.axis('off')
    plt.grid(False)

    if export:
        plt.savefig(export_name, bbox_inches='tight')

    plt.show()

然后生成要转储到该函数中的数据:

import numpy as np

def top_k_words(list_of_word_lists, k=10):
    full_count = Counter()

    for word_list in list_of_word_lists:
        for word in word_list:
            full_count[word] += 1

    return full_count.most_common(k)

raw, stop, stem, lemma = file_objects_to_words(all_text)

counter = aggregate_words_counts(lemma)
data = [[len(all_files)],
        [top_k_words(lemma, k=1)[0][0] + ' (' + str(top_k_words(lemma, k=1)[0][1]) + ')'],
        [top_k_words(lemma, k=len(list(counter.keys())))[-1][0] + ' (' + str(top_k_words(lemma, k=len(list(counter.keys())))[-1][1]) + ')'],
        [len(counter.items())],
        [np.mean(list(counter.values()))],
        [sum([len(word_list) for word_list in raw])],
        [sum([sum([len(w) for w in word_list]) for word_list in raw])]]
row_labels = ['Collection size: ', 'Top word: ', 'Least common: ', 'Vocab size: ', 'Average word usage count: ',
              'Total words: ', 'Total characters: ']

plot_table(cell_data=data, row_labels=row_labels,
                  export=True, export_name='visualizations/basic_stats_table.png')

这些只是我认为有趣的一些数据。这次我把所有的数据都放了进去,因为我觉得这是最有趣的。

我们可以看到,我在脸书有 2147 天的文本活动。

我最喜欢的词是“哈哈”(这并不奇怪)。

总共 19,508 个单词

我用了将近 400 万个字符。

这是大量的文本数据!

随着时间的推移,我的 Vocab 使用情况如何?

我想知道随着时间的推移,我的 vocab 使用情况如何变化。我们怎样才能产生这种效果呢?好吧,幸运的是我们给所有的文件都打上了时间戳!

首先,让我们创建我们的绘图函数:

def plot(x, y, x_label='X', y_label='Y', title='Title', export=False, export_name='default.png'):
    plt.plot(x, y)
    plt.xlabel(x_label)
    plt.ylabel(y_label)
    plt.title(title)

    if export:
        plt.savefig(export_name)

    plt.show()

现在让我们写一些函数来绘制我们的单词使用情况:

import datetime

def get_datetimes(list_of_files):
    base_files = [os.path.basename(f) for f in list_of_files]
    no_ext = [os.path.splitext(f)[0] for f in base_files]
    splits = [f.split('.') for f in no_ext]
    times = np.array(
        [datetime.datetime(int(t[0]), int(t[1]), int(t[2])) for t in splits])
    return times

def unique_vocab(word_list):
    cnt = Counter()
    for word in word_list:
        cnt[word] += 1
    return cnt

raws = []
names = []
for key, value in all_text.items():
    raws.append(value._raw)
    names.append(key)

raw_wc = [len(word_list) for word_list in raws]
labels = get_datetimes(names)

labels, raw_wc = zip(*sorted(zip(labels, raw_wc)))

plot(labels, raw_wc,
            x_label='Date', y_label='Word Count', title='Word Count Over Time',
            export=True, export_name='visualizations/word_count_by_time.png')

raw_wc_u = [len(list(unique_vocab(word_list).items())) for word_list in raws]
plot(labels, raw_wc_u,
            x_label='Date', y_label='Word Count', title='Unique Word Count Over Time',
            export=True, export_name='visualizations/unique_word_count_by_time.png')

我们开始吧:

我觉得真的很有意思,2013 年年中,我用了很多词。我不太确定我在做什么,但是当你把它分解成独特的词时,在我那天使用的 20,000 个词中,没有多少是非常独特的…

更不用说,在 2017 年后,你肯定可以看到我的脸书使用量下降。

我觉得这真的很酷!

包扎

我们做到了!对我们的一些脸书数据的基本分析。希望你从你的脸书数据中学到了一两个技巧,也许还有一些关于你自己的东西!我知道当我开始分析我自己的时候,我确实是这样想的。如果你有很酷的视觉效果或者你想分享的东西,给我留言吧!很好奇看看别人在自己的数据里发现了什么。

下一次,我想我们会尝试对我们的脸书数据进行一些情绪分析,看看我们是否能从中找到任何有趣的花絮。

如果你喜欢这篇文章,或者发现它在任何方面都有帮助,如果你给我一两美元来资助我的机器学习教育和研究,我会永远爱你!每一美元都让我离成功更近一步,我永远心存感激。

请继续关注更多的脸书 NLP 分析!

最初发布于 hunterheidenreich.com 的

通过网络搜集 MapMyRun.com 和硒在 R

原文:towardsdatascience.com/mapping-phy…

我们都知道锻炼是我们身心健康最重要的因素之一。随着新的一年即将到来,一个强有力的宣言是**锻炼更多!**一定会在很多分辨率排行榜上名列前茅。但是要弄清楚如何真正做到这一点是很困难的。虽然一月是开设健身房会员最常见的月份,受健身中心无处不在的新年,新的我的刺激!在一些城市,这样的计划平均每年要花费 800 美元,这让许多美国人望而却步。一些人选择购买他们自己的设备,这种购买在新年达到高峰,但这仍然会带来可观的价格标签。这些障碍限制了锻炼的机会,增加了我们的健康不平等,这已经是世界上最糟糕的健康不平等了。

这些经济限制,加上个人偏好等其他因素,导致许多人转向另一种选择:外出健身。近 6000 万人说他们在 2017 年慢跑或越野跑,比 2006 年增加了 60%,还有 1 亿 T2 人说他们出去散步是为了个人健康。虽然这是对大自然的奇妙利用,但它也带来了另一个问题:并不是所有的户外都同样美丽。大多数美国城市被划分为高贫困和低贫困社区,中间几乎没有。马里兰大学的助理教授拉肖恩·雷博士说,高度贫困地区的人们不太可能锻炼,因为他们遭受犯罪的可能性更大。由于非洲裔美国人和西班牙裔美国人遭受极端贫困的可能性是白人的两倍多,这些邻里差异也导致了我们日益扩大的种族健康差距。

Photo by Kate Trifo on Unsplash

多伦多大学教授理查德·佛罗里达写道,解决这些问题需要“大量投资来改善贫困社区”,但是这种彻底的改变很难实现。与其等待整个社区被彻底改造,我想知道我是否能在高贫困社区内或附近找到“绿洲”,作为居住在附近的个人的运动区。人们通常不知道藏在自家后院的宝藏,这项分析可以解决这个问题。这也有利于那些想要改变锻炼习惯的活跃人群。我在户外锻炼的一个主要原因是探索我所在城市的新领域,但在像巴尔的摩这样的地方,错误的转弯可能是危险的,我经常发现自己坚持自己熟悉的东西。有了这些内部信息,无畏的城市探险者可以更安全地进行他们的冒险。

Photo by Erin White on Unsplash

为了进行这项分析,我计算了在过去的四年里,巴尔的摩地区的每个 GPS 坐标在 MapMyRun 上发布的一项锻炼中被包含的次数。我分析了近 20,000 次锻炼,这些锻炼是我用 Selenium ChromeDriver 刮下来的,在 R 中整理,并在 Kaggle 上分享。然后我使用 Javascript 中的传单库来映射数据。本文将展示如何做到这一点,并提供在另一个城市重复这一过程所需的所有步骤。这篇文章开头的地图绝不是一个成品,如果有传单或 Javascript 经验的人能帮助我扩展它的功能,我将不胜感激。要参与或分享反馈,请发推特给我 @halfinit 或访问该项目的 Github 资源库

获取数据

我选择使用安德玛旗下的路线追踪应用 MapMyRun 作为我的数据源,因为它有大量用户生成的巴尔的摩市和周边地区的锻炼数据。MapMyRun.com 的路线显示为覆盖在谷歌地图上的红色轨迹。这里有一个例子,2016 年 10 月 26 日发布的随机选择的 7.4 英里跑步。

Run centered in Severna Park, Maryland.

虽然这 20,000 张路线图可以进行很好的罗夏测试,但我们需要的是作为其基础的数据,这些数据包含在一个 **GPX 文件中的一列 GPS 坐标中。**我们可以手动进入每次跑步的网页下载 GPX 文件,但使用 Selenium ChromeDriver 来自动化这个过程肯定更有效。如果您想按照代码进行操作,您需要在 Linux 虚拟机上运行的 Docker 容器中设置 Selenium。为了进行设置,以及获得浏览器自动化的基本背景,请查看我的教程。对于 webscraping 代码,对 HTML、CSS 和 Javascript 等 web 开发概念的基本理解将对您有所帮助。

在 R 中加载库并启动 Selenium

**# Load the necessary libraries** library(tidyverse)**# Contains many important functions including %>%** library(RSelenium)**# Allows you to call Selenium functions through R** library(rgdal) **# We will use this for reading in GPX files** library(beepr) **# Alerts you when R finishes a long analysis****# Load Selenium in R. You'll need to have started Selenium with the Docker Quickstart Terminal first- See** [**tutorial**](/an-introduction-to-web-browser-automation-with-selenium-and-docker-containers-c1bcbcb91540) **for info!)** **# Tell Selenium to download files to a specific directory in
# the Docker Linux OS.** eCaps <- list(chromeOptions =
 list(prefs = list(
  “profile.default_content_settings.popups” = 0L,
  “download.prompt_for_download” = FALSE,
  “download.default_directory” = “home/seluser/Downloads”
  )
 )
)**# Sets how long the driver should wait when searching for elements.** remDr$setTimeout(type = “implicit”, milliseconds = 100000)**# Sets how long it should wait for the page to load.** remDr$setTimeout(type = “page load”, milliseconds = 100000)**# Set up the remoteDriver as remDr. Include chromeOptions** remDr <- remoteDriver(remoteServerAddr = “192.168.99.100”, browserName= “chrome”, port=4445L, extraCapabilities = eCaps)**#Open remoteDriver** remDr$open()

现在我们在 r 中有一个到 Selenium ChromeDriver 的活动连接,在你的全局环境中,remDr应该是一个object containing active binding

要在 MapMyRun 中做任何事情,我们首先需要登录我们的帐户,我们可以从 MapMyRun 主页访问它。

**#Navigate to MapMyRun homepage** remDr$navigate(“[https://www.mapmyrun.com](https://www.mapmyrun.com)")remDr$screenshot(display = TRUE)

在整个教程中,我使用remDr$screenshot函数来显示浏览器中正在发生的事情。如果你看到一个截图,这意味着它被调用了,尽管为了简洁起见我会把它排除在外。

我们需要点击右上角的 Log in 链接,我们可以通过它的 xpath 来识别它。xpath 只是一个 web 元素的路径。一个绝对 xpath 从网页的根开始,并以/开始,而一个相对 xpath 从任何被选择的节点开始,并以//开始。这里,我使用从 HTML 文档的header开始的相对 xpath,所以我的 xpath 从//header开始。

login <- remDr$findElement(using = “xpath”, “//header/div[2]/nav[2]/ul/li[1]/a”)

The HTML for MapMyRun’s homepage, with the relative xpath to the Log in Link highlighted.

现在我们告诉 ChromeDriver 点击 login 元素。

login$clickElement()

这将把我们带到登录页面,在这里我们需要输入我们的电子邮件和密码。但是它们的输入形式都是输入元素,那么我们如何区分它们呢?幸运的是,web 开发人员已经通过给每个元素赋予自己的属性解决了这个问题。存在许多类型的属性,但是我们将主要使用的三个是名称、ID 和类别。每个网页只使用一次 id,而类可以多次使用。姓名通常用在表单中,就像我们的电子邮件/密码输入表单一样。

The HTML for the input form, with the name and ID of our input elements highlighted.

**# Either name or id will work to select the element.**
email <- remDr$findElement(using = ‘name’, value = “email”)password <- remDr$findElement(using = ‘id’, value = “password”)

使用sendKeysToElement功能输入电子邮件和密码。

email$sendKeysToElement(list(“Example@email.com”))password$sendKeysToElement(list(“Password”))

要单击登录按钮,我们可以通过它的类来选择它,它有多个类。为了处理 RSelenium 中的复合类,我们需要使用css作为我们的选择器。

login <- remDr$findElement(using = 'css', "[class='success-2jB0o button-2M08K medium-3PyzS button-2UrgI button-3ptrG']")login$clickElement()

Some personal information blocked out

我们已经登录,并在我们的主页。要访问发布的路线,我们需要导航到查找路线页面,该页面位于左上方的路线下拉列表中。

我用另一个相对 xpath 选择它。

findRoutes<- remDr$findElement(using = “xpath”,
“//header/div[1]/nav[1]/ul/li[2]/ul/li/a”)

The HTML for our homepage, with the xpath to the Find Routes link highlighted.

findRoutes$clickElement()

我们已经创建了 routes 页面,但是现在我们需要在 Near 部分输入我们的城市。MapMyRun 将为您提供一个默认位置(称为占位符,但这可能是错误的——在我的例子中,它将马里兰州的巴尔的摩与俄亥俄州的哥伦布的邮政编码组合在一起!我们将通过点击它旁边的x来清空它,这是一个带有类“Select-clear”的< span >。

clear <- remDr$findElement(using = “class”, value=”Select-clear”)clear$clickElement()

由于 Near 元素没有惟一的 ID、名称或类,我们转而使用findElements找到页面上的所有元素,选择第二个元素,并使用 tab 键发送我们的城市信息进行自动填充。

**# Find all input elements with findElements.**
inputElements <- remDr$findElements(using = “css”, value=”input”) **# Choose the second one.** city <- inputElements[[2]]**# Send city info and the code for the tab key, uE004.** city$sendKeysToElement(list(“Baltimore, MD, 21231”, “\uE004”))

还有一点:我们希望获得所有上传的活动,而不仅仅是三英里以上的活动。

distance <- remDr$findElement(using = “name”, value = “distanceMinimum”)distance$sendKeysToElement(list(key = “backspace”, “0”))

我们现在可以单击 search 按钮,它有一个复合类。

searchButton <- remDr$findElement(using = "css", "[class = 'primary-xvWQU button-2M08K medium-3PyzS']")searchButton$clickElement()**#Scroll down the page to see the results.** webElem <- remDr$findElement(“css”, “body”)
webElem$sendKeysToElement(list(key = “end”))

HTML 表格中间的每个链接都是指向保存运行数据的网页的链接。

我们需要一个功能,将下载 20 个链接(这是 HTML 元素),按下一步按钮,并重复。我用一个 while 循环来完成这个操作,只要页面底部有一个“上一步”和“下一步”按钮,这个循环就会持续下去,当它下载完所有的锻炼数据时就会结束。由于这些按钮都是带有类pageLink-3961h的元素,我告诉 ChromeDriver 只要有两个这样的元素就继续下载。

**# Initialize a starter matrix** runs <- as.tibble(matrix(nrow = 20))**# Start the while loop** while (length(remDr$findElements(using = ‘xpath’,
  “//a[@class= ‘pageLink-3961h’]/span”)) == 2){**# Find the 20 links on the page that contain “routes/view”.** links <- remDr$findElements(‘xpath’, “//a[contains([@href](http://twitter.com/href), ‘routes/view’)]”)**# Save these in the runs starter matrix** for (i in 1:length(links)){
  runs<- links[[i]]
  runs<- runs$getElementAttribute(“href”)
  runs[i,1] <- paste(runs, sep=””)
}**# Add 20 more empty rows to the matrix to put the next set of links in.** runs <- rbind(as.tibble(matrix(nrow = 20)), runs)**# Click the next button** next<- remDr$findElement(using = ‘xpath’,
“//a[@class= ‘pageLink-3961h’][2]/span”)next$clickElement()**# Wait to make sure the webpage fully loads.** Sys.sleep(10)
}

runs数据框架中,我们现在有一个 20,000 个网址的列表,每个网址都链接到一个跑步者的网页。我们需要另一个循环来告诉 Selenium 打开每个 URL,将其导出为 GPX 文件,然后重复。

for (i in 1:20000){

  **# Navigate to this URL.** remDr$navigate(paste0(runs[i,]))**# Finds the element with the ID “export_this_route”** exportRoute <- remDr$findElement(using = "id", value = "export_this_route")

The web element with ID of “export_this_route”.

点击这个元素应该会打开一个下拉菜单,但是并不总是有效。所以我使用了一个 while 循环:只要下拉菜单(它有 ui_widget_overlay 的类)没有打开,就一直点击。

while(length(remDr$findElements(using= “xpath”,
“//div[[@class](http://twitter.com/class)=’ui-widget-overlay’]”))==0){

  exportRoute$clickElement()
}

The dropdown that appears after clicking “Export This Route”.

我们想点击“下载 GPX 文件”按钮。我们可以通过查看 HTML 来找出它的 ID。

The button has an ID of “export_route_gpx_btn”

gpxButton<- remDr$findElement(
    using = "id", value = "export_route_gpx_btn")gpxButton$clickElement()**# End the for loop** }

这些 GPX 文件将位于您在启动 Selenium 时确定的文件夹中。

结论

下载完这些 GPX 文件后,我对坐标进行了合并、归一化和对数变换。然后,我将数据发送给 Javascript,并使用传单标记集群插件来创建地图。但是这部分的分析将不得不放在另一篇文章中。你可以在 Kaggle 上查看或使用数据集,也可以在这里查看地图。感谢您的阅读,记得关注并发微博给我 @halfinit

绘制神经科学的图景

原文:towardsdatascience.com/mapping-the…

关于现代神经科学研究,对 100 多万篇文章的分析能告诉我们什么

神经科学是一个多样化的科学领域,由不同的学科组成:生物学、心理学、计算机科学、语言学等等。脑科学的主要目标是了解神经系统。然而,通向这一崇高目标的道路因领域而异。这种多样性源于这样一个事实,即大脑(或一般的神经系统)是已知宇宙中最复杂的器官,包含许多层次的动力学和复杂性。

所有这些多样性使得神经科学的语义界限更加难以界定。我们能不能用一种数据驱动的方法来阐明这样的边界,而不是被动地思考这样的边界可能是什么?

地图

让我们通过重新使用人类最伟大的发明之一:地图来解决这个问题。它们的主要功能是在一个有限的空间内概括感兴趣的世界,而不是像高度详细的卫星图像那样将真实世界复制到最后的细节。换句话说,地图仅仅是近似值,它突出了感兴趣的特定部分,而忽略了不相关的部分。因此,所有的地图都是有目的的。例如,纽约市的地图可能会突出显示街区地铁系统自行车路线旅游景点。虽然这些地图都不是纽约的准确代表,但它们都很有用。

我们能否重新利用绘制城市地图的概念来制作类似“神经科学地图”的东西?自然语言处理(NLP)领域提供了很有前途的工具,可以让我们更进一步,剩下的就交给我们自己的创造力了。

首先,我们应该考虑将自由文本(从 PubMed 神经科学文章数据库中抓取的超过 1M 的文章摘要)转换为更结构化的表示,以便于操作。从技术上来说,我们将通过构建一个文档术语矩阵(DTM) ,把摘要转换成一个向量空间表示。《DTM》的行里有所有的报纸,列里有单项选择。每个单元格的值代表一个单词 j 在文档 I 中出现的次数,因为我们会有一个非常大的词汇量,所以这个矩阵中的大部分值都被设置为零(这使得它成为一个稀疏矩阵)。使用这个矩阵,我们可以应用许多基本的数学技巧来回答我们的一些问题,如找到相似的单词、找到相似的论文、将论文分组(找到子字段)或对单词做同样的事情,等等。

正如我前面提到的,地图是根据一个特定的目标创建的,我有两个不同的目标或者两个地图。第一张地图将把潜在的语义可变性分成一个简单的范围。你可以把第一张地图想象成简单的语义罗盘。第二张图总结了单词相似度结构:什么单词和什么单词最相似,哪组单词和哪组最相似。这两张地图将在解开现代神经科学研究中潜在的语义景观方面相互补充。

故事 1——提取语义指南针

如果你要决定适用于研究领域的最简单(也是最通用)的标准,你可能会从基础研究开始,从应用研究开始。这些行中的数据驱动标准可以帮助我们阐明哪些维度或信息在整个词汇表中最具区分性。

对应分析是一种定量方法,将任何给定单词的信息压缩成简单的频谱,同时保留任何相似单词的映射。

Map 1: Correspondence Analysis results

如果你仔细观察上面的地图,你会发现单词分成两大组:右组和左组。我们可以通过查看这些单词所属的类别来确定这些组的身份。对我来说,右组主要是关于认知成像研究,而左组主要是关于更基本的方法(分子和遗传)。稍微缩小一下,最有鉴别能力的标准可能更多的是关于研究工具,而不是被调查问题的性质(诚然,有时可能很难将两者分开)。

故事 2——揭示底层结构

虽然上面的地图作为一幅大图是有用的(并且在数学上更容易分析),但它没有告诉我们底层的结构。如果我们把最相似的单词放在一起,利用我们所拥有的关于单词共现的信息,我们可能能够展示一个更准确和信息更丰富的画面。

Map 2: A network visualization of most common terms colored by the group they are most connected to.

在这里,我们看到一个更底层的结构。正如我们已经知道的,这个领域可以分成许多子域。也就是说,有一个临床神经科学集群(绿色),一个认知神经科学集群(绿色),一个神经生理学集群(灰色)和一个分子神经科学集群(红色)——以及更精细的集群。请注意,如果我对共现次数使用较低的阈值,可以显示更多的单词,但这可能会使网络更难正确可视化。

Close-ups.

虽然第二张图比前一张图更主观(也更不科学),但它仍然有助于显示构成现代神经科学研究的大的子领域。

乔治·博克斯曾经说过“所有的模型都是错的,但有些是有用的。”根据这个美丽的引用,我们也可以声称,每个可视化的故事只说明了关于要分析的数据的本质的一部分真相。希望这篇文章是有用的和有价值的。

感谢阅读。

描绘真实世界

原文:towardsdatascience.com/mapping-the…

Fig 1. Administrative divisions of China and Taiwan on (Left) an equal-area map, and (Right) a Flow-Based Cartogram where areas are proportional to GDP.

我们都认为我们知道世界是什么样子的。但是我们的世界地图总是反映最重要的东西吗?例如,关于中国在过去几十年中惊人的经济增长已经说了很多。然而,图 1(左)中的中国地理地图并没有告诉我们中国不同地区的国内生产总值(GDP)增长程度。

如果我们能制作更多视觉信息的地图会怎么样?

事实证明,我们可以。图 1(右)展示了一幅中国地图,其中各地区根据其各自的 GDP 贡献进行了重新调整。它不仅描绘了人们在经济进步背景下谈论的真实的中国,也展示了该国发展中的严重扭曲和不平等。这是对现实的一个有力的、引人入胜的描述——中国西部和东北实际上没有出现在伟大的中国成功故事中。

如图 1(右)所示的地图被称为地图。常规地图在形状、大小和位置方面优先考虑地理准确性。另一方面,统计图根据统计数据(如人口或 GDP)重新调整地图区域,这对于特定应用程序可能更有意义,同时仍尽可能保持拓扑地理特征。

除了提供震动和警报,在可视化空间数据方面,统计图还提供了优于常规地图的统计优势。下面图 2 中描述 2016 年美国总统选举结果的地图就是一个例子。共和党的唐纳德·特朗普赢得了红色的州。民主党的希拉里·克林顿赢得了蓝色州。

Fig 2. 2016 US presidential election map. Red indicates Republican victory. Blue indicates Democratic victory.

快速浏览一下这张地图,就会发现唐纳德·特朗普(Donald Trump)取得了压倒性胜利。这一错误结论的出现是因为地图上的区域面积没有反映它们所代表的定量数据。例如,爱达荷州和罗德岛州都拥有 4 张选举人票。但是爱达荷州在地图上占据了明显的位置,而罗德岛几乎不引人注意。

相反,如果我们使用基于每个州的选举人票的图表来表示相同的数据,我们将获得更直观的选举结果的代表性描述。这样的图表包括在下面的图 3 中。蓝色和红色区域现在准确地显示了民主党和共和党分别赢得了多少张选举人票。

Fig 3. US presidential election results on a Flow-Based Cartogram. Red indicates Republican victory. Blue indicates Democratic victory.

基于扩散的图表(链接)在很大程度上是使用中最流行的图表。这种图表生成技术在扩散的物理过程中找到了灵感。定义扩散运动的数学已经在科学文献中被仔细研究和记录,并很好地解决了手头的问题。

打个比方,考虑在房间的一个角落喷洒一些香水。几分钟后,你也能在房间的其他地方闻到香味。这是因为气味从高密度区域(最初被喷洒的角落)扩散到低密度区域(房间的其余部分)。同样,地图上的每个区域都可以认为是由粒子组成的。那么,对于人口分布图来说,高人口密度区域的粒子会向外流动并扩散,从而扩大这些区域,进而缩小低人口密度区域。

也就是说,这种图表生成方法相当慢。在当今的硬件上,他们可能需要几十分钟来创建简单的图表,如本文中使用的图表。创建图表的耗时过程是广泛采用这种数据可视化技术的巨大障碍。

为了加速图表生成过程并促进它们的共同使用,我与迈克尔·t·加斯特纳教授和 T2 研究并设计了一种新的算法,来生成我们称之为 T4 的基于流程的图表。请阅读我们在美国国家科学院院刊上发表的研究论文。

总之,在生成图表时,我们希望均衡地图上所有位置的密度。虽然传播是这样做的一种方式,但不是唯一的方式。我们的技术背后的新数学带来了算法效率,并使我们能够将计算分成可以独立完成的小而不相关的部分,从而利用了当今广泛使用的多核处理器。

我们工作的结果是图表生成器的速度提高了 60 倍。本文中展示的每个图表都是在几秒钟内生成的。中国地图用了我们的方法不到 3 秒钟就制作出来了,而美国大选地图用了 1.5 秒。作为另一个例子,下面的图 4(右)中的印度图表,其中各邦根据其各自的 GDP 贡献进行了重新调整,使用我们基于流量的方法需要 2.6 秒来生成。

Fig 4. States and union territories of India on (Left) an equal-area map, and (Right) a Flow-Based Cartogram where areas are proportional to GDP.

图表是一种强有力的工具,可以展示地面真相的有力表示,从而有效地提高广大民众对这些真相的认识。我希望通过我们的研究,我们可以减少创建图表的障碍,从而促进它们的采用。带着这个目标,我们还在 GitHub 上提供了我们的软件,以及如何使用它的说明。请欣赏使用该软件创建的网格世界地图,并根据人口进行缩放,如下图 5 所示。

注意:我正在开发一个 Python 包,让图表生成更加简单。我还计划建立一个 web 应用程序来实现同样的目的。这两个项目都是开源的。如果你想合作,请发电子邮件到 pratyushmore1996@gmail.com找我。

研究论文: PNAS 文章

软件: GitHub

Fig 5. Flow-Based Cartogram of a gridded world map, rescaled according to population.

英国交通事故热点地图

原文:towardsdatascience.com/mapping-the…

Photo by Chris Lawton on Unsplash

在寻找一些有趣的地理数据时,我偶然发现了英国政府发布的道路安全数据。这是一个非常全面的道路事故数据集,包括事件的地理坐标,以及其他相关数据,如当地的天气情况,能见度,警察出勤等。早在 2009 年就有可用的数据,一直到 2016 年,所以这是一个非常有趣的数据集,可以用于地理和机器学习目的。

当我看到这些数据时,我立即想到在地图上可视化它。也许有可能找出交通事故密度较高的地区?由于一年的数据量巨大,仅 2016 年就超过 136,000 个点,将所有的点都转储到交互式地图中的天真方法被证明是不可能的。根据我的经验,这对于一个交互式网络地图来说太多了。这里需要另一种方法,显而易见的想法是为此探索基于密度的聚类算法。

与将整个输入空间划分为互补区域或聚类的其他聚类算法相反,这里我们只关注交通事故密度较高的区域,而将所有其他点作为噪声丢弃。对于这些高密度区域中的每一个,我们将创建一个地理围栏,作为其周围的包络,并使用它作为其中包含的点的图形表示。这种新的地理实体(多边形)可以存储在地理数据库中,以后可以用于在英国非常繁忙的街道上行驶时提供驾驶辅助。想象一下你的车辆 GPS 系统的一个额外功能,它会通知你进入一个道路事故热点,就像它警告你不要靠近高速摄像机一样。我打赌你在那里会试着开车更安全,不是吗?

让我们想象一下,我们的任务是使用来自英国政府的数据来实现这样一个系统。我们必须以某种方式将这一长串地理位置转换成地理围栏,将道路事故地理密度较高的区域圈起来。有了地理围栏——一种用地理坐标表示的多边形——我们可以很容易地测试你的车辆是否正在接近一个这样的热点,如果它已经进入或离开它。

基于密度的聚类

因此,为了检测事故热点,我们必须找到事故位置密度高的区域,并在每个区域周围绘制一个多边形。首先,我们必须澄清我们所说的密度是什么,以及如何测量它。此外,我们必须了解如何处理低密度区域。

基于密度的数据分析技术背后的基本思想是,感兴趣的数据集代表来自未知概率密度函数(PDF)的样本,该函数描述了负责产生观察数据的一种或多种机制。[1]

这里,我们将聚类定义为包围高密度区域的区域,而所有其他点将被视为噪声,因此从分析中丢弃。有几种算法可以处理这种类型的数据,我为本文选择的算法是 DBSCAN [2]。

我们识别聚类的主要原因是,在每个聚类内,我们都有一个典型的点密度,该密度远高于聚类外的密度。此外,噪声区域内的密度低于任何聚类中的密度。[2]

你可以在这里阅读关于这个聚类算法的很好的描述,并且本文中使用的实现是由scikit-learn【3】提供的。注意,有两个非常重要的参数需要设置:聚类点之间的最小距离( eps )和每个聚类的最小点数( minPts )。为了更好地理解这些参数如何工作以及它们如何影响聚类结果,您可以在笔记本单元格上单独设置它们:

这里, minPts 参数被创造性地命名为 num_samples 。这些参数决定了什么属于一个聚类,什么被认为是噪声,因此这些参数将对最终聚类集的数量和大小产生直接影响。

运行 DBSCAN 实际上非常简单:

One-liner for running DBSCAN. See the GitHub repository for more information.

在对数据运行 DBSCAN 之后,我们得到了一个集群及其对应点的集合。噪声点用聚类号-1 标记,并从我们的分析中排除。

气泡

现在是用这些数据做有趣事情的时候了,在本文中,我们将使用每个星团的云的形状来绘制地理围栏。有几种策略可以做到这一点,比如绘制一个凸包或者甚至一个凹包,但是这里我将使用一个非常简单的方法,可以称为“聚结气泡”。这个想法很简单:在每个点周围画一个圆,然后将它们合并在一起。像这样:

The “coalescing bubbles” process of geofence calculation.

这是一个两步的过程,我们首先将所有的位置点“膨胀”成一个给定半径的圆,然后将所有的圆合并成一个多边形。创建圆(缓冲)代码如下:

投影代码需要在米(我们用于圆半径的单位)和地理坐标(纬度和经度对)之间进行转换。至于半径,我们使用一个比 eps 小 0.6 倍的值,以避免出现非常大的圆。

让我们看看这在代码中是如何工作的。首先,我们必须根据聚类标识符对点进行分组。记住噪声点用-1 标记。

现在我们可以开始冒泡过程。为了提高效率,我使用了 shapely 中的 cascaded_union 函数。

Bubble creation process

现在,我们可以使用上面创建的列表创建一个地理数据框架,并简单地绘制它。就像这样简单:

Create a geopandas GeoDataFrame and plot it.

最后,我们可以用两行代码将整个事情发送到一个交互式地图:

Show the interactive map.

完整代码可在相关的 GitHub repo 中获得。尽情享受吧!

London traffic accident hotspots example using data between 2015 and 2016.

所需包

为了运行笔记本,您必须首先安装几个包,即 geopandas 和所有依赖项。这不是一个简单的任务,但幸运的是 Geoff Boeing 在他出色的博客文章在 Windows 上使用 geo pandas中为我们简化了这个任务。

你还需要安装笛卡尔包来渲染地图上的多边形。

最后,你还需要mple leaf,在浏览器上渲染交互式地图。

参考文献

[1] 用于数据聚类、可视化和异常值检测的分层密度估计

[2]sci kit-learn:Python 中的机器学习,Pedregosa 等人,JMLR 12,第 2825–2830 页,2011。

[3] Ester,m .,Kriegel,H.P .等人(1996)一种用于在带有噪声的大型空间数据库中发现聚类的基于密度的算法。KDD,226–231

[4] 用 Python 制作地图,米歇尔·富尔伍德

将旅行时间映射到新加坡的各个地方

原文:towardsdatascience.com/mapping-tra…

今天我正在玩 rmapzen 包来创建一些等时图。

等时线地图描绘了通过不同的交通方式到达特定位置的旅行时间相等的等值线。

在这里,我画出了开车去果园的时间和步行去海湾花园的时间。

Driving time to Ion Orchard

Walking time to Garden By the Bay

可以很好地利用等时线地图:例如,通过将等时线地图与地区/邮政编码 shapefile 重叠来缩小搜索范围,以查找公共交通 x 分钟内的工作场所,或者找出医疗保健设施覆盖最少的区域,教育部门覆盖最多的区域等。我还没有弄清楚是否可以用 rmapzen 创建多个等时线,来描绘到不同位置的旅行距离的轮廓。

这是我的# 100 日项目的第 38 天,在外出旅行几周后,我开始了数据科学和视觉故事讲述。完整的代码在我的 github 上。感谢阅读,欢迎反馈。

用 Word2vec 映射单词嵌入

原文:towardsdatascience.com/mapping-wor…

利用词向量之间的语义和句法关系增强自然语言处理

Word embedding created using Word2vec | Source: www.adityathakker.com/introductio…

介绍

自然语言处理(NLP)是人工智能的一个领域,专注于让计算机理解、处理和分析人类语言。NLP 广泛应用于科技行业,作为搜索引擎、垃圾邮件过滤器、语言翻译等等的主干。NLP 使计算机能够将人类语言转换成它可以阅读和理解的形式,例如向量或离散符号。例如,NLP 可以接收句子So hungry, need food,并将其分解为四个任意符号:so表示为K45hungry表示为J83need表示为Q67food表示为P21,然后所有这些都可以由计算机处理。每个唯一的单词由不同的符号表示;然而,缺点是指定给hungryfood的符号之间没有明显的关系。这阻碍了 NLP 模型使用它所了解到的关于hungry的信息并将其应用到food,这在语义上是相关的。向量空间模型(VSM)通过将单词嵌入向量空间来帮助解决这个问题,在向量空间中,相似定义的单词被映射到彼此附近。这个空间叫做单词嵌入。

Word2vec

Word2vec 是由谷歌的托马斯·米科洛夫领导的一个研究小组的成果,是用于创建单词嵌入的最流行的模型之一。Word2vec 有两种将单词上下文化的主要方法:连续词袋模型(CBOW)和跳过语法模型,我将在本文中总结这两种方法。这两个模型得出了相似的结论,但是采用了几乎相反的途径。

连续词袋模型

CBOW 是两个模型中不太受欢迎的一个,它使用源单词来预测目标单词。例如,以这个实例中的句子I want to learn python**.**为例,目标词是python,而源词是I want to learn。CBOW 主要用于较小的数据集,因为它将句子的上下文视为预测目标单词的单个观察。实际上,当处理大量单词时,这变得非常低效。

跳格模型

Skip-Gram 模型的工作方式与 CBOW 模型相反,使用目标单词来预测周围单词的来源或上下文。考虑句子the quick brown fox jumped over the lazy dog,假设我们对给定单词的上下文使用一个简单的定义作为紧接在它前面和后面的单词。Skip-Gram 模型将把句子分成**(context, target)**对,产生一组格式如下的对:

**([the, brown],quick), ([quick,fox],brown), ([brown,jumped],fox)...**

这些对被进一步筛选成**(input, output)**对,表示每个单词(输入),该单词直接位于其前面或后面。这是必要的,因为 Skip-Gram 模型通过使用目标单词(输入)来预测上下文或输出。这些对表示如下:

**(quick, the), (quick, brown), (brown, quick), (brown, fox)...**

既然每个单词都能够在上下文中表示出来,有趣的事情就开始了。我不会进入数学领域——这个 TensorFlow 教程提供了深入的解释——但是预测给定上下文中每个单词的损失函数可以使用随机梯度下降和迭代数据集中的每一对来优化。从那里,可以使用 t-SNE 降维技术将向量降维为二维。

Skip-Gram model with context defined as the two words immediately before and after the target word. Source: mccormickml.com/2016/04/19/…

可视化单词之间的语义和句法关系

一旦单词向量被简化为二维,就有可能看到某些单词之间的关系。语义关系的例子是男性/女性名称和国家/首都关系,而句法关系的例子是过去时和现在时。下图很好地展示了这些关系:

Source: www.tensorflow.org/tutorials/r…

共享语义或句法关系的单词将由相似大小的向量来表示,并且在单词包含中被映射为彼此非常接近。不再是用任意的离散符号K93表示king,用S83表示queen的情况。相反,kingqueen之间的关系更加明显——事实上,它与分别对应于manwoman,的向量之间的关系完全相同。这允许在向量上执行非常酷和神奇简单的运算。例如:如果从brother的矢量表示中减去boy的矢量,然后加上girl的矢量,就会得到sister

**brother - boy + girl = sister****queen - woman + man = king****biking - today + yesterday = biked**

这为在数据中寻找模式或其他见解开辟了一个全新的可能性维度。

使用 TensorFlow 实现 Word2vec

使用 TensorFlow 教程中展示的示例代码我将演示 word2vec 在实践中是如何工作的。TensorFlow 是由谷歌大脑团队开发的内部使用的机器学习库,于 2015 年向公众开源,旨在加速人工智能的发展。TensorFlow 是一种用于深度学习、逻辑回归和强化学习的强大工具,并且由于其在训练大型数据集时优化计算效率的能力而变得流行。

提供的示例代码读取 50,000 个单词的大型数据集,并训练 skip-gram 模型以上下文方式对单词进行矢量化。然后,它遍历数据 100,000 次,以优化数据集中一批随机流行词的损失函数。首先,流行单词的最近邻居没有显示出与每个单词的任何句法或语义关系。在 100,000 步之后,可以看到清晰得多的关系,并且损失函数降低了 98%以上。

Summary showing 8 nearest neighbors for a random batch of popular words, at 0 and 100,000 steps

如上所述,在训练 skip-gram 模型之前,单词three的八个最近邻是:bissau, zeal, chong, salting, cooperate, quarterfinals, legislatures, ample。当迭代完成时,three的最近邻居是:five, four, seven, six, two, eight, nine, agouti。虽然不完美——据我所知,agouti 是一种热带美洲啮齿动物,而不是一个数字——8 个最近的邻居中有 7 个显示出与单词three有明确的语义关系。

Left: Central American Agouti | Right: Numbers - (Sources: Wikipedia, Google Images)

谷歌翻译中的性别偏见

虽然 word2vec 可以创建说明单词之间的语义和语法关系的单词嵌入,但该模型并非没有一些缺陷。2016 年一项名为 的研究显示,男人对于电脑程序员就像女人对于家庭主妇一样?消除单词嵌入的偏见 (Bolukbasi,Chang,Zou,Saligrama,Kalai)展示了谷歌使用的单词嵌入如何以惊人的速度强化性别陈规定型观念,并确定了解决这一问题的潜在办法。数据科学家和企业家 Emre arbak 使用谷歌翻译进一步强调了单词嵌入算法所显示的性别偏见。精通土耳其语的 arbak 测试了谷歌如何将使用中性代词的土耳其语句子翻译成英语。结果既令人着迷又令人不安。(更多例子)

Source: www.facebook.com/photo.php?f…

在很大程度上,当一个句子包含陈规定型地归因于女性的描述符(cook, teacher, nurse)时,土耳其中性代词o被翻译成she。相反地,包含诸如hard workinglawyerengineer的句子中,代词被翻译成了男性形式。这不仅仅是谷歌的责任——它的算法是基于包含数十亿数据点的人类词汇语料库,所以谷歌只是反映了已经存在的偏见。然而,谷歌仍在确定数百万人在使用翻译(或搜索、YouTube 和任何其他流行的谷歌平台)时看到了什么。

最终,这很可能是一个强大工具的意想不到的负面后果,但它提出了一个重要问题,即我们有多容易让计算机和人工智能决定我们的想法和看法。NLP 和单词嵌入是必不可少的工具,可以说是人工智能的未来;然而,关于机器学习算法如何做出决策的公开对话非常重要,这样边缘化的声音才不会被拒之门外。

其他资源:

三月版:理解如此多的数据

原文:towardsdatascience.com/march-editi…

11 篇必读文章

关于机器学习需要知道的 12 件有用的事情

詹姆斯·勒 — 16 分钟读完

机器学习算法可以通过从示例中进行归纳来找出如何执行重要任务。在手动编程不可行的情况下,这通常是可行的且成本有效的。随着越来越多的数据可用,更多雄心勃勃的问题可以得到解决。

加入我们成为编辑助理 : 随着我们读者群的持续增长,我们将开放 5 个新的编辑助理志愿者职位。立即申请。

多维数据的有效可视化艺术

迪潘詹·萨卡尔 — 16 分钟读取

描述性分析 是任何与数据科学项目甚至特定研究相关的分析生命周期的核心组成部分之一。数据聚合、汇总和可视化是支持这一数据分析领域的一些主要支柱。

大数据会有偏差,如果我们放任不管

由费德丽卡·佩泽尔 — 7 分钟读完

对于我们这些肩负着使用大数据来帮助解决组织中一些最大的低效、问题或难题的令人兴奋且日益增长的任务的人来说,永久化偏见是一种太容易犯的错误,我们现在都应该熟悉它了。

解释机器学习模型

由 Lars Hulstaert — 8 分钟阅读

不管您的数据科学解决方案的最终目标是什么,最终用户总是更喜欢可解释和可理解的解决方案。此外,作为一名数据科学家,您将始终受益于模型的可解释性,以验证和改进您的工作。

如何构建数据科学管道

Balázs Kégl — 5 分钟阅读

从 y 开始。专注于形式化预测问题,建立工作流程,并将其转化为生产,而不是优化您的预测模型。一旦前者做到了,后者就好办了。

如何使用序列对序列模型创造神奇的数据产品

通过哈默尔侯赛因 — 17 分钟读取

我从未想过我会用“神奇”这个词来描述机器学习技术的输出。当我被引入深度学习时,这种情况发生了变化,在深度学习中,你可以完成像识别图片中的物体分类两吨重的乐高玩具这样的事情。

如何进行数据实验室:针对大型数据集运行笔记本电脑

郁风克 — 5 分钟读出

将大数据传输到本地计算环境既缓慢又昂贵。在这一集的人工智能冒险中,我们将看到如何将您的笔记本环境应用到您的数据中!

概括 10 种常见的软件架构模式

通过Vijini mallawatarachchi—5 分钟读取

想知道大型企业级系统是如何设计的吗?在主要的软件开发开始之前,我们必须选择一个合适的架构,它将为我们提供期望的功能和质量属性。

数据库— SQL 和 NoSQL

阿努拉达·维克拉马拉奇 — 4 分钟阅读

SQL 在 1970 年与 E. F. Codd 博士的研究论文“大型共享数据库的关系数据模型”一起使用。是啊!!这是 Boyce-Codd 规范化中的 Codd。

大规模应用开发架构

查敏·纳林达 — 17 分钟读完

现在,只需一瞥的时间,就可以实时分析卫星从太空发送到地球的数十亿字节的数据。还记得埃隆·马斯克强调的来自人工智能的潜在威胁吗?如果我们不能规范“人工智能”(他认为这非常重要),这种威胁就不远了。

数据科学工作流程

通过 Aakash Tandel — 13 分钟读取

没有解决数据科学问题的模板。路线图会随着每个新数据集和新问题而变化。但是我们确实在许多不同的项目中看到类似的步骤。

我们也感谢最近加入我们的所有伟大的新作家,乌兹玛·巴拉斯卡克里斯·罗尔斯凯末尔·图鲁尔内森·胡本斯简内·库尔艾克·奥康科沃维德雅瑟格·马楚帕里德里克·姆维蒂尼克拉斯·东格斯 扎克·阿基格雷格·拉弗蒂阿桑·阿尼斯杰瑞米·哈里斯易勒雅斯·哈比卜文卡特什·帕帕克里什南博士诺拉·侯赛尼阿迪蒂亚·阿南瑟拉姆弗朗切斯科·祖皮奇尼 我们邀请你看看他们的简介,看看他们的工作。

Mario vs . Wario:Python 中的图像分类

原文:towardsdatascience.com/mario-vs-wa…

使用逻辑回归和卷积神经网络对视频游戏图像进行分类

从我的学龄前时代,我记得花了很多时间在我最喜欢的游戏男孩身上玩游戏。我最喜欢的两个平台游戏是马里奥和瓦里奥。我记得当我的祖母看了一眼我正在玩的游戏,问我那是什么。我解释说是超级马里奥。过了一段时间,当她看到我又在玩游戏时,她看着屏幕说:“又是马里奥?这个游戏有多长?”但这是一场完全不同的比赛,瓦里奥。这种记忆启发我尝试图像识别,并尝试看看我是否可以训练一个分类器来准确识别一些截图的来源。

在本文中,我使用了两种方法。基本的是逻辑回归,比较高级的是卷积神经网络(使用 Keras 配合 TensorFlow 后端)。我并不专注于解释算法背后的逻辑或数学,因为已经有大量关于 Medium 和其他地方的优秀文章。相反,我试图展示一个简单、随机的想法如何快速转化为数据科学项目。

为了简洁起见,我只发布了一些代码片段,而完整的代码可以在我的 GitHub 上找到。

数据准备

本着儿时的回忆,我选择了两款游戏进行这次实验:超级马里奥之地 2: 6 金币瓦里奥之地:超级马里奥之地 3 。我选择这些游戏不仅是因为它们是我当时最喜欢的,而且在检查游戏的图像时,人们可以看到它们在视觉上非常相似,这应该会使任务变得有点困难!

我想知道从这些游戏中获取大量截图的最好方法是什么,于是决定从 Youtube 上的一个视频中“抓取”下来。Python 的pytube库可以帮助完成这项任务。我可以毫不费力地用几行代码下载整个视频。

下一步包括从视频中剪切帧。为此,我迭代所有帧(使用OpenCV库),并且只将每第 n 帧保存到指定的文件夹中。我决定用 10k 的图像(每场 5k)。在这两种方法中,我将使用相同的 80-20 训练测试分割来确保可比性。

当抓取帧时,我跳过了视频的前 60 秒,其中主要包含开场序列和菜单(我没有在视频结尾这样做,所以可能会包含一些噪声,但我们会看到的!).

A sample image from Mario class

A sample image from Wario class

在看了预览后,很明显这些图像的大小不一样。这就是为什么我把它们重新调整为 64x64 像素。此外,对于逻辑回归,我会将图像转换为灰度,以减少模型的特征数量(CNN 将处理 3 个颜色通道)。

64x64 greyscale image for logistic regression

逻辑回归

我将从更简单的模型开始。逻辑回归是一种基本的二元分类器,,使用一组预测器来分配两个类别中的一个。

话虽如此,要使用逻辑回归来解决图像分类问题,我首先需要准备数据。输入应该与从Scikit-Learn开始的其他模型完全相同,即特征矩阵 X 和标签 y

由于本文的目标是展示如何为一个特定的问题构建一个图像分类器,所以我不关注算法的调整,而是使用逻辑回归的默认设置。让我直接跳到结果!

Confusion matrix for logistic regression’s predictions on the test set

上面我给出了测试集的结果,因此模型无法用于训练的部分数据(20%的数据)。这看起来很棒,实际上可能有点好得难以置信。让我们检查几个正确/错误分类图像的例子。

Correctly classified images

Misclassified images

5 张图片中有 4 张被错误分类,这背后的逻辑非常明显。这是一些模型实际上不能做任何事情的转换屏幕。第二个屏幕来自《超级马里奥》中的关卡地图,与游戏的其余部分明显不同(这里不是平台化游戏)。但是,我们也可以看到,该模型正确地分类了另一张地图(正确分类的图像中的图像 3)。

卷积神经网络

这部分显然会比逻辑回归复杂一点。第一步包括以特定的方式存储图像,这样 Keras 就可以施展它的魔法了:

mario_vs_wario/
    training_set/
        mario/
            mario_1.jpg
            mario_2.jpg
            ...
        wario/
            wario_1.jpg
            wario_2.jpg
            ...
    test_set/
        mario/
            mario_1.jpg
            mario_2.jpg
            ...
        wario/
            wario_1.jpg
            wario_2.jpg
            ...

这个目录树显示了我如何为这个特定的项目构建文件夹和文件。下一部分是数据扩充。这个想法是对可用的图像应用一些随机变换,以允许网络看到更多独特的图像用于训练。这将防止过度拟合并导致更好的泛化。我只使用了一些变换:

  • 重新缩放-在进行任何其他处理之前,数据将乘以的值。原始图像由 0-255 范围内的 RGB 系数组成。对于模型来说,这样的值可能太高而无法处理(以典型的学习率),因此乘以 1/255 的因子会将变量重新调整到 0-1 的范围内
  • shear _ range 用于随机应用剪切变换
  • zoom_range —用于随机缩放图片
  • horizontal_flip —用于水平随机翻转一半的图像(当没有水平不对称的假设时相关,例如真实世界的图片)。我决定不使用这个功能,因为在视频游戏截图的情况下,这将毫无意义(数字等)。)

当指定图像的路径时,我还确定了我要输入到神经网络的图像的大小(64x64,与逻辑回归相同)。

下面我展示了一个应用一些变换后的图像的例子。我们看到图像在两侧被拉伸。

现在是时候定义 CNN 了。首先,我初始化 3 个卷积层。在第一个例子中,我还需要指定输入图像的形状(64x64,3 个 RGB 通道)。后来,Keras 自动处理大小。对于所有这些,我使用 ReLU(校正线性单位)激活功能。

卷积层之后是扁平化。由于最后两层基本上是一个常规的人工神经网络分类器,我需要将卷积层的数据转换成 1D 向量。在这两个密集层之间,我还使用了 dropout。简单来说,dropout 在训练过程中忽略指定数量的神经元(随机选择)。这是一种防止过度拟合的方法。最后一个密集图层使用 sigmoid 激活函数,并将返回给定观测值属于其中一个类的概率。最后一步基本上就是逻辑回归所做的。

现在是时候运行 CNN 了(这可能需要一段时间……)。我使用 ADAM 作为优化器,选择二进制交叉熵作为该二进制分类任务的损失函数,并使用准确度来评估结果(不需要使用不同的度量,因为在这种特定情况下,准确度是我感兴趣的)。

那么神经网络表现如何呢?让我们看看!

Confusion matrix for CNN’s predictions on the test set

嗯,精确度低于逻辑回归的情况,但对于这样一个快速建立的模型来说,它仍然是非常好的。可以通过改变卷积/密集层的数量、改变丢失、对图像执行额外的变换等等来微调网络。也可能是转换隐藏了图像中的一些数据(例如图像底部的摘要栏)。事实上,我最初怀疑这个条可能在识别图像中起重要作用,因为它出现在几乎所有的截图中,并且在两个游戏之间略有不同。但是我一会儿会回来。

现在是时候检查几个正确/错误分类图像的例子了。乍一看,不同之处在于,在这种情况下,没有像屏幕转换这样明显的错误分类例子。

Correctly classified images

Misclassified images

用石灰解释分类

作为奖励,我尝试用 LIME(本地可解释模型不可知解释)解释 CNN 的图像分类。模型不可知意味着 LIME 可以应用于任何机器学习模型。它基于修改单个观察的特征值并观察对预测的影响的思想。我强烈推荐介绍这个想法的论文[2]。

下面我展示了应用石灰解释图像的结果。绿色区域表示对预测类别的积极影响,红色表示消极影响。从正确分类的案例中,我们看到角色总是在绿色区域。这是符合逻辑的。然而,对于不真实的负面情况,一些图像也只有一种颜色。这提供了一些见解,但我认为通过额外的修补,甚至可以从石灰解释中提取更多。

LIME explanation for correctly classified images

LIME explanation for misclassified images

结论

在本文中,我介绍了如何将一个随机的想法快速转化为一个图像分类项目。这两种方法在数据集上都表现得很好,我相信 CNN 可以通过一些调整获得更好的成绩。

进一步修补的一些潜在想法:

  • 添加更多游戏(不同的平台游戏或不同的马里奥/瓦里奥系列)来研究模型在多职业环境中的表现
  • 准备数据的不同方法——我可以从当前图像的中间截取更大的图像(128x128,因为两个视频都有更大的分辨率)。这也可以解决底部摘要栏的潜在问题。
  • 在 CNN 的数据生成步骤中添加额外的图像变换
  • 尝试检测图像中的马里奥/瓦里奥(物体检测问题)

我希望你喜欢这篇文章。如果您对框架或模型的潜在改进有任何建议,请在评论中告诉我!你也可以通过 Twitter :)联系我

本文中使用的代码可以在我的 GitHub 上找到。

参考资料:

[1] Keras 在 CNN 上的博文:https://blog . Keras . io/building-powerful-image-class ification-models-using-very-little-data . html

[2]石灰纸:arxiv.org/pdf/1602.04…

免责声明:我不拥有任何与 YouTube 内容或视频相关的视频游戏的权利。

在线零售数据的购物篮分析

原文:towardsdatascience.com/market-bask…

你有没有注意到,在杂货店里,面包和牛奶往往离得很远,尽管它们通常是一起购买的?这是为什么呢?这是因为他们想让你逛遍整个商店,注意面包和牛奶之间的其他商品,也许会买更多的商品。这是一个应用市场篮子分析 (MBA)的完美例子。MBA 是一种建模技术,其理论基础是,如果你购买了某一套商品,你或多或少会购买另一套商品。这是一项用于发现关联规则的基本技术,可以帮助增加公司的收入。

在我之前的一篇文章(预处理大型数据集:50 万以上实例的在线零售数据)中,我解释了如何处理 50 万以上观察值的庞大数据集。我将使用相同的数据集来解释 MBA,并找到潜在的关联规则。

正在使用的软件包有:

  • plyr —函数 ddply 所必需的,该函数允许根据指定的标准创建项目集
  • arules——一个非常有用的包,有许多选项,其中一个是函数 apriori ,它可以根据一起购买的商品的频率来查找关系
  • arulesVizarules包的扩展,具有关联规则和频繁项目集的各种可视化技术

已经使用 ddply 功能创建了项目集;对于数据框的每个子集,将应用一个函数,并将结果合并到一个数据框中。我们将按变量发票号进行拆分。物品清单已写入“Items_List.csv”文件,交易已使用 read.transactions 函数扔入“篮子”中。以下是篮子的摘要:

我们可以在上面看到哪些是最常购买的项目,以及项目集长度分布和基本统计数据。挺有用的!人们平均购买 23 件商品。

看看前 15 本畅销书可能会很有意思。可以改变 topN 参数或将类型切换到“相对”状态。

接下来,使用先验函数,我们能够生成规则。我们将支持度设置为 0.005,它应该很小,因为我们有一个大的数据集,否则我们可能会得到很少的规则。置信度设置为 75%。总共生成了 678 条规则:

这些规则已经按照可信度降序排列,在下面的总结中,我们可以看到规则的长度分布(从 2 到 6)。我们也看到最小和最大的支持、信心和提升。我们看到最大升力接近 122%。最小值是 8.691%,这表明这些项目出现的频率比预期的少得多。这两个信息在 MBA 中都非常有用——非常小的提升不应该被忽略!

arulesViz 软件包让我们能够可视化我们的发现。我们在下面看到我们所有规则的散点图。暗红色的点表示高度提升。那些规则往往有较高的可信度,但支持度较低。

我们可以通过在先验函数中指定 maxlen 来轻松控制规则的最大长度。这是在创建 basket_rules2 时完成的。当我们希望规则简洁时,这非常有用。在这种情况下, maxlen 被设置为 3。

另一种可视化规则的方式是下图。我们在图表中看到一些圆圈。圆圈越大,支撑越大。圆圈的颜色与升力有关。大圆圈代表支持度高的规则。

也可以将项目作为目标来生成规则。例如,我们可能对顾客在喝咖啡之前可能会买什么感兴趣。在先验函数中,我们将外观设置为带有 default = "lhs" (左手边)和 rhs = "COFFEE" 的列表。我们发现了 5 种通常与咖啡搭配的食物。

糖之后顾客可能会买什么?让我们来了解一下!

当然是咖啡!:)

MBA 吸引了众多零售公司的目光。零售业本质上是高度以客户为中心的,零售商正在不遗余力地寻找新的方法来更好地了解他们的客户。利用 MBA,零售商不仅可以确定目标市场,还可以通过创造、提供和交流卓越的客户体验来扩大客户群。MBA 允许零售商快速查看客户购物篮的大小、结构、数量和质量,以了解购买产品的模式。然而,MBA 给零售业从业者带来的好处要多得多。

完整的 R 代码,请访问我的 GitHub 简介。

用相似方法进行市场评估

原文:towardsdatascience.com/market-eval…

探索当前市场状况的一个方法是将今天与历史上类似的时期进行比较,然后观察在那个时期什么成功了,什么失败了。尽管小样本和不断变化的世界存在明显的缺陷,但这种方式的分析仍然是政策、投资和社会努力的有趣起点。

探索这一点的一个策略是收集过去几十年中一系列指标的数据,以表明这两年有多相似,至少在感兴趣的指标方面是如此。国际货币基金组织提供了大量的时间序列数据——这里我只关注美国和国内的数据来源。仅这一项就为国家的金融、宏观经济和社会状况提供了 1500 多项指标,可以通过应用一些启发法来缩小范围:

  • 仅包括以通货膨胀调整值或百分比表示的变量。
  • 不要为同一个概念包含多个变量,以避免多重共线性和特定指标的权重过大。

这些变量被归一化以消除比例效应。宏观经济变量的一个问题是,它们通常是非平稳的,这是一种奇特的说法,即它们受到前几年价值的严重影响。一个明显的例子是国内生产总值,我们预计国内生产总值将只是一个小的(希望是积极的)不同于去年的国内生产总值。一种方法是只考虑 GDP 的变化量,或者一年和前一年的 GDP 差异。这种转换也适用于所有使用的指标。

每个经过处理的指标在给定年份的值被表示为一个向量。

基本上,向量定义了空间中的一个点。为了评估两个点有多相似,我们可以测量这两个点有多接近。通过将指标存储为向量,我们可以使用基于向量的距离度量,例如欧几里德距离,来评估两个向量有多相似。

评估每一对年份之间的相似性可以让我们绘制出相似性如何随时间演变的图表,如下图所示的 2008 年。值得注意的是,其他衰退年份显示出与 2008 年的高度相似性,2008 年本身就是一个衰退年。

或者,我们可以绘制一个相似性热图,尽管这很难解释。

技术札记

变量的非平稳性

变量的非平稳性可以通过变量的自相关来检测,自相关测量变量与不同年份前的过去值的相关程度,其滞后

GDP showing significant autocorrelation, indicative of non-stationarity

差异 GDP 变量的自相关如下所示,其快速下降表明非平稳性已被消除。这使得它的积分阶为 1,即 I(1)。

市场组合建模 101 —第二部分(贡献图)

原文:towardsdatascience.com/market-mix-…

Source: Pixabay

大家好。新年快乐!

在我的上一篇文章中,我已经向您介绍了市场组合建模的概念。如果您想要复习,请点击下面的链接:

https://towards data science . com/market-mix-modeling-mmm-101-3d 094 df 976 f 9

在这篇文章中,我将解释如何解读贡献图,以及应该避免哪些常见的陷阱。

那么,什么是贡献图呢?

贡献图是一种直观的方式来表示哪些营销投入推动了销售,以及每个营销投入的影响有多大。以视觉方式展现市场现实,总是有助于减轻时间紧迫的客户的认知负担。

贡献图表的类型:

贡献图通常以两种方式绘制:

1.总计为 100 的绝对贡献

2.总计为 100 的非绝对贡献

  1. 总计为 100 的绝对贡献

为了解释上面的贡献图,我们假设已经销售了 100 个单位的产品。

在售出的 100 个单位中,即使营销人员不投资任何形式的广告,也会售出 53 个单位。基本上,这 53 个单位的销售是因为品牌在市场上的资产和它在过去在客户心目中创造的意识。类似地,通过电视广告售出 7 台,通过消费者促销和 BTL 促销各售出 3 台。

正确解释价格是理解 MMM 贡献的关键。很多时候,人们被误导了,因为当提到代表价格的负号时。请注意,当我们用价格上的负号对上图中的贡献进行求和时,总和是 44 而不是 100。

如果我们忽略价格上的负号,贡献的总和将是 100。因为大多数品牌的销量和价格呈负相关(该死的苹果!),价格贡献用负号表示,以表示它可能导致的销售损失量。

此处,价格的负号表示由于价格上涨,损失了 28 个销售单位。这是一个概念性的概念,描述了如果价格没有增加,可以获得 28 个额外的销售单位。

除了价格之外,出于同样的原因,竞争对手的活动在贡献上也用负号表示。

2。 非绝对贡献总和为 100

第一种解释贡献的方法对一些人或客户来说有点混乱。因此,有另一种方法可以用来解释结果。

在上面的图表中,我们可以看到总贡献总和为 100%,负号保持不变。

因此,从这张图表中我们可以看出,该品牌售出了 162 件(所有积极贡献的总和)。在售出的 162 个单元中,118 个单元的销售来自基础和分销。电视广告等推动了 17 台的销售。由于价格上涨,已经失去了 62 个销售单位。因此,总销售量为 100 台。

解释贡献图时要避免的陷阱:

1。 仅仅依靠捐款:

市场组合模型用整体方法解释。仅仅使用贡献百分比不是解决市场组合建模问题的正确方法。贡献图之后是计算投资回报率。

可能会有某个变量显示贡献与使用的数据一致,但会显示不稳定的 ROI 数字。在这种情况下,贡献被调整以获得所有的结果。

2。 不将贡献与基准进行比较

建议将模型的贡献与类似品牌/类别的基准数据进行比较,以衡量贡献结果的准确性。这有助于在到达 ROI 计算阶段之前验证结果。从领域的角度来看,可以调整模型以使结果更加准确。

3。 平衡统计和域:

一些 MMM 模型在统计上是稳健的,但可能没有商业意义,反之亦然。领域知识应该与统计数据结合使用,以利用业务洞察力。

所以,这些是在 MMM 上工作时需要考虑的一些事情。当然,MMM 是一个很大的话题,并且有进一步的细微差别。我希望在不久的将来写更多关于这个话题的文章。

如果你喜欢我的文章,给它一些掌声,或者更好地与你的朋友或同事分享。

最近,很多人问我是否做市场组合建模/营销分析方面的咨询。

答案是肯定的。您可以将您的咨询问题发送到www.arymalabs.com

领英:www.linkedin.com/in/ridhima-…

推特: @kumar_ridhima

版权所有 2018www.ridhimakumar.com版权所有。

市场组合建模(MMM) — 101

原文:towardsdatascience.com/market-mix-…

Source: tvba.co.uk

市场组合建模(MMM)是一种技术,有助于量化几种营销投入对销售或市场份额的影响。使用 MMM 的目的是了解每项营销投入对销售额的贡献,以及每项营销投入的花费。

MMM 有助于确定每项营销投入在投资回报方面的有效性。换句话说,投资回报率(ROI)较高的营销投入作为媒介比投资回报率较低的营销投入更有效。

MMM 使用回归技术,通过回归执行的分析进一步用于提取关键信息/见解。

在这篇文章中,我将谈论与理解 MMM 相关的各种概念。

1。 多元线性回归:

如前所述,市场组合建模使用多元线性回归原理。因变量可以是销售额或市场份额。通常使用的独立变量有分销、价格、电视支出、户外活动支出、报纸和杂志支出、线下促销支出和消费者促销信息等。如今,一些营销人员大量使用数字媒体来提高品牌知名度。因此,像数字消费、网站访客等输入。也可用作 MMM 的输入。

因变量和预测变量之间形成一个方程。这个方程可以是线性的,也可以是非线性的,这取决于因变量和各种营销投入之间的关系。有些变量,如电视广告,与销售呈非线性关系。这意味着电视 GRP 的增长与销售额的增长不成正比。我将在下一节中更详细地讨论这一点。

回归分析生成的 betas 有助于量化每个输入的影响。基本上,beta 表示投入值增加一个单位将增加 Beta 单位的销售额/利润,同时保持其他营销投入不变。

Sales Equation

2。 线性和非线性影响的预测因子:

某些变量与销售额呈线性关系。这意味着随着我们增加这些投入,销售额将持续增长。但是像电视 GRP 这样的变量对销售没有线性影响。电视 grp 的增加只会在一定程度上增加销售额。一旦达到饱和点,GRP 的每一个增量单位对销售的影响都将减少。因此,对这种非线性变量进行一些转换,以便将它们包含在线性模型中。

电视 GRP 被认为是一个非线性变量,因为根据营销人员的说法,广告只会在一定程度上引起消费者的注意。超过某一点,增加广告曝光不会在顾客中产生任何进一步的增加的意识,因为他们已经知道该品牌。

因此,为了将 TV GRP 视为建模输入之一,将其转换为 adstock。

电视广告由两部分组成。

a. **收益递减:**电视广告的基本原理是,电视广告的曝光在一定程度上在消费者的头脑中创造了意识。除此之外,随着时间的推移,接触广告的影响开始减弱。GRP 的每一个增量对销售或认知度的影响都会降低。因此,增量 GRP 产生的销售额开始减少并保持不变。从上图中可以看出这种影响,其中电视 GRP 和销售额之间的关系是非线性的。这种类型的关系可以通过计算 GRP 的指数或对数来获得。

b. **结转效应或衰减效应:**过去的广告对现在销售的影响被称为结转效应。一个称为 lambda 的小部分乘以上个月的 GRP 值。这一部分也被称为衰减效应,因为前几个月的广告影响会随着时间的推移而衰减。

3。 基础销售和增量销售:

在市场组合建模中,销售分为两个部分:

**a .基础销售额:**基础销售额是营销人员不做任何广告而得到的。这是多年来建立的品牌资产带来的销售额。基本销售额通常是固定的,除非经济或环境因素发生变化。

**b .增量销售:**电视广告、平面广告、数字消费、促销等营销活动产生的销售。总增量销售额被分割成每个输入的销售额,以计算对总销售额的贡献。

4。 贡献图表:

贡献图是表示每项营销投入的销售额的最简单方法。每个营销投入的贡献是其 beta 系数和投入值的乘积。

例如:报纸贡献= β*报纸支出

为了计算贡献率%,将每个投入的贡献率除以总贡献率。我将在 MMM 101 第 2 部分详细阐述贡献图的解释。

5。 深潜

MMM 结果可进一步用于执行深潜分析。通过深入了解哪些活动或创意比其他活动或创意更有效,可以评估每个活动的效果。它可以用于按类型、语言、渠道等对创意进行复制分析。

从深潜中获得的洞察力被考虑用于预算优化。资金从低绩效渠道或类型转移到高绩效渠道/类型,以增加整体销售或市场份额。

6。 预算优化

对于任何企业来说,预算优化都是出于规划目的而采取的关键决策之一。

MMM 有助于营销人员优化未来支出和最大化效益。使用 MMM 方法,可以确定哪些介质比其他介质效果更好。然后,完成预算分配,将资金从低投资回报率媒介转移到高投资回报率媒介,从而在保持预算不变的同时最大限度地提高销售额。

各位,这是一个关于市场组合建模的简介。

请继续关注 MMM 上的更多文章。

如果你喜欢我的文章,给它一些掌声,或者更好地与你的朋友或同事分享。

页(page 的缩写)最近,很多人问我是否做市场组合建模/营销分析方面的咨询。

答案是肯定的。您可以将您的咨询问题发送到 www.arymalabs.com/[的](www.arymalabs.com/)

领英:www.linkedin.com/in/ridhima-…

版权所有 2019 www.arymalabs.com 保留所有权利。

Python 中的马尔可夫链蒙特卡罗

原文:towardsdatascience.com/markov-chai…

一个完整的现实世界实现

在过去的几个月里,我在数据科学世界里反复遇到一个术语:马尔可夫链蒙特卡罗。在我的研究实验室里,在播客里,在文章里,每当我听到这个短语时,我都会点头,认为这听起来很酷,只是模糊地知道任何人在谈论什么。有几次我试图学习 MCMC 和贝叶斯推理,但每次我开始阅读这些书籍时,我很快就放弃了。恼怒之下,我求助于学习任何新技能的最佳方法:将它应用于一个问题。

利用我一直想探索的一些睡眠数据和一本基于实践应用的书(黑客贝叶斯方法网上免费提供),我终于通过一个现实世界的项目学会了马尔可夫链蒙特卡罗。和往常一样,当我把技术概念应用到问题中时,理解它们比把它们作为纸上的抽象概念阅读要容易得多(也更令人愉快)。这篇文章介绍了用 Python 实现马尔可夫链蒙特卡罗,最终教会了我这个强大的建模和分析工具。

这个项目的全部代码和数据在 GitHub 上。我鼓励任何人看一看并将其用于自己的数据。这篇文章的重点是应用和结果,所以有很多高层次的主题,但我试图为那些想了解更多的人提供链接!

简介

我的 Garmin Vivosmart 手表根据心率和运动跟踪我何时入睡和醒来。它不是 100%准确,但真实世界的数据从来都不是完美的,我们仍然可以通过正确的模型从嘈杂的数据中提取有用的知识!

Typical Sleep Data

这个项目的目标是使用睡眠数据创建一个模型,该模型将睡眠的后验概率指定为时间的函数。由于时间是一个连续变量,指定整个后验分布是困难的,我们转向近似分布的方法,如马尔可夫链蒙特卡罗(MCMC)。

选择概率分布

在我们开始使用 MCMC 之前,我们需要确定一个合适的函数来模拟睡眠的后验概率分布。做到这一点的一个简单方法是目视检查数据。我入睡时的观察结果作为时间的函数如下所示。

Sleeping Data

每个数据点表示为一个点,点的强度表示特定时间的观察次数。我的手表只记录我入睡的那一分钟,所以为了扩展数据,我在精确时间的两边的每一分钟都加上了点。如果我的手表显示我在晚上 10:05 睡着了,那么之前的每一分钟都表示为 0(醒着),之后的每一分钟都得到 1(睡着了)。这将大约 60 个晚上的观察扩展为 11340 个数据点。

我们可以看到,我倾向于在晚上 10:00 后入睡,但我们希望创建一个模型,以概率的形式捕捉从清醒到入睡的过渡。我们可以为我们的模型使用一个简单的阶跃函数,它在一个精确的时间从清醒(0)变为睡眠(1),但是这并不代表数据中的不确定性。我不是每晚都在同一时间睡觉,我们需要一个函数来模拟一个渐进的过渡过程,以显示可变性。给定数据的最佳选择是在 0 和 1 之间平滑过渡的逻辑函数。以下是睡眠概率随时间变化的逻辑方程

这里,β (beta)和α (alpha)是我们在 MCMC 期间必须学习的模型参数。具有不同参数的逻辑函数如下所示。

逻辑函数符合数据,因为睡着的概率逐渐变化,捕捉到我睡眠模式的可变性。我们希望能够在函数中插入一个时间 t,并得出睡眠的概率,它必须在 0 和 1 之间。我们可以得到一个*概率,而不是对问题“我晚上 10:00 睡着了吗”的直接回答是或否。*为了创建这一模型,我们使用数据,通过一种称为马尔可夫链蒙特卡罗的技术,找到最佳的α和β参数。

马尔可夫链蒙特卡罗

马尔可夫链蒙特卡罗是指一类从概率分布中进行抽样的方法,以构建最可能分布。我们不能直接计算逻辑分布,因此我们为函数的参数(α和β)生成数千个值(称为样本),以创建分布的近似值。MCMC 背后的想法是,随着我们产生更多的样本,我们的近似值越来越接近实际的真实分布。

马尔可夫链蒙特卡罗方法有两个部分。蒙特卡洛是指利用重复随机样本获得数值答案的通用技术。蒙特卡洛可以被认为是进行许多实验,每次改变模型中的变量并观察反应。通过选择随机值,我们可以探索参数空间的大部分,变量的可能值的范围。下面显示了使用变量的正态先验的问题的参数空间(稍后将详细介绍)。

显然,我们无法尝试这些图中的每一个点,但通过从高概率区域(红色)随机取样,我们可以为我们的问题创建最可能的模型。

马尔可夫链

一个马尔可夫链是一个下一个状态只依赖于当前状态的过程。(该上下文中的状态指的是给参数赋值)。马尔可夫链是无记忆的,因为只有当前状态是重要的,而不是它是如何到达那个状态的。如果这有点难以理解,考虑一个日常现象,天气。如果我们想预测明天的天气,只需利用今天的天气就可以得到合理的估计。如果今天下雪,我们会查看显示下雪后一天天气分布的历史数据,以估计明天天气的可能性。马尔可夫链的概念是,我们不需要知道一个过程的整个历史来预测下一个输出,这是一种在许多现实世界情况下都适用的近似方法。

将马尔可夫链和蒙特卡罗的思想结合在一起,MCMC 是一种基于当前值重复绘制分布参数的随机值的方法。值的每个样本都是随机的,但是值的选择受到当前状态和假设的参数先验分布的限制。MCMC 可以被认为是逐渐收敛到真实分布的随机游走。

为了得出α和β的随机值,我们需要假设这些值的先验分布。由于我们事先没有关于参数的假设,我们可以使用正态分布。正态或高斯分布由平均值和方差定义,前者显示数据的位置,后者显示分布。具有不同平均值和分布的几种正态分布如下:

我们正在使用的特定 MCMC 算法被称为 Metropolis Hastings 。为了将我们观察到的数据与模型联系起来,每次绘制一组随机值时,算法都会根据数据对它们进行评估。如果它们与数据不一致(我在这里做了一点简化),这些值将被拒绝,模型将保持当前状态。如果随机值与数据一致,则这些值被分配给参数并成为当前状态。这个过程持续特定数量的步骤,模型的精度随着步骤数量的增加而提高。

综上所述,在我们的问题中,马尔可夫链蒙特卡罗的基本过程如下:

  1. 为逻辑函数的参数α和β选择一组初始值。
  2. 根据当前状态,为 alpha 和 beta 随机分配新值。
  3. 检查新的随机值是否与观察值一致。如果没有,则拒绝这些值并返回到以前的状态。如果是,则接受这些值作为新的当前状态。
  4. 重复步骤 2 和 3,重复指定的迭代次数。

该算法返回它为 alpha 和 beta 生成的所有值。然后,我们可以使用这些值的平均值作为逻辑函数中α和β最可能的最终值。MCMC 不能返回“真实”值,而是近似的分布。给定数据的睡眠概率的最终模型将是具有α和β平均值的逻辑函数。

Python 实现

上面的细节在我脑海里过了很多遍,直到我用 Python 应用了它们!亲眼看到结果比阅读别人的描述更有帮助。为了用 Python 实现 MCMC,我们将使用 PyMC3 贝叶斯推理库。它抽象掉了大部分细节,使我们能够创建模型而不会迷失在理论中。

下面的代码用参数alphabeta、概率p和观察值observed创建完整的模型,step变量指的是特定的算法,sleep_trace保存模型生成的所有参数值。

(查看笔记本中的完整代码)

为了了解运行这段代码时会发生什么,我们可以查看模型运行期间生成的所有 alpha 和 beta 值。

这些被称为跟踪图。我们可以看到,每个状态都与之前的状态相关,即马尔可夫链,但值会显著振荡,即蒙特卡洛采样。

在 MCMC 中,通常会丢弃高达 90%的轨迹。该算法不会立即收敛到真实分布,并且初始值通常不准确。参数的后期值通常更好,这意味着我们应该使用它们来构建我们的模型。我们使用了 10000 个样本,并丢弃了前 50%,但一个行业应用可能会使用数十万或数百万个样本。

给定足够的步骤,MCMC 收敛到真实值,但是评估收敛性可能是困难的。我不会在这篇文章中讨论这个话题(一种方法是测量轨迹的自相关),但如果我们想要最精确的结果,这是一个重要的考虑因素。PyMC3 内置了评估模型质量的函数,包括轨迹和自相关图。

pm.traceplot(sleep_trace, ['alpha', 'beta'])
pm.autocorrplot(sleep_trace, ['alpha', 'beta'])

Trace (left) and autocorrelation (right) plots

睡眠模式

最终构建并运行模型后,就该使用结果了。我们将最后 5000 个α和β样本的平均值作为参数的最可能值,这允许我们创建模拟后验睡眠概率的单一曲线:

该模型很好地表示了数据。此外,它还捕捉到了我睡眠模式中固有的可变性。这个模型给了我们一个概率,而不是一个简单的是或否的答案。例如,我们可以查询模型以找出在给定时间我睡着的概率,并找出睡着的概率超过 50%的时间:

**9:30  PM probability of being asleep: 4.80%.
10:00 PM probability of being asleep: 27.44%.
10:30 PM probability of being asleep: 73.91%.****The probability of sleep increases to above 50% at 10:14 PM.**

虽然我试着在晚上 10:00 上床睡觉,但很明显大多数晚上都不会这样!我们可以看到我睡觉的平均时间是晚上 10:14 左右。

这些值是给定数据的最可能的估计值。但是,这些概率存在不确定性,因为模型是近似的。为了表示这种不确定性,我们可以使用所有α和β样本而不是平均值来预测给定时间的睡眠概率,然后绘制结果的直方图。

这些结果更好地说明了 MCMC 模型真正的作用。该方法不会找到单一答案,而是可能值的样本。贝叶斯推理在现实世界中很有用,因为它用概率来表达预测。我们可以说有一个最可能的答案,但更准确的回答是任何预测都有一个取值范围。

尾流模型

我可以利用清醒时的数据为我早上醒来时找到一个相似的模型。我试着总是在早上 6:00 起床,但是我们可以看到这并不总是发生!下图显示了从睡眠到清醒的过渡的最终模型以及观察结果。

我们可以查询这个模型,找出我在给定时间睡着的概率,以及我最有可能醒来的时间。

**Probability of being awake at 5:30 AM: 14.10%. 
Probability of being awake at 6:00 AM: 37.94%. 
Probability of being awake at 6:30 AM: 69.49%.****The probability of being awake passes 50% at 6:11 AM.**

看来我得好好修理一下那个警报器了!

睡眠持续时间

我想创建的最后一个模型——出于好奇和实践——是我的睡眠时间。首先,我们需要找到一个函数来模拟数据的分布。提前,我觉得会很正常,但是我们只能通过检查数据来发现!

正态分布可以工作,但是它不能捕捉到右边的外围点(我严重睡过头的时候)。我们可以使用两个独立的正态分布来表示这两种模式,但我将使用一个偏态正态分布。偏斜正态有三个参数:均值、方差和α,即偏斜度。所有这三个都必须从 MCMC 算法中学习。以下代码创建模型并实现 Metropolis Hastings 采样。

现在,我们可以使用三个参数的平均值来构建最可能的分布。下面是数据顶部的最终偏态正态分布。

看起来很合身!我们可以查询该模型,以找到我至少获得一定量睡眠的可能性以及最可能的睡眠持续时间:

**Probability of at least 6.5 hours of sleep = 99.16%.
Probability of at least 8.0 hours of sleep = 44.53%.
Probability of at least 9.0 hours of sleep = 10.94%.****The most likely duration of sleep is 7.67 hours.**

我对那些结果并不完全满意,但是作为一名研究生你能期待什么呢?

结论

完成这个项目再一次向我展示了解决问题的重要性,最好是有现实应用的问题!在使用马尔可夫链蒙特卡罗构建贝叶斯推理的端到端实现的过程中,我学到了许多基础知识,并在这个过程中乐在其中。我不仅了解了一点我的习惯(以及我需要改进的地方),现在我终于可以理解大家说的 MCMC 和贝叶斯推断是什么了。数据科学就是不断地向您的技能库中添加工具,而最有效的方法就是找到问题并开始行动!

一如既往,我欢迎反馈和建设性的批评。可以通过推特 @koehrsen_will 联系到我。

马尔可夫链——对生活的一种理解

原文:towardsdatascience.com/markov-chai…

给定现在,未来独立于过去

在我最近上的另一堂课的第 24 张幻灯片中,有一句看似无关紧要的话,却完全揭示了我们是如何思考和行动的(至少我是这样思考和行动的)。在我开始之前,让我给你简单介绍一下我是如何偶然发现这句话的。我还将冒昧地提供一个小窥视模型的工作原理,其标语可能价值数百万美元。

最近我开始看这些关于强化学习的讲座,由谷歌 deep mindalpha go 团队的首席程序员大卫·西尔弗主讲。对于外行人来说,强化学习是机器学习的一个子领域,它处理基于代理人收到的奖励做出决策的过程。请允许我更清楚地说明这一点:代理人可以实现的任何目标都可以用期望累积报酬的最大化来描述。通俗地说,如果一个代理想要完成它的目标,那么与它为达到目标所采取的行动相关的回报将会最大化。

An example of RL for the robot agent. Image from Safaribooksonline

举个例子,考虑让一个人形机器人学会走路的过程;正奖励可能由机器人到达目的地(或在目的地的方向上迈出每一步)构成,而负奖励可能与机器人摔倒的动作相关联,或采取使其远离目标的动作相关联(这是一个天真的例子,因为如果机器人正在探索更好的全局路径以达到目标,则它可能实际上在远离目标时获得正奖励)。正是通过这些积极和消极奖励的结合,机器人最终学会了如何到达它的潜在目的地。

这种过程可以很容易地通过代理人处于某个状态,并通过考虑所有立即可获得的未来状态的回报并朝着回报最大的状态移动而采取行动来可视化(是的,这听起来有点不真实,但相信我这不是)。不难分析,一个贪婪的代理人会采取一种行动,这种行动对应于从当前状态可能获得的最高回报。

马尔可夫链是强化学习的支柱,因为它们以一种非常简单的方式帮助建立决策的概念;代理已经处于的状态的整个序列可以归结为它的当前状态,即,下一个可到达的状态可以由代理的当前状态来预测,而不管代理已经处于的状态的历史序列。下图简洁地说明了这个想法:

A simple Markov Model

边上的标签表示从当前状态移动到下一个状态的概率。例如,如果今天下雨,明天下雪的概率是 0.02,下雨的概率是 0.8 等等。

另一个解释马尔可夫链的简单例子是:假设你正在访问一个群岛,有桥连接着这些岛屿。这些桥代表了从一个岛移动到另一个岛的可能性。你明天要去的岛是由你今天所在的岛决定的,你以前的职位与这个决定无关。

对于面向数学的,马尔可夫模型的公式可以描述为:

一个状态 S_t+1马尔可夫当且仅当(原谅没有下标) :

解释:在给定代理处于[RHS]的整个状态序列的情况下,移动到下一个状态的概率等于在给定当前状态[LHS]的情况下,移动到下一个状态的概率。换句话说,由于过去状态的所有信息已经浓缩在代理的当前状态中,我们可以假设从当前状态到下一个状态的转移概率完全取决于当前状态。

这也可以表述为:

鉴于现在,未来独立于过去。

正是第一堂课幻灯片中的这一行,让我意识到这句话不仅适用于马尔可夫模型,也适用于我们所有人的生活。塑造我们未来的决定完全取决于我们在当前心态下做出的选择;我们过去的所有经历都隐含在我们现在的状态中,因为它们引导我们走到了今天。

对过去的选择感到遗憾不会改变宇宙中事情将如何发展的更大计划,哀叹只会让我们付出代价,并模糊我们未来的决策过程。所以,让我们试着通过充分利用我们此刻所处的状态来放下过去。

让我们通过分析给定我们当前情况下所有可能行动的回报,来模拟一个理性主体做出最优决策。还有什么比人类一直做出理性决策更好的 AI,我说的对吗!;-)

漫威电影宇宙超级英雄排名:表情可视化

原文:towardsdatascience.com/marvel-cine…

我和其他人一样喜欢好的超级英雄电影。动作场面真正构成了这些电影的大部分。

Photo by Raj Eiamworakul on Unsplash

没有超能力的超级英雄算什么?

有两个主要的超级英雄电影系列:漫威电影宇宙(MCU)和 DC 扩展宇宙(DC 扩展宇宙)。可以说, MCU 正在赢得票房和整体营销游戏。另外,我喜欢《死侍》,我为惊奇队长感到非常兴奋。因此,尽管我欣赏黑暗和坚韧不拔的 DC 扩展宇宙风格,单片机是我目前的最爱。

当我看 MCU 电影,尤其是复仇者联盟电影时,有一个问题一直困扰着我——谁是最强大的 MCU 超级英雄?

当超级英雄面对灭霸时,基于性格和力量的分析并没有太大意义。力量胜过一切,所以最好的超级英雄也应该是最强大的超级英雄。但更大的问题是——权力的数量重要还是权力的类型更重要?

我们来看数据。

一.数据

这个分析使用了上传到 Kaggle 上的超级英雄数据集这是分析和可视化的代码。数据是 2017 年 7 月从超级英雄 DB 刮来的。这项分析将超级英雄的力量列为 20 个最受欢迎的 MCU 超级英雄之一。我已经使用这篇文章作为参考排名,看看我是否可以根据数据提出一个替代方案。文章从最好到最差对以下 18 位超级英雄进行了排名:

  1. 美国队长
  2. 黑豹
  3. 托尔
  4. 奇异博士
  5. 绿巨人
  6. 蚁人
  7. 瓦尔基里
  8. 视力
  9. 冬季士兵
  10. 猎鹰
  11. 绯红女巫
  12. 蜘蛛侠
  13. 星际领主
  14. 黑寡妇
  15. 战争机器
  16. 鹰眼
  17. 钢铁侠

这份分析中的排名包括了文章中的全部 18 位以及死池和惊奇队长。

二。分析

1.能力

本文中的 20 个超级英雄总共拥有 46 种不同的能力。权力具有以下特征:

  1. 每种能力都有一个奖励点
  2. 他们可能是“极端的”

奖励积分

永生的奖励点数最高,智力和反应能力的奖励点数最低。超级英雄的标准绝对很高,因此我更爱他们。

极端权力

有更少的极端力量,这很好,因为不是所有的超级英雄都是平等的。我的猜测是,拥有最极端力量的超级英雄在战斗中会比那些拥有较少极端力量的超级英雄做得更好。

2.超级英雄和他们的力量

幂的数量

惊奇队长拥有迄今为止最多的权力…

…但是在拥有“极端”力量的超级英雄中,奇异博士拥有最多的“极端”力量。惊奇队长跌至第三,钢铁侠跌至第六。

我们看到钢铁侠在《无限战争》中被摧毁,而雷神继续战斗,所以我同意极端力量比任何力量都重要的假设。

3.超级英雄和奖励积分

每种威能都有加分。如果极端力量是最好的力量,他们应该有更多的加分。让我们来看看极端与非极端奖金点四分位数。

超能力者比非超能力者得分更多。我相信分数是超级英雄整体实力的排名方式。

三。最终排名

从最好到最差,以下超级英雄根据与其能力相关的奖励点数总数进行排名(括号内为参考文章排名):

  1. 《惊奇队长》
  2. 雷神(#3)
  3. 奇异博士(排名第四)
  4. 《死侍》
  5. 猩红女巫(#11)
  6. 钢铁侠(#17)
  7. 战争机器(#15)
  8. 绿巨人(#5)
  9. 水银(#18)
  10. 愿景(#8)
  11. 蜘蛛侠(#12)
  12. 黑豹(排名第二)
  13. 美国队长(#1)
  14. 黑寡妇(第 14 位)
  15. 星际领主(#13)
  16. 鹰眼(#16)
  17. 猎鹰(#10)
  18. 冬日战士(#9)
  19. 瓦尔基里(排名第七)
  20. 蚁人(排名第六)

索尔和奇异博士各上升一位,而《美国队长》和《黑豹》则下降了。

这主要是因为如果一个超级英雄主要拥有像“智力”和“敏捷”这样的无用能力,那么大量的能力并不意味着什么。

此外,在下面的情节中,很明显,像雷神这样的超级英雄与其他 19 个超级英雄相比,只拥有平均数量的力量,但在战斗中,wayyy 比其他人更强大。

令人惊讶的是,浩克的排名在《战争机器》之后。这是因为虽然他们都有相同数量的极端力量,战争机器有更多的力量。所以,极端的权力比简单的更多权力要好。但是如果极端力量相等,拥有更多力量的超级英雄获胜。

四。结论

很多人都在谈论惊奇队长是迄今为止最强的复仇者。虽然根据这个排名这是真的,但如果超级英雄是根据他们的力量值来评分的话,惊奇队长并不比雷神强大多少。但最终,只有当他们面对灭霸时,我们才会知道谁是最强的复仇者。