Python 教程:模糊名称数据集的名称查找表
Python 统计教程系列
通过使用姓名成分组合提高人名匹配的准确性
这是我们探索 Python 数据世界之旅的第六篇文章。点击标题上方的链接,获得所有文章的列表。
本教程的目标是
让我们回顾一下我们在的最后一课中所取得的成绩。我们建立了一个人的名字“模糊”的名字匹配算法,通过使用一个标准化的步骤以及双变音算法的力量。
All Name Components available
该算法可以处理姓名属性中的错别字、特殊字符和其他差异,但如果 twitter 账户名称中缺少任何姓名成分,该算法就会失败。
Two Name Components Available, one only available as an abbreviation
问题是 Twitter 帐户名称是由用户连接的,因此不具有确定性(我们假设他没有使用假名,并且有可能匹配)。正如我们在上一个教程中看到的,我们试图修复某些异常,但解决方案似乎不够健壮。因此,我们必须投入额外的时间来建立一个更健壮的解决方案,通过使用组合学来解决“缺少名称组件”的问题。
获取 Jupyter 笔记本以获得额外的教程解释
作为我自己进入 Python 数据科学世界的一部分,我最近开始玩 Python 的 Jupyter Notebook 。
Jupyter Notebook 是一个开源的 web 应用程序,允许您创建和共享包含实时代码、等式、可视化和叙述性文本的文档。用途包括:数据清理和转换、数值模拟、统计建模、数据可视化、机器学习等等。
这简直太棒了,而且通过 Ancaconda 发行版几分钟就安装好了(你的机器上需要一些备用磁盘空间)。
我把它整合到了我的训练过程中,从现在开始,我将在 Jupyter 的专用笔记本中更详细地描述算法和编码的构建。
你可以在我的 Github 项目中找到本教程的 Jupyter 笔记本。使用 Jupyter 笔记本(与本教程并排),允许您在笔记本中随意摆弄和实时修改代码序列。令人惊讶的是,Github 以其原始的、易于阅读的 Web 视图格式呈现了一个 Jupyter 笔记本文件。以防你想看只读格式的!
Jupyter Notebook rendered in Github
在名称匹配过程中处理缺少的名称组件
我们想要实现的是,我们的算法能够管理丢失的名称组件。在下面的示例中,我们可以通过省略两边的第二个名称组件来实现匹配。
显然,我们想要一个通用算法,它可以处理 twitter 帐户名称中的任何复杂性,也就是说,在命名结构中也应该有可能进行匹配,如:
- “梅德医生。彼得·埃舍尔(苏黎世)"或
- “梅德医生。彼得·埃舍尔(苏黎世)”,用户打错了一个字。
我们用组合学解决的第一个问题,第二个问题,我们已经通过应用著名的双变音算法解决了。
组合和排列的简明介绍
为了在缺少元素的情况下导出任何种类的名称成分星座,我们必须理解组合学中的基本概念。
我们应该使用组合还是排列
先来回答这个问题。离开学校太久?看看这个有帮助的六分钟视频(来自 betterExplained.com),它更新了各种概念。
Permutations/Combinations explained
Python 在其标准库 itertools 中已经提供了函数置换和组合,我们想进一步研究一下。
让我们将它应用于我们的名称示例,以了解其机制。首先是组合功能
以及排列功能:
如您所见,在排列中,名称组件的顺序起着作用。有一个用于('peter','alfred')
的元组和一个用于('alfred','peter')
的元组。在组合中,名称组件的顺序与无关。
对我们来说,顺序不起作用,'peter escher'
被视为'escher peter'
在应用双音素算法之前,我们无论如何都要对姓名成分进行排序。因此我们使用组合函数。
请始终将“个人识别码”视为“排列”的摇篮。任何 pin 码锁定机制都是基于 10 个元素中给定的一组“数字”的排列数:“1234”不能解锁被“4321”锁定的屏幕。
ATM — Photo by Mirza Babic on Unsplash
为名称组合建立一个查找目录
我们现在构建一个查找目录类,它允许我们存储姓名组件组合,并将它们与我们从 Twitter API 获得的人名进行比较。
将人员添加到查找目录
方法add_person_to_lookup_table
计算所提供的姓名元组的所有组合,为每个组合生成相关联的双变音键,并将该键与唯一的个人 id 一起存储在查找目录中。
该方法的序列图如下所示,让我们看看所需的各种帮助方法。
方法:生成组合
作为第一步,我们生成名称元组的所有组合。下面的方法计算名称元组的所有组合。例如,对于 3 个元素的元组,构建的数组包括
- 三元素元组本身
- 具有 3 个元素中的 2 个元素的组合的所有元组
- 具有 3 个元素中的 1 个的组合的所有元组
例如,下面的 3 个名称元组导致下面的组合列表。
方法:将组合添加到目录
在这种方法中,我们通过将每个元组添加到我们的查找目录来构建目录。
我们的查找目录由两个字典的元组组成,这两个字典用于存储键、值对。我们的键是从名称创建的双变音元组,值是唯一的个人标识符。
__lookup_dict = ({}, {})
该方法如下所示
在第 3 行代码中,我们用名称组件元组创建了一个规范化的名称字符串。该方法如下所示
结果是排序后的串联元组名称元素的小写字符串:
该字符串用于生成双变音元组,这是我们的字典的键。
在代码行 5 和 10 中,我们检查字典中是否已经有一个具有相同键的条目。
- 如果没有,我们添加新的键,值对。
- 如果已经有一个条目,我们首先检查是否已经存储了
person_id
。如果不是,我们将person_id
添加到值数组中。
方法:将人员添加到查找目录
最后,我们准备好了方法,它允许我们将名称元组添加到查找目录中。
例如,我们将以下三个人添加到查找表中。
正如我们在输出中所看到的,我们的带有关键字'PTR'
的 Peter 条目有一个三人标识符数组。
在我们的查找目录中匹配一个名字
我们的查找目录现在已经准备好了,可能会填充人名数据,这些数据是通过我们的政府 API 检索的。
缺少的是match_name
方法,该方法允许我们查找通过 Twitter API 检索到的名称。我们现在要解决这个问题。
我们的第一步是将所有查找目录条目——与我们搜索的名称元组匹配——收集到一个结果列表中。
- 在代码行 3 中,我们通过现有的方法生成了所有名称组件组合,并对所有组合进行了迭代
- 在代码行 5 和 6 中,我们为查找准备了组合元组的键元组。
- 在代码行 7 中,我们检查我们的密钥是否存在。如您所见,我们只对双变音元组的第一个条目进行检查,它存储在查找目录的第一个条目中。我们将基于双变音元组的排序特性的完整实现作为练习。
if metaphone_tuple[0] in self.__lookup_dict[0]:
在我们的样本加载查找目录上运行match_name
方法会产生以下输出:
正如我们所看到的,我们有两个指向一个person_id
的元组和一个指向三个人的元组peter
(显然姓氏被多人重复使用)。指向一个人的二元组有相同的 id 'A123'
。这意味着我们的匹配只确认了一个人。如果我们的结果中有指向不同人的单人元组,这意味着我们的匹配不是唯一的。
因此,我们增强了我们的方法来进行这种唯一性检查(代码行 12–20):
- 我们的匹配列表中有一个或多个元组总是指向一个人吗?
- 如果是,我们发现了一个唯一的记录,否则我们返回 None
让我们在测试样本上验证该算法(如上所述,所有内容也可以作为交互式 Jupyter 笔记本使用)
所以我们准备将新的查找策略应用到我们的程序中。
重构我们现有的类
GovAPI 类扩展
我们通过集成一个NameLookupDirectory
类实例来增强我们的抽象GovAPI
类。
class GovAPI(ABC):
def __init__(self):
self._members = []
self._nameLookupDirectory = NameLookupDirectory()
我们用代码序列增强了add_person_record
,以构建我们的查找目录(代码行 22–29)
我们还为match_name
检查添加了一个新方法,当我们试图合并表记录时会调用这个方法。
def match_name(self, name_tuple):
return self._nameLookupDirectory.match_name(name_tuple)
不再需要下面的calculate_name_matching
方法。
SocialMediaAnalyzer 类重构
在这个类中,我们还必须重构calculate_name_matching
方法,我们现在调用匹配的govAPI
类实例的match_name
方法(第 5 行)。
如果我们有一个匹配(7–14),我们从 govAPI 类中检索完整的 person 记录。
记住,calculate_name_matching
方法是通过 Panda apply
方法在每个行记录上调用的,因此,该行将由附加的新列补充:
panda_data = { 'ScreenName' : self.__col_screen_name,
'Name': self.__col_name,
'Description': self.__col_description,
"FollowersCount": self.__col_followers_count,
"FriendsCount": self.__col_friends_count,
"Party": self.__col_party
}
df = DataFrame(panda_data, index=self.__labels)
df = df.apply(self.__calculate_name_matching, axis=1)
当我们再次执行程序时,Twitter 检索到的表如下所示:
在col_match1
中,我们列出了 govAPI 惟一 id,在col_match2
中,我们列出了结果元组列表,并对其进行了分析。例如
对于 Twitter 名称“Christian Levrat ”,我们在查找表中找到了三个条目:
- “christian leverat”映射到 1 个人 id (1150)
- “christian”映射到 5 个人 id
- “leverat”映射到 1 个人 id (11509
我们的匹配算法有一个肯定的匹配,因为两个条目都指向同一个人 id。
评估我们算法的假阳性
让我们检查一下误报的算法。
什么是误报?
假阳性是指当你应该得到阴性结果时,却得到了阳性结果。它有时被称为“假警报或“假阳性错误”它通常用于医学领域,但也可以应用于其他领域(如软件测试)。( Ref
假阳性意味着我们算法的准确性有问题。
假阳性 1
当我们浏览 Twitter 表时,会遇到下面的记录。
它们都指向同一个人员 id。在检查 govAPI 表中的记录时,我们得到以下记录“74 Christoph Eymann”,它没有 Twitter 帐户,因此在 Twitter 表中找不到。
哪里出了问题:
“Christophe Darbellay”和“Christoph mrgeli”曾是瑞士委员会的政治家,因此不在 govAPI 名单中,我们只对活跃成员进行了筛选。
“Christophe”和“Christoph”被转换成相同的双变音字符串,并且与“Christoph Leymann”的 govAPI 记录 74 相匹配。由于 govAPI 列表中只有一个姓“Christoph”的人,因此我们的算法会为任何姓“Christoph(e)”的人返回一个误报,并将其与“Christoph Leymann”进行匹配。govAPI 会列出两个姓“Christoph”的人吗?匹配记录会指向两个人的 id,不再是唯一的。我们的算法在这种情况下不会导致假阳性。
解决方案:
嗯,我们必须重新调整我们的算法,使它更加严格。这意味着我们改变了名称元组生成器的条件
仅允许至少将姓氏作为元素的名称组件元组。
因此,当调用者向目录中添加一个人时,我们重新调整我们的方法,要求调用者提供姓氏在元组中的位置。
def add_person_to_lookup_directory(self, person_id, name_tuple, last_name_pos):
tuples = self.generate_combinations(name_tuple)
self.add_combinations_to_directory(tuples, person_id, name_tuple[last_name_pos])
在add_combinations_to_directory
方法中,我们现在只添加包含姓氏的元组(第 3 行)。
重新运行我们的程序会产生以下匹配的统计数据。
Matching via Lookup Directory
这实际上并不比我们的第一次尝试更好(参考下面的教程),但是我们得到了一个更健壮的匹配算法。看起来使用的 Twitter 政治家列表在联邦议会的活跃成员中并不是真正最新的。但是,这是下一课要讲的内容,我们想在下一课结束数据匹配的话题,然后继续。
源代码你可以在相应的 Github 项目中找到,所有其他文章的列表在这里。
编码愉快。
Python 简介#3:使用工厂模式连接政府数据 API
Python 统计教程系列
从公共政府数据 API 中获取政治家数据,以提高分配准确性
点击标题上方的链接,查看所有教程文章的列表。
在本教程中,我们主要做两个步骤
- 首先,我们从收集的数据中生成一些图表,
- 其次,我们将一个公共政府 API 连接到我们的程序,以可靠的方式检索政府成员和党派分配
所有教程的列表可以在这里找到。
正如你已经知道的,我们对一个通用程序感兴趣,它可以在多个国家工作。为此,我们将在教程的第二部分介绍一个抽象类和工厂模式。
Plotly 图表生成
但是让我们从第一个非常简单的任务开始,为我们的表生成两个图表。
我们创建了条形图和饼图。作为基础,我们以我们的 plotly 表 CH-tw-party-list 。
代码很简单:
- 在条形图中,我们直观地显示了每个聚会累积的朋友数。
- 在饼状图中,我们汇总了每一方的 twitter 账户。
从代码摘录中可以看出,各种配置参数允许您修改图表的布局。前往 Plotly Python 开源图形库了解更多关于图表、panda 和 Plotly 的多种可能性。
正如你所看到的,我们有很多“未知数”,也就是说,我们不能仅仅通过分析 Twitter 数据元素来确定相应的一方。在本教程的第二部分,我们将连接另一个数据源来解决这个问题。
政府数据 API 工厂
Photo by Rodney Minter-Brown on Unsplash
近年来,所谓的开放政府 API 的可用性激增。它源于数据应该开放的理念,正如维基百科对术语开放数据的描述:
开放数据的理念是,一些数据应该免费提供给每个人使用和重新发布,不受版权、专利或其他控制机制的限制。
开放数据的最重要形式之一是开放政府数据(OGD),这是由执政的政府机构创建的一种开放数据形式。开放政府数据的重要性在于它是公民日常生活的一部分,甚至是看似远离政府的最日常/最平凡的任务。
获得政府数据 API 概述的一个很好的起点是 programmableweb 目录,它列出了 20,000 多个不同的 API。
政府 API 类别可在此处找到:
https://www.programmableweb.com/category/government/apis?类别=20094
政府空气污染指数的两个例子:
美国政府的数据 API:【https://www.data.gov/developers/apis T3
或者瑞士议会的瑞士政府 API:【http://ws-old.parlament.ch/ :
我们将使用瑞士议会 API 提取议会成员的个人数据(主要是政党分配),以提高我们的 twitter 匹配算法的准确性。
正如我们在第二篇教程中已经解释过的,一个公共 API 可能会返回很多信息。为了控制一个请求中返回的数据量,使用了游标或分页机制的概念。
瑞士议会 API 在一次请求中返回大约 25 条记录。最新的请求记录将附带一个属性,告诉您是否有更多可用的数据( hasMorePages=true )。
如果设置为’ true ,您可以通过添加查询参数: pageNumber=2 等获取下一页。
你可以在 API 的用户文档中找到这类信息,例如,瑞士议会 API 有一些控制输出格式、语言等的参数。
现在对 API 有了基本的了解,我们可以增强程序,使其能够从特定国家的政府 API 中读取数据。让我们深入研究代码。
增强代码 UML 图
以一种通用的方式引入政府 API 需要对我们的程序进行一些认真的设计和改进。我们的增强程序的 UML 类图如下所示(不要被复杂性淹没,所有细节将在本文后面解释)。
我们到目前为止所做工作的快速总结:
- 我们在第二个教程中创建了 GovernmentSocialMediaAnalyzer 类,它能够检索一个国家的政治家的 twitter 相关账户数据。我们使用了一种基于配置驱动的方法——基于 YAML——将特定于国家的数据抽象到一个配置文件中
- 定义了几种方法,使我们能够创建 panda 数据框,以及 plotly 特定的表格和图表。
现在我们将引入三个新类 govAPIFactory 、 govAPI (一个抽象类)和 govAPI_CH ,它们将构建一个通用的方法来连接任何类型的政府 API。
软件设计模式在软件设计中起着重要的作用,正如 Wikipedia 所描述的:
在软件工程中,软件设计模式是对软件设计中给定上下文中常见问题的通用、可重用的解决方案。它不是一个可以直接转化为源代码或机器码的成品设计。它是如何解决一个问题的描述或模板,可以在许多不同的情况下使用。设计模式是形式化的最佳实践,程序员可以用它来解决设计应用程序或系统时的常见问题。
在我们的设计中,我们将使用工厂方法模式将连接推广到政府 API,这由维基百科解释如下:
在基于类的编程中,工厂方法模式是一种创造性的模式,它使用工厂方法来处理创建对象的问题,而不必指定将要创建的对象的确切类。这是通过调用工厂方法(在接口中指定并由子类实现,或者在基类中实现并可选地由派生类重写)而不是通过调用构造函数来创建对象来完成的。
我们的设计将以策略为基础,来定义
- 一个抽象的基类(父类— GovAPI )
- 一个派生类(child — GovAPI_CH ),它将具有特定于国家的实现(如瑞士)。
- 将来,我们可以引入额外的类,例如对于英国,我们将构建实现类: GovAPI_UK
抽象基类“GovAPI”
govAPI 是一个抽象类,包含几个抽象方法。抽象方法是已声明但不包含实现的方法。
在 Python 中,抽象类是从 ABC 类派生(或继承)的,并且将有一个或多个标有@abstractmethod 的方法。
所以抽象类为你提供了一个构建计划,在我们的类 govAPI_CH 中,任何继承它的实现类。
govAPI_CH 要实现什么样的方法?
首先,*load _ government _ members()*方法的实现必须处理从政府 API 获取的政治家的数据记录。每个提取的记录——代表一个政治家的数据——必须传递给方法 add_person_record (该方法已经由 govAPI 基类实现)
现在的问题是, add_person_record 方法到底在做什么?好吧,看看下面的代码。
该方法只是为我们的个人记录准备一个目标字典。即定义的属性名(姓、名、会等)。)是我们希望用于任何 GovAPI 实现的名称。
这意味着我们检索的记录以专用政府 API 实现的形式(即以瑞士政府议会 API 的形式)必须通过使用一堆 getter 方法来转换。
这些 getter 方法要么是抽象的,要么返回空字符串。提供正确的 getter 实现是继承类( GovAPI_CH )的实现者的责任。
实现类“GovAPI_CH”
下面显示了 GovAPI_CH 的 getter 方法实现。它由一组 getter 方法组成,这些方法将从记录中返回所需的属性值。
让我们深入到方法load _ gegovernment _ members:
我们的实现使用了 python 模块 requests ,这是“一个优雅而简单的人类 HTTP 库”在本文的简介部分,我们提供了瑞士议会 API 的概述。下面的代码将使用分页机制获取数据。
我们将 URL 及其参数放在配置 YAML 文件中。
govAPIUrl: "http://ws-old.parlament.ch/"
govAPICouncillorsRes: "councillors"
govAPIParams:
- format : "json"
- 8:第一个 requests.get 将获取所有议员概况页面http://ws-old.parlament.ch/councillors?format=json&page number = 1。
- 15:如果数据记录被标记为激活,将提取详细记录
- 17:第二个请求将使用记录的 id和细节记录的 URL。也就是说,在这个例子中,我们获取了 id 为‘1358’的政治家记录:http://ws-old.parlament.ch/councillors/1358?format=json
- 19:我们将检索到的详细记录传递给方法 addPerson ,该方法将把提供的数据记录转换成目标记录(通过使用我们已经实现的 getters)。
- 20:最后,我们检查 hasMorePages 属性,如果到达最后一条记录,我们将中断循环。
上述方法将在 govAPI 函数create _ political _ from _ govAPI _ table(已经由 govAPI 父类实现)中调用,该函数将把政客记录列表转换为 Panda 数据帧。
正则模型
重要的是要认识到,只要我们基于 govAPI 抽象类实现一个特定的类,这个 Panda 数据帧的结构将与任何种类的政府 API 相同。所以我们将数据标准化,这样我们就可以用标准化的方式工作和处理它。
我们再次努力实现一个重要的设计模式,我们的目标结构(或模型),被称为规范模型。正如维基百科所描述的:
规范模型是一种用于在不同数据格式之间进行通信的设计模式。本质上:创建一个数据模型,它是所有其他模型的超集(“规范的”),并创建一个“翻译”模块或层,所有现有的模块通过它与其他模块交换数据。
从概念上讲,我们已经建立了一个迷你数据管道。对于每个政府 API,我们必须实现一个数据记录获取函数和转换规则(getters ),将数据转换为我们的标准化数据。
整个模式在 UML 序列图中可视化。
- “消费”操作由步骤 60 表示
- “转换规则”操作由步骤 80-120 表示。
- “存储”操作由步骤 130 表示
重要的是你要理解各种类的职责。 govAPI 和 govAPI_CH (红点)对外界可见( govAPIFactory , gsma )作为一个类实例。对于调用者来说,谁实现哪个方法并不重要。
govAPIFactory 类
还缺少最后一个东西,即 govAPIFactory 类,它非常简单。根据 country_code ,将创建相应的实现类实例并返回给调用者:
可以看到,这个类只有一个类方法。即 GovAPIFactory 不支持对象实例。一个工厂在一个程序中只包含一次,这被称为单例模式。
在软件工程中,单件模式是一种软件设计模式,它将一个类的实例化限制为一个。当只需要一个对象来协调整个系统的动作时,这很有用
我们在这里使用了一个派生,通过只有一个静态类方法来确保单例性。
这是相当多的内容要吸收,我们介绍了两个重要的设计模式工厂方法和规范的数据模型,以及如何生成第一对图表。
lesson3.py 程序将在 Plotly 内生成名为 CH-govapi-member-list 的表格。
今天就到这里,在下一篇文章中,我们将开始通过结合两个数据源来进行一些数据分析。
锻炼
你可以在这里找到练习教程:链接
源代码
源代码可以在这里找到(第三课目录):https://github.com/talfco/clb-sentiment
进一步阅读
如果您想更深入地了解设计模式和 Python 的主题,请阅读下面的文章"确保代码整洁:Python 的一瞥,参数化的"
原载于 2019 年 2 月 11 日dev.cloudburo.net。
Python 教程:模糊名称匹配算法
sur Python 统计教程系列
如何应对用作标识符的人名变量的可变性和复杂性?
这是我们探索 Python 数据世界之旅的第五篇文章。点击上面的链接,获取已发表文章的列表。
名称匹配的方法
在从公共来源检索的统计数据集中,(一个人的)姓名通常被视为与其他一些字段(如电子邮件、电话号码或 ID 号码)的元数据相同。在我们的样本集中是这样的情况:
当姓名是唯一的统一数据点时,正确匹配相似的姓名就变得更加重要,但是它们的可变性和复杂性使得姓名匹配成为一项极具挑战性的任务。昵称、翻译错误、同一个名字的多种拼写等等都可能导致匹配失败。(【rosette.com】T2
这正是我们现在面临的情况:
我们的 twitter 数据集包含一个由 Twitter 用户自己设置的名称变量。根据个人的偏好,它可以
- 一个完整的假名
- 名字,姓氏(反之亦然)
- 只有姓氏
- 名字缩写,中间名缩写,姓氏缩写
- 他的全名,但不小心拼错了
- 等等。等等。等等。
这给我们留下了一些数据质量和规范化的挑战,我们必须解决这些挑战,以便我们可以使用 Name 属性作为匹配的标识符。
下表描述了一些挑战以及我们应对这些挑战的策略。用于解决挑战的每种方法都将在本文中解释,并且是 Github 教程源代码的一部分。
让我们从解决头衔&敬称、*缺失成分、*和其他一些异常开始。
幸运的是,从许多记录的角度来看,我们的列表是可管理的,也就是说,我们可以对“Name”属性中使用的异常和标题进行手动检查。
命名清洁剂步骤
我们的质量审查显示,Name 字段似乎具有良好的质量(没有使用虚拟或昵称)。但是,我们发现了一些异常情况,如下所示:
我们在第一个清理步骤中修复了程序中的这些异常。为了保持通用性,我们再次使用 yaml 配置文件并添加了两个额外的参数。
- twitterNameCleaner ,由必须从任何 twitter 帐户名称中删除的关键字列表组成
- twitternamesexpender,它将每个 Twitter 帐户名称缩写子串扩展为一个完整的名称字符串。
我们特定于国家的 yaml 文件通过以下条目得到了增强。
清理步骤在下面的方法中调用,该方法评估我们的 Twitter 表的每一行(稍后解释它与程序的集成)
使用字符串替换方法非常简单
- 我们从 Name 属性中删除了在 twitterNameCleaner 列表中找到的每个关键字(用’ ’ '替换它)
- 我们将在twitternamesexpender字典中找到的每个缩写替换为它的全名。
对于我们的下一个正常化步骤,我们介绍一种起源于 100 年前美国面临巨大移民潮时的方法。
双变音算法
该算法的原理可以追溯到上个世纪,实际上是 1918 年(那时第一台计算机还远在几年前)。
作为补充信息(如果你曾经参加过百万富翁智力竞赛节目),第一台电脑是在 23 年后
Z3 是由康拉德·楚泽设计的德国机电计算机。这是世界上第一台可编程的全自动数字计算机。Z3 由 2,600 个继电器组成,实现了 22 位字长,以大约 4–5Hz 的时钟频率运行。程序代码存储在穿孔胶片上。初始值是手工输入的(维基百科)
A Z3 replica at the Deutsche Museum (CC BY-SA 3.0)
回到 1918 年,美国人口普查局的 Robert C. Russell 发明了 Soundex 算法,这种算法能够对英语进行索引,只需粗略浏览一下就可以找到同一姓名的多种拼写。
美国的移民有一种不以罗马字符为基础的母语。为了写出他们的名字,他们亲戚的名字,或者他们来自的城市,移民们不得不尽可能地猜测如何用英语来表达他们的象征性语言。美国政府意识到需要能够以一种允许同一姓名的多种拼法(例如 Smith 和 Smythe)被分组的方式对普通公民的姓名进行分类。(点击阅读完整故事
Immigrants arriving at Ellis Island — Wikipedia — Commons Licence
Soundex 算法基于字母表中字母的语音分类。在他的专利中,Russell 描述了给每个类别分配一个数值的策略。例如,约翰逊被映射到 J525,米勒被映射到 M460 等。
随着时间的推移,Soundex 算法在效率和准确性方面得到了发展,并被其他算法所取代。
在很大程度上,它们都被强大的索引系统所取代,该系统被称为“双变音位 T2”。该算法是开源的,其最新版本发布于 2009 年左右。
幸运的是有一个 Python 库可用,我们在我们的程序中使用它。我们围绕该算法编写了一些小的包装器方法,并实现了一个比较方法。
doublemethane方法返回一个由两个字符组成的关键字元组,它是传入的单词的语音翻译。我们的比较方法显示了该算法的排序能力是相当有限的。
让我们通过引入基于 Python pytest 框架的 test_class.py 来运行一些验证检查,以评估算法的效率。
pytest
框架使得编写小测试变得容易,同时也支持应用程序和库的复杂功能测试。(链接)
它的用法很简单,您可以在下面看到测试类的实现
测试结果如下所示。我们使用了两个名字(A+B ),并用一些改变的名字(A1/A2+B1/B2/B3)来检验算法的效率。
- A1+B1 通过了强匹配检查。因此,缺少空格和用 u/a 替换“/”似乎不会影响双变音键的生成
- B2 通过了正常比赛。拼写错误也被算法覆盖
- A2 + B3 正在失败。A2 使用名称部分的缩写,无法应付。这种行为我们已经预料到,并决定引入名称扩展器算法(见上文)。B3 因缺少“-”而失败。这是意料之外的,但是我们用第二个名字 cleanser 步骤覆盖了这个行为。
命名清洁剂第二步
我们的小规模试运行表明,以下额外的清洁步骤是有益的:
- 我们必须去掉任何可能导致失败的特殊字符
- 无序成分,即名字之前的姓氏影响语音键的生成。因此,最好是按字母顺序对姓名成分进行排序,以提高匹配的准确性。
除了删除任何特殊字符之外,我们还决定删除任何 Unicode 字符,即在瑞士,使用多种德语(δ,θ,ü)、法语(δ,é)或意大利语变音符号,我们也想删除它们。
下面的normalize _ unicode _ to _ ascii方法使用 unicodedata 库将名称转换为 ASCII,然后将所有字符转换为小写。然后,使用两个正则表达式删除特殊字符和多个空格。
正则表达式操作是一种强大的机制,但对于新手来说并不容易理解。你可以通过下面的教程来获得这个强大方法的第一印象。
- 只有一个解释,正则表达式术语*‘[^a-za-z0–9]+’*的意思是从传入的字符串中取出’ a-z ‘+’ a-z ‘+’ 0–9 ‘+’ '之间任意数量的字符。1+是正则表达式中的特殊字符。表达式序列可以被翻译成“从字符串中去除任何特殊字符”
数据框“应用”方法的妙处
在两个类中建立 __calculate_name_matching 方法
现在,我们已经将所有的名称清理器、规范化和语音关键字生成器方法组合在一起(捆绑在 namemachting.py 模块中),最终可以为我们的两个类 govAPI 和government social media analyzer 构建我们的名称匹配标识符创建方法_ _ calculate _ name _ matching*。*
One new module and two new methods are added
这两个类都获得了一个新方法,我们将其应用于它们的 Panda 数据框,每个方法都创建了三个新的标识符属性( col_match1、col_match2、col_match3 )。然后,这三列可用于合并两个数据框。
我们的政府 API 类的方法如下所示:
所以对于我们的 govAPI 类如下。这是我们抽象类中的一个实现方法。该方法用于任何实现的子类(参考我们的早期教程中解释的工厂设计)
实现很简单,我们规范化每个表的人名(从政府 API 我们得到三个独立的属性,从 Twitter API 一个帐户名),然后计算两个双变音标识符字符串。我们将所有三个新值追加到传入的行中。
不要打电话给我们,我们打电话给你:“好莱坞原则”
Photo by Annie Spratt on Unsplash
有一个问题仍然悬而未决:“我们如何使用我们的新方法来增强由三列及其每行计算值组成的数据框?
嗯,这可以通过使用熊猫数据帧应用 方法很容易地实现,其定义为:
apply()可以沿数据帧的任意轴应用函数
这是在数据框的每一行或每一列上进行任何计算的一种很好很优雅的方式。
下面你会看到增强的create _ politican _ from _ govapi _ table方法。在第 4 行代码中,我们新调用了数据帧( df )的 apply 方法,并将我们的方法名 self 作为参数传入。__calculate_name_matching 并指示 apply 方法为每一行调用我们的方法( axis=1 )。
现在熊猫数据框
- 遍历每个数据行,每个数据行代表一个专门的政治家的数据记录
- 称我们为自我。_ _ 计算名称匹配方法
- 将数据行作为参数传入,并存储回方法调用返回的数据行(这三个新列)。
非常漂亮不是吗?我们不必编写任何类型的循环来获取数据、检查循环结束条件等等。我们刚刚实现了可以传递给 apply 方法的方法。
这种交互是 IT 界众所周知的设计原则,被称为“好莱坞原则”,Matthew Mead 的优秀博客文章描述如下:
这个原则的精髓就是“不要打电话给我们,我们会打电话给你”。如你所知,这是你在好莱坞电影试镜后听到的回应。这也是软件好莱坞原则的相同概念。目的是明智地构造和实现您的依赖关系。
想想我们上面的实现。我们的*_ _ calculate _ name _ matching*方法与 panda 数据帧实现完全分离。我们的方法只获得一个行字典作为输入,我们可以操作它,并在最后作为返回值返回。我们完全没有意识到有一个大规模的熊猫数据框架实施涉及,也没有任何依赖它。
在最基本的层面上,这个原则是关于减少软件系统中的耦合。这又回到了软件开发中众所周知的一句话:松耦合和高内聚。通过确保没有不必要的引用,并且正确地引用类和其他子系统,可以保持类的松散耦合*。虽然好莱坞原则没有规定任何关于凝聚力的东西,但它也是一个重要的方面。内聚就是让你的类忠于它们的意图,不允许它们的行为超出其核心目的。像高耦合一样,低内聚不会导致系统不稳定或不能正常工作,但可能会导致软件维护困难。*
今天的设计教学已经足够了,当我们运行我们的程序时,两个 plotly politician 表都被 3 个新属性增强了,这允许我们最终将它们结合在一起(uff…我们必须做一些概念性的工作)
最后,连接数据框
我们现在必须连接两个数据框,这是 Panda 提供的许多组合和合并可能性之一。参考下面这篇由datacarpentry.org写的文章,这篇文章很好地介绍了整个主题。
组合数据帧的另一种方法是在每个数据集中使用包含公共值(一个公共的唯一 id)的列。使用公共字段组合数据帧称为“连接”。包含公共值的列称为“连接键”。当一个数据帧是一个“查找表”,包含我们希望包含在另一个数据帧中的附加数据时,以这种方式连接数据帧通常很有用。
这是通过可应用于熊猫数据帧的合并方法实现的(参见下面的代码行 23)。你必须提供参数
- 两个数据帧
- 匹配键(在我们的例子中,双变音属性 col_match2 在两个数据帧中都可用)
- 和连接策略( how 参数)
我们表示一个“外部左”连接( how=left) ,它可以被可视化如下
省略参数将如何导致内部连接
“内连接”意味着只接管匹配的行,“左连接”意味着接管左数据帧的所有行。如果没有匹配的右表记录,用“NaN”值填充相关记录。
我们新的 Plotly 表CH-tw-politican-merged-list就有这样的条目。
这意味着我们的匹配策略仍然会遗漏条目。让我们将合并后的表格的饼状图与初始表格进行比较:
Twitter User per Party (merged with gov API source)
最初的一个(参考这篇文章)漏掉了 51 个,通过我们简单的合并策略,我们可以识别另外 14 个条目。
Twitter User per Party (just using Twitter API information)
所以,我们还没有到那一步(啊…),但是今天足够了,在下一节课中,我们将尝试调整我们的匹配策略,以便我们可以再次减少未知。
源代码你可以在相应的 Github 项目中找到,其他所有文章列表在这里。
Python 教程:政府社交媒体分析器类构建
Python 统计教程系列
增强您的程序,使其适用于任何政治人物名单
点击标题上方的链接,查看所有教程文章的列表。
在这个练习中,我们将把我们的初始程序(参考第一个教程)转换成一个通用程序,用于获取一个国家的议会成员的 twitter 信息。这意味着我们对代码进行第一次迭代的概括和重构。
我们的目标是有一个程序可以分析任何国家的政治家的 Twitter 账户,而不仅仅是瑞士。
因此,我们的计划应该以一种我们也可以在其他国家使用(即配置)的方式进行推广。
Yaml 配置文件
我们的第一个 lesson1.py Python 程序是直接在程序中硬编码 twitter 账户及其列表名。
现在我们要做的是从配置文件中读出这些信息,类似于我们在第一课中为传递 Twitter API 秘密所做的工作(参考教程)
这个信息不是什么秘密,所以我们创建了一个新的公共 yaml 配置文件,它将用于我们程序的任何国家配置参数。当您一般化程序时,将所有这些参数卸载到一个配置文件中是一个关键点。
正如你在下面的截图中看到的,我们将文件命名为 config-CH.yaml 。
config-CH.yaml
那么为什么要使用 CH 后置呢?CH 后缀是瑞士国际 ISO 国家代码标准的 Alpha-2 代码。代码值(或参考值)是任何信息编码程序的组成部分。最佳实践是尽可能使用标准化的(不要在这里重复发明)。也就是说,对于国家,我们决定使用标准 ISO 3166-1 的 Alpha-2 代码。正如您将在后面看到的,对于编码语言,我们采用了类似的方法。顺便说一下,ISO 的意思是“国际标准化组织”
重构和增强
GovernmentSocialMediaAnalyzer 类
对于我们的一般化程序,我们做第一个重构步骤。代码重构是在不改变现有计算机代码外部行为的情况下对其进行重构的过程。所以我们将 sample1.py 中的类重命名为government social media analyzer,并通过参数 country_code 增强其类构造函数 __ init__ 方法。我们做出了第一个设计决定:
设计决策 1:我们的类 GovernmentSocialMediaAnalyzer 的一个实例将封装一个专用国家的数据和行为。
代码增强如下所示:
- 创建类时传入的 country_code 参数(如 CH
- 将作为私有实例变量 __ country_code 存储并使用
- 要创建 yaml 配置文件名,
- 从这里我们将加载配置数据并将数据存储在私有变量 __cfg 中
因此,现在我们准备通过从存储在 self 中的配置数据中读取 twitter 帐户和列表名称来推广我们的 get_government_members 方法。__cfg 实例变量。
但是让我们先完成对我们的 init 类的重构和增强。我们采取另一个设计决策
设计决策 2:init方法应该封装所有 Twitter 帐户从 Twitter 的加载,以及到 attributes 列(=字符串数组)中相关属性的转换。该列应该可以作为私有类实例变量使用
这意味着当我们为一个专门的国家创建一个 GovernmentAnalyzer 实例时,案例的初始化阶段将包括将来自 Twitter 的数据放入我们的 intern 数据结构(如列所示)的所有必要步骤
我们将在一个专用帮助器方法中完成这个步骤,这个方法将被称为 __ extract_columns 。我们将它定义为私有方法(__ prefix),因为它不应该被这个类之外的任何人使用。
第一课中重构的类现在看起来像这样。
我们使列属性更具描述性,并将它们定义为类实例变量,以便这些列可以被我们的类中的任何方法使用。
所以我们已经完成并重构了类实例创建类
- 5–12:加载特定于国家的配置文件的代码块
- 16–21:从秘密配置文件中读取 twitter 安全令牌和密钥的代码块,然后连接到 Twitter API
- 39:调用 _extract_columns 方法检索数据并转换成列。
教程一中的 check_for_party 算法是在代码中硬编码 party 缩写。
好了,让我们重构代码,并将参与方信息移动到我们的配置文件中。由于 YAML 文件的灵活性,这可以很容易地完成。
设计决策 3:我们希望为每个政党使用几个政党缩写(可能使用多种语言)和关键字(例如,政党 Twitter 昵称),以尝试识别政治家的政党所有权。
所以我们的配置 config-CH.yaml 将需要每一方的配置信息。在瑞士的 parlament.ch 网站上可以找到四种语言的政党及其缩写。
在 YAML,您可以快速建立一个配置项目列表(例如,聚会配置项目)。列表成员由前导连字符(-)表示,每一到多行一个成员,或者用方括号([ ])括起来,并用逗号分隔(,)。
- 当事人名单成员用连字符表示。一个政党名单成员有一个 twitter 和缩写属性。缩写(缩写)属性本身是一个由方括号符号表示的字符串列表。
config-CH.yaml (with parties list)
如果我们检查加载的配置文件(存储在 self 中。_cfg 变量)在 Python 调试器中,使用 Python 列表和字典应该很清楚数据结构是什么样子。
作为对abbres属性的补充说明,我们介绍了他们的政党缩写列表,即拥有多种国家语言也意味着政党有多种缩写(例如,德语和法语)。在我们上面的例子中是“FDP”和“PLR”我们想检查所有的。在其他国家,可能只有一个缩写,但有了这个决定,我们就是未来的证明。
我们改进的 check_for_party 方法现在看起来如下。它将遍历所有 twitter 帐户和每一方的配置记录,并检查 twitter 帐户的描述或昵称是否与某一方匹配。
- 在第 6、10 和 19 行,我们从配置结构中获取数据。
- 根据属性类型,我们必须遍历值列表(6,10)或直接获取数据(19)
- 如果我们有一个匹配,第一个缩写将被返回,作为我们识别当事人的代码值:RES = party[’ abbs '][0]
微调算法
引入第二个 Plotly 表:按交易方对账户进行分组
为了微调我们的算法,我们必须检查它在寻找 twitter 帐户的一方时的有效性。为此,我们必须引入第二个表,它将根据我们的 twitter 帐户的聚会分配对它们进行分组。
强大的熊猫包将为我们提供必要的工具。你可以参考 panda API 描述后面的,了解如何分组数据的所有细节。
代码片段的一些注释:
- 4–8:我们在这里创建一个包含 4 列的 panda_data 记录。 __col_party , __col_followers_count , __col_friends_count 。 __col_party 使用了两次,第一列用于标记每一行(如您在第 11 行看到的,我们按参与方进行分组),在第二列中,我们汇总了具有相同参与方的行。
- 9:我们创建了这个表的第一个 panda 数据框,有四列
- 11:这里我们通过使用 groupby 函数来转换创建的数据帧。我们还为第二、第三和第四行定义了聚合 agg 操作。
- 15–19:创建一个漂亮的 plotly 表的基本材料。
让我们运行程序并检查我们的 party assignment 分配算法的准确性。作为程序执行的结果,您现在必须在您的 plotly 帐户中创建表格(还将创建一些网格表,这些表格目前并不相关)。
用关键字属性增强我们的配置文件
我们的首次调查显示,大多数政客(65 人)不会在他们的账户描述/昵称中提及他们的党派缩写或党派 twitter 昵称。
所以让我们试着微调我们的算法。让我们再次浏览这个列表,并检查其他可以帮助我们识别他们的聚会关系的关键字。
我们找到了以下关键词:
- 社会主义党
- glp (GLP)、
- 格鲁内(GLP)
- 勒加
所以让我们用一个新属性将它添加到我们的配置文件中: keywords 。这就是 YAML 的魅力所在,你可以很容易地用附加属性来扩展它。在我们的例子中是另一个列表属性。
我们在我们的 check_for_party 方法(23–28)中添加了额外的检查
瞧,我们可以找到另外 13 个拥有超过 20,000 名粉丝的推特账户。尽管如此,52 个帐户不能映射到一个聚会,但为此,我们必须连接另一个数据源,这将在后面的教程中完成。
作为今天的最后一步,我们重构了 create_politican_table 方法。我们主要通过在文件名中使用国家代码来规范 plotly 中使用的文件名。这使我们能够为不同的国家生成表格,并确保它们不会在我们的 plotly 帐户中相互覆盖(20)。
现在,我们已经概括和重构了我们的整个应用程序,并为进一步的构建打下了良好的基础。
我们现在可以为一个特定的国家实例化一个government social media analyzer(假设我们已经提供了必要的配置文件),并将 twitter 相关数据提取到一个 plotly 表中以供进一步处理。
作为一个可视化的 UML 序列图,我们的类的交互流可以表示如下:
如果你想了解更多关于 UML 序列图的细节,请参考下面的教程。这是一种很好的可视化程序各个方面的技术。在上图中,消息的返回调用用蓝色表示。
例如:panda 包创建数据帧的消息行用红色表示(createDataFrame),它返回的 data frame 对象的消息行用蓝色表示。
锻炼
使用通过政府推特账户(【https://twitter.com/TwitterGov】)提供的列表之一,例如,英国议会成员列表(【https://twitter.com/TwitterGov/lists/uk-mps】)。
- 制作相应的 yaml 配置文件
- 看看什么样的信息可以用来识别政治家的政党。
- 用你的发现强化关键词
- 用一个用户输入问题增强主程序,类似于“你想分析哪个政府”。提供可用配置列表,然后根据用户选择运行程序。
- 想想分析每个国家的多个政治家名单所需要的改变。也就是说,我们希望区分每个国家的不同政府机构,并在配置文件中对其进行概括。
源代码可以在这里找到:https://github.com/talfco/clb-sentiment
原载于 2019 年 2 月 3 日 dev.cloudburo.net。
Python 教程:瑞士政府成员的 Twitter 账户检索
Python 统计教程系列
开始你的政治家社交媒体分析之旅
点击标题上方的链接,查看所有教程文章的列表。这是第一条。
本教程将向您展示如何通过 Twitter API 提取瑞士政府成员的 tweet 列表。提取的数据将被放入 Panda Dataframe 中,然后通过强大的 Plotly 软件包进行可视化。
Source Attribution: http://www.parlament.ch/
结果将如下所示:
从哪里获取数据?
twitter 账户 SoMePolis 维护着一份拥有 Twitter 账户的瑞士政府成员名单。
该计划的目标是获取所有名单成员(正在发推特的政府成员)并提取一些关键人物(追随者和朋友)
创建一个 Twitter 开发者账户和应用
确认后,您就可以在您的开发者帐户中创建您的第一个应用程序了。
完成后,Twitter 将生成 API 密钥和令牌,您将在程序中使用它们与 Twitter API 进行通信。
该写程序了。
我们将创建一个 TwitterClient 类,它提供两个方法 get_government_members 和 create_plotly_table
该程序将使用以下 python 包
- tweepy :将提供对 Twitter 公共 API 的访问
- yaml :是一个解析器,可以读取 yaml 配置文件(用来存储我们的密钥)
- pandas :是一个提供高性能、易于使用的数据结构和数据分析工具的库
- plotly :是一个图形库,可以在线制作交互式的、出版物质量的图形。
也就是说,通过使用这四个库,我们已经做好了准备。
init
在类初始化方法中,我们必须建立与 Twitter API 的连接。
为此,我们使用从 yaml 文件加载的密钥和令牌,该文件位于当前目录之外的 secret 目录中。
with open("../../secret/twitter.yaml", 'r') as stream:
这是一种常见的方法,用于定位目录中的敏感数据,这些数据只存在于本地计算机上,不会在 Github 这样的远程源系统中进行检查。
Yaml 是一种人类可读的数据序列化语言。它通常用于配置文件,但也可以用于存储数据的许多应用程序。
它有一个简单的语法,对于我们的例子,安全令牌有四个 : 对,它们被加载:
consumer_key = res['apiKey']
consumer_secret = res['apiSecretKey']
access_token = res['accessToken']
access_token_secret = res['accessSecretToken']
作为补充信息,当您增强您的程序并需要额外的非敏感配置数据时,您将引入第二个包含公共信息的 yaml 文件,您可以使用普通源代码签入该文件。
下一步,我们使用 OAuth 对 Twitter 进行认证,并获得对其 API 的访问权。OAuth 是一种开放的访问授权标准,通常用于互联网用户授权网站或应用程序访问他们在其他网站上的信息,但不需要给他们密码。即,您将向他们提供您的秘密访问令牌和密钥。
我们创建了一个私有的类变量 _api ,它保存了对 Twitter API 的引用。通过这个引用对象,您现在可以与 Twitter API 交互,这里描述了所有可用的方法。我们为 API 使用私有变量,因为我们不想直接向类的消费者提供 API,而是提供更高级别的方法,如 getgovernment_members 。
当您设计将在程序或模块中使用的类时,定义类变量的范围(public、protected、private)是一个重要的设计决策。
获取 _ 政府 _ 成员
在这个方法中,我们将从上面描述的 twitter 帐户 SoMePolis 的列表 BundesparlamentarierInnen 中获取所有的 twitter 政客帐户。
第一步,我们获取 SomePolis 帐户的所有 Twitters 列表,并搜索所需的列表。找到它之后,我们将使用它的 list.id 来检索列表中的所有成员(twitter 帐户)。
为了检索一个列表的所有成员,我们必须使用一个所谓的*光标(*位置 7 )
管理任意大型列表的数据检索是 API 接口中的一个重要概念(例如,一个列表可能有数百万个条目)
如果你在程序中使用下面的代码序列,你将只返回列表的前 25 个成员。
__api.list_members(list.id)
使用这种策略,Twitter API 将确保它不必在一个请求中向客户端程序传递大量数据。如果您想要所有成员,您必须多次调用 list_members。在每个请求中,将返回 25 个列表成员帐户的数据包。这个概念被称为“分页”,在各种提供者的公共 API 中使用(你可以在这里找到更多信息: Tweeply Cursor Tutorial
该列表大约有 110 个成员,这意味着 list_members 方法将被调用五次。最后,结果变量将有 110 个 Twitter 帐户。
创建 _ 绘图 _ 表格
最后,我们从 twitter 帐户列表中提取某些数据,以供进一步处理。
我们将准备一个 Panda Dataframe,它是一个二维大小可变的、潜在异构的表格数据结构,带有标记轴(行和列)。
我们将提取的 Twitter 帐户列表
- 屏幕名称,也就是@ <推特名称>
- 名,通常是这个人的全名
- 描述,提供对该人的附加描述
- Twitter 账户的关注者计数,以及好友计数。
我们必须为数据帧创建列,这是通过以下命令序列实现的
col1 = [list.screen_name for list in lists]
我们遍历列表,为每条记录提取出 screen_name 。
最后,我们创建一个 plotly 表:
有两个主要模块,我们将需要生成我们的 Plotly 图形。
- p lotly.plotly 包含了帮助我们与 plotly 服务器通信的功能
- plotly.graph_objs 包含了为我们生成图形对象的函数。
您必须在 https://plot.ly 创建一个帐户(当您第一次运行应用程序时),您生成的表将被上传到该帐户。因此,您将在您的帐户中获得一个与此类似的表。
在 Plotly 创建账户
第一次运行该程序时,会要求您在 plotly 注册并创建一个帐户。
创建帐户后,您必须检索访问令牌(如有必要,创建一个新的)
并将它存储在文件中。凭证在主目录的文件夹中。plotly
这就是今天的练习,尝试添加以下代码序列。
锻炼
通过分析每个政府人员的网名或描述,试着识别他们的党派。作为指导性帮助,请查看以下瑞士聚会指南。主要政党是“CVP”、“FDP”、“SP”、“SVP”以及“Grüne”。缩写很有可能是一个人提到的(见下面截图)。
因此,构建一个新的 Panda 列,并将其添加到数据框中进行可视化。
源代码( lesson1.py )可以在这里找到:https://github.com/talfco/clb-sentiment
此处练习的解答
原载于 2019 年 1 月 26 日【dev.cloudburo.net】。
Python 教程:简要介绍主要统计概念
Python 统计教程系列
通过使用我们的教程程序作为展示来解释概念
我计划在本次会议中通过名称变量介绍两个数据集的合并算法。
为了简单明了地概述整个方法,有必要事先介绍一些统计概念。
我决定做一个短暂的停留,将工作嵌入到统计框架中,这是任何数据探索项目的基础。所以今天没有编程,只是一些概念,对整体理解至关重要。我计划在未来建立更多的统计站来建立我们项目的科学背景。
Photo by chuttersnap on Unsplash
总体、样本、测量、参数和统计
我们从统计学中使用的一些基本定义开始,通过使用塞勒学院的介绍教程(内容在知识共享许可下提供)
群体 是感兴趣对象的任何特定集合。 样本 是总体的任何子集或子集合,包括样本由整个总体组成的情况,这映射到术语 普查。
人口
在我们的例子中,人口是瑞士联邦议会的瑞士政府成员(议员)。
联邦议会是两院制的,由 200 个席位的国民议会和 46 个席位的州议会组成。这些房子拥有相同的权力。两院的议员代表州,但是,尽管国民议会的席位是按人口比例分配的,但每个州在上院有两个席位,除了六个半州各有一个席位。两人每四年选举一次,最后一次选举将于 2019 年举行。(维基百科链接)
样品
我们的样本是所有议员,他们都有一个 Twitter 账户,并被列为 SoMePolis Twitter 议会名单的成员。这是一个子集,所以我们不用人口普查这个术语。
一个 测量值 是为总体或样本的每个成员计算的一个数字或属性/变量。样本元素的测量值统称为 样本数据。
尺寸
对于样本集的每个成员,我们通过两个数据集收集变量,即第一组变量通过 Twitter API 收集,第二组变量通过政府 API 收集。
抽样资料
我们通过统一这两个数据集得到的总体样本数据集。
一个 参数 是一个概括总体某个方面的数字。 统计量 是从样本数据中计算出的数。
因为我们要计算的数字是基于人口子集的,所以我们讨论的是统计数据而不是参数。
这就是最初的范围定义;现在让我们关注“变量”的概念,这是至关重要的。
数据集和变量
作为补充,让我们来看两个数据集,我们已经检索过了:
作为第一个表( Tutorial 2 ),我们从 Twitter 中检索了以下瑞士政治家 Twitter 账户的数据记录。
Image owned by the writer
它为我们提供了在未来分析中发挥作用的三个统计变量:
- 追随者计数
- 好友计数
- 聚会
- 姓名,可能代表 Twitter 用户的真实姓名。“可能”这个词在这里是必不可少的,但更多的是在后面。
我们还从 Twitter 获得了一个标识符变量,它帮助我们揭示一个人的真实身份(在我们的例子中,是瑞士委员会的成员):
- ScreenName, 这是 Twitter 账户的唯一标识符。如果我们必须在稍后阶段通过 Twitter API 检索附加信息,我们可以使用它。
作为第二个表(教程 3 ),我们通过瑞士政府 API 检索了瑞士联邦议会所有现任议员的名单。
Image owned by the writer
从该表中,我们得到了以下变量,我们希望将其用于我们的统计评估:
以及标识符变量
- id ,标识政府 API 上下文中的数据记录
让我们更深入地挖掘一下数据集中使用的变量的统计意义
可变术语定义
在统计学中,一个变量有两个定义特征。(1)变量是描述人、地点、事物或想法的属性。(2)变量的值可以从一个实体到另一个实体“变化”。
在我们的例子中,一个人的性别*、*政党、或 T 维特追随者数量都是潜在变量。
数据类型分类
变量的数据类型在统计中起着重要的作用,必须理解这一点才能正确地将统计测量应用到数据中。在统计学中,术语“测量尺度”也可以使用。
下面的树对数据类型概念进行了分类
Data Type Classification Tree (image owned by writer)
参考下面这篇优秀的中型文章来获得对数据类型主题的整体介绍。
让我们将分类应用于我们的两个数据集,结果如下表所示。
Image owned by the writer
两条评论
- 我们将每个集合的变量标记为“唯一”,这标识了一个专用的样本结果,我们不能对其执行任何数据分析。所谓的标识符变量
- 在 gov.api 数据集中,我捆绑了一些变量来描述一个人或地址。即,一个人的名字由该人家庭住址的名、中间名、和姓组成,家庭住址由邮政编码、和邮政编码组成。
存在许多变量类型,可以用上述方案对它们进行分类(参考参考章节中的附加链接)。不过,我们现在对所谓的标识符变量感兴趣。
标识符变量
标识符变量和唯一标识符通常在统计中用于唯一标识数据收集目的。
标识符变量是分类变量,每个类别有一个个体。例如:
社会安全号码、面试者身份证号码或雇员身份证号码。
由于标识符变量是单一的,不可能对它们进行任何数据分析。相反,它们是用来识别结果的。
嗯,我们的两个数据集在自己的域中有一个唯一的标识符。然而,没有真正的唯一标识符允许通过两个(跨域)数据集中可用的唯一标识符直接组合两个数据集。
在理想的情况下,这两个记录都有一个唯一的标识符,也就是说,在它们的数据集中有一个国家标识符,可以很容易地将它们合并。
以下示例显示了瑞士的国民身份证,它为瑞士公民(持有身份证)提供了一个唯一的标识号。
Swiss ID Card — Source
以下标识符要求个人持有身份证;如果一个人只持有一本护照,你就必须获得唯一的护照标识符。不旅行的人不会潜在地拥有这两种身份手段中的任何一种。
一个更好的标识符是国家安全标识符,几乎每个公民都持有(超过一定年龄)。低于居住在瑞士的人数。
Swiss Social Security Number — Source
现实情况是,你公开获得的数据集将不包括这种易受影响的人数。
不应以不安全的方式暴露这种强标识号(在互联网上公开共享)。参考 CNN 的以下文章,它描述了 Equifax 的一个著名的数据黑客,导致 1.43 亿美国人的社会安全号码泄露。
罪犯可以使用您的社会安全号码来窃取您的身份。他们可以开立银行账户和信用卡,或者申请贷款。黑客还可以以你的名义获得你的退税或医疗服务。(【money.cnn.com】T2
我们今天对统计概念的介绍到此结束,下一篇文章将带我们回到通过 Python 连接没有通用唯一标识符的数据集的问题。
在我们的例子中,我们在两个数据集中都有一个(或多个)描述个人姓名的属性。但是,要注意;
当姓名是唯一的统一数据点时,正确匹配相似的姓名就显得更加重要。然而,它们的可变性和复杂性使得名称匹配成为一项极具挑战性的任务。昵称、翻译错误、同一个名字的多种拼写等等都可能导致匹配失败。(【rosette.com】T4)
这正是我们现在面临的情况;让我们进入下一篇文章。
参考
- 不同变量类型的综合列表(嗯,你能想到的任何类型)——链接:datasciencecentral.com
原载于 2019 年 2 月 24 日dev.cloudburo.net*。*
Python 和矢量化
当今世界的计算机硬件通过利用 SIMD(单指令、多数据)架构,利用并行计算来实现更快的计算。SIMD 是一类并行计算,其中逻辑处理器同时对多个数据点执行单个指令。我们需要向量化我们的深度学习代码,以便我们可以利用我们的系统提供的所有计算能力。计算越快,我们的神经网络训练得越快,我们得到结果的速度就越快。因此,对一段代码进行矢量化的能力已经成为深度学习实践者的一项关键技能。在这个故事中,我将解释使用 python 进行矢量化的基础知识。
到底什么是矢量化?
在逻辑回归的背景下,让我们借助下面的等式来理解矢量化的确切含义:
存储输入和权重的约定并不标准,但我更喜欢用以下方式存储。设 X 为输入矩阵的维数( n,m) 其中 n 是出现在中的特征数,而 m 是出现在我们的训练数据集中的训练样本数,即我们在 1 列中存储一个数据点。对于每个特征,都会有一个与之相关联的权重,因此设 W 为维度的权重矩阵( n,1) )。
现在,如果我们使用 为 循环编写示例方程,我们会得到如下结果:
上述代码片段由针对 循环的显式 组成,不会利用并行化。因此,我们需要将其转换为矢量化版本。这可以通过以下方式利用内置的 Numpy 函数轻松完成:
Z 将是一个 (1,m) 矩阵,按照矩阵乘法规则。 np.dot() 函数执行给定输入矩阵的矩阵乘法。它不仅使代码更具可读性和可理解性,而且还利用并行化来提高计算速度。下面的代码片段显示了与非矢量化实现相比,矢量化实现的速度有多快。
***import** numpy **as** np
**import** time*# Number of features* n = 1000
*# Number of training examples* m = 10000
*# Initialize X and W* X = np.random.rand(n,m)
W = np.random.rand(n,1)*# Vectorized code* t1=time.time()
Z = np.dot(W.T,X)
print(**"Time taken for vectorized code is : "**,(time.time()-t1)*1000,**"ms"**)*# Non Vectorized code* Z1 = np.zeros((1,m))
t2 = time.time()
**for** i **in** range(X.shape[1]):
**for** j **in** range(X.shape[0]):
Z[0][i] += W[j]*X[j][i]print(**"Time taken for non vectorized code is : "**,(time.time()-t2)*1000,**"ms"**)**''' Output
Time taken for vectorized code is : 5.964040756225586 ms
Time taken for non vectorized code is : 40915.54665565491 ms
'''***
上述实现仅考虑 10k 训练示例和 1k 特征。尽管有代码优化策略,但显然矢量化实现比非矢量化实现快得多。 Numpy 是一个用于科学计算的 python 库。它提供了各种内置函数,让我们可以轻松地编写矢量化代码。
根据经验,我们应该使用内置的 numpy 函数为任何未来的实现编写矢量化代码。
向量化逻辑回归
既然我们已经看到了编写矢量化代码的好处,那么让我们更深入地研究,为逻辑回归编写矢量化代码。不可能为每种情况编写一个矢量化代码,但是我们应该尽可能地遵循经验法则。让我们看一个非矢量化版本的逻辑回归,并尝试找出可以矢量化的部分。这样我们就能理解如何将一段给定的代码转换成它的矢量化版本。为简单起见,我们将只考虑 X 中的 2 个特征,因此只有 2 个权重。
在上面的例子中,我们只考虑了 2 个权重,即 w1 和 w2 ,但是在实际生活场景中会有更多的权重,处理它们将成为一项复杂的任务。因此,我们将通过以下方式对权重导数 dw1 和 dw2 的计算和更新进行矢量化:
*dW = X[i].dZ[i]
dW /= m*
在上述更改的帮助下,我们已经设法对代码的一小部分进行了矢量化。大部分仍然依赖于 for 循环,该循环用于迭代所有的训练示例。让我们看看如何移除 循环的 并对其进行矢量化:
所有训练示例的值 A 可以很容易地通过下式找到:
*A = sigmoid(np.dot(W.T,X)+b)*
成本 J 可由下式查出:
*J = -(np.dot(Y,np.log(A).T)+np.dot((1-Y),np.log(1-A).T))*
所有训练示例的导数 dZ , dW 和 dB 可以通过以下方式找到:
*dZ = A - Y
dW = np.dot(X*(dZ.T))/m
dB = (np.sum(dZ))/m*
权重矩阵 W 和偏差 B 可以通过以下方式更新:
*W = W - alpha*dW
b = b - alpha*dB*
这些转变起初看起来令人困惑。因此,我敦促读者看看每次计算后每个矩阵的维数是如何变化的。这将有助于更好地理解事物。让我们应用这些变化,看看当所有东西都被编译在一起时,矢量化的代码是什么样子。
上面的代码更加清晰、易读、简短,计算速度也更快。
广播
在代码中,你可能已经发现两个维数不相容的矩阵被相加、相减、相乘和相除。 Numpy 有一大特色叫 播 。在某些约束条件下,较小的矩阵被 广播 到较大的矩阵,以便它们具有兼容的维数来执行各种数学运算。让我们借助一些例子来看看广播是如何工作的。设 A 和 B 为输入矩阵, C 为输出矩阵,作为对 A 和 B 的任何数学运算的结果。
*Shape of A : 5 x 4
Shape of B : 4
Shape of C : 5 x 4Shape of A : 15 x 3 x 5
Shape of B : 15 x 1 x 5
Shape of C : 15 x 3 x 5Shape of A : 8 x 1 x 6 x 1
Shape of B : 7 x 1 x 5
Shape of C : 8 x 7 x 6 x 5Shape of A : 2 x 3 x 3
Shape of B : 1 x 5
Shape of C : Error*
由此可见,广播工作基于两个原则:
- A 和 B 的尾部尺寸应等于 或
- A 或 B 的拖尾尺寸应为 1。
理解矢量化并将给定代码转换为矢量化格式的最佳方式是跟踪表中各种矩阵的维数。
起初,编写代码的矢量化版本可能会令人望而生畏,但是,在 Numpy 的内置函数和广播的帮助下,通过练习,它变得非常容易。这将使代码可读性更强,速度更快。
参考
我要感谢读者阅读这个故事。如果你有任何问题或疑问,请在下面的评论区提问。我将非常乐意回答这些问题并帮助你。如果你喜欢这个故事,请关注我,以便在我发布新故事时获得定期更新。我欢迎任何能改进我的故事的建议。
Python 虚拟环境变得简单
Photo by Chris Ried on Unsplash
我开始了一个项目,在这个项目中,我必须快速检查一个包Flask
是否能与我机器上安装的 Python 一起工作。当我运行安装 Flask 的命令时,它提醒我已经安装了这个包,因为我的机器上已经安装了 Anaconda 。
但是当我试图在 Sublime Text 3 上运行Hello World
Flask 应用时,控制台给出了一个错误,它无法找到 Flask 模块。我很困惑,开始在网上看关于这个问题的资料。我发现 Anaconda 有 Flask 模块,但是我在 Sublime Text 中使用的 Python 没有这个模块。我草草地找到了一个解决方案,以理解如何正确地设置 Python,在正确的位置安装正确的包,并设置 Sublime Text build 系统。我的网上研究揭示了关于Virtual Environments
,一些我以前没有读到过的东西。
在阅读了许多关于Virtual Environments
的文章后,我学到了很多关于 Python 的知识,以及我应该如何创建使用 Python 的环境。在本文中,我将分享我关于如何使用终端设置这些环境的知识。
计算机编程语言
Photo by Icons8 team on Unsplash
社区目前支持两个版本, 2.x 和 3.x 版本。有些包适用于 Python 3.x,但有些包不支持 3.x,仅适用于 2.x。在这种情况下,有时人们可能希望使用 Python 2.x,有时使用 Python 3.x。我们从安装两个版本的 Python 开始,这样我们可以使用其中一个或两个来设置环境。如果你有一个包安装程序,比如 Homebrew ,你可以用它来安装 Python。
Python 3。X
brew install python@3
Python 2。X
brew install python@2
它还安装了pip
,帮助我们安装 Python 包。
虚拟环境
Photo by Kimberly Farmer on Unsplash
每当您开始一个项目时,您将首先决定使用哪个 Python 版本,然后选择一些您想要的库/包。然而,最好不要在系统范围内安装这些软件包。您可能在同一台机器上使用多个版本的 Python,或者某些包只适用于某些版本的 Python 而不适用于其他版本。在这样的场景中,我们可以设置不同的环境,称为Virtual Environments
。
每个环境都将是它自己的虚拟空间。安装在该空间内的所有软件包不会干扰环境外部的软件包,并且将只包含在该空间内。
识别 Python 安装位置
根据您使用的安装方法,Python 将安装在不同的位置。
从官网安装的 Python
/Library/Frameworks/Python.framework/Versions/
在这里,你会找到已安装的版本。我的版本是 3.6,所以 Python 的路径应该是:
/Library/Frameworks/Python.framework/Versions/3.6/bin
使用自制软件安装的 Python
/usr/local/Cellar/
接下来是目录python
或python@2
下的版本。如果我们以python
和3.7.2_1
版本为例,位置将是:
/usr/local/Cellar/python/3.7.2_1/bin
安装虚拟
我们将使用自制软件安装 Python 3。我们用pip3
安装virtualenv
。
pip3 install virtualenv
创建虚拟环境
所有软件包都已安装,现在我们可以开始设置我们的虚拟环境了。我们需要定义我们想要建立环境的位置,并提供一个名称。我将它放在主目录中,并将名称设为virtEnv1
。
virtualenv -p python3 ~/virtEnv1
上面命令中的最后一个参数定义了我们环境的确切路径以及环境名称。我们的环境现在已经设置好了,我们可以开始在其中工作了。
激活环境
为了开始在一个环境中工作,我们需要激活这个环境。当设置环境时,在环境的bin
文件夹中创建一个名为activate
的文件。我们将这个文件设置为源文件,现在我们在环境中了。
cd ~/virtEnv1
source bin/activate
环境的名称开始出现在括号中,表示我们现在正在环境中工作。
Inside ‘virtEnv1’ environemnt
安装软件包并创建文件
我去装烧瓶。
pip install flask
我还在 home 目录下创建了一个文件app.py
,其中包含 Flask 中最基本的Hello World
代码。
如果我试着在环境中运行代码,它会像下面的图片一样工作。
python ~/app.py
客户端
Hello World in Flask
服务器
停用环境
如果您的环境是启用的,那么很容易就能摆脱它。只需在终端中键入deactivate
即可。
由于环境现在被停用,我们使用系统范围内安装的 Python。系统中没有安装烧瓶。因此,如果我再次尝试运行相同的代码,它会给出一个错误,即没有找到flask
。
No flask error outside environment
以类似的方式,我们可以创建更多的环境,甚至为 Python 2 复制类似的过程。
在 Sublime Text 3 中使用 Python
Photo by Ilya Pavlov on Unsplash
当我不在 Jupyter 笔记本上工作时,我更喜欢处理精彩的文本。要为 Python 设置 Sublime 文本,我们需要创建一个新的构建系统。转到:
Tools > Build System > New Build System...
并将文本设置如下:
用您的用户名替换<username>
。
先存成virtEnv1Python
吧。在 Sublime 文本中打开文件 app.py。将构建系统设置为virtEnv1Python
。要测试应用程序是否工作,请按Command
+ B
。您将看到应用程序开始运行,您可以通过链接[http://127.0.0.1:5000/](http://127.0.0.1:5000/.)
进行确认。
结论
在本文中,我讨论了使用virtualenv
来创建 Python 虚拟环境并在这些环境中安装包。
请随时分享你的想法,想法和建议。
Python 与 Excel —复合年增长率(CAGR)
我对 Microsoft Excel(或 Google Sheets)最大的失望之一是缺乏一个内置的函数来计算复合年增长率或 CAGR (XIRR 是最接近的,但它不是相同的)。这意味着,在我需要进行快速 Excel CAGR 分析的任何情况下,我都需要为 CAGR 编写 Excel 公式。
每一个。单身。时间。
如果您还不知道,CAGR 的 Excel 公式如下:
=(结束值/开始值)^ (1 /年数)-1
其中,结束值是给定时间段的最新值,开始值是给定时间段的第一个值,年数是要计算年增长率的年数。
在 Excel 表格中,它可能看起来像这样:
CAGR formula to calculate growth rate between 2010 and 2018
这是一个相当简单的公式,可以很容易地依赖…除了当表随着时间的增长而变长的时候!
Just add one more year, and you now need to specify the correct cells for the formula again
数据集往往会增长,这给分析师带来了一个问题,他们必须确保公式始终正确!(我去过。我不得不为一个更大的数据集修正一个更复杂的公式,纠正一个整整一年都没人注意到的错误,因为这个公式被淹没在数据的海洋中!)
这种挫败感是我从电子表格转向编程(特别是 Python 和 Pandas 库)进行数据分析的原因。我对 Python 还比较陌生,但是我已经体验到了它比 Excel 强大的多功能性和高效性(就像不用等几个小时 Excel 表来填充所有缺失的值)。
在这里,您将了解为什么编写 CAGR 函数并将其应用于从电子表格转换而来的数据帧会更好、更高效。
让我们从用 Python 定义 CAGR 函数开始:
def cagr(start_value, end_value, num_periods): return (end_value / start_value) ** (1 / (num_periods - 1)) - 1
就是这样!两行代码就有了你自己的 CAGR 函数。您会注意到这里与 Excel 函数略有不同。在该函数中,在 num_periods 之后有一个负 1。这使我能够通过将总周期数而不是复利周期数(总周期数总是减 1)指定为参数来正确计算 CAGR。我这样做是因为在过去,我曾多次在 Excel 中错误地指定复利周期数。
现在您已经有了 CAGR 函数,您可以将它保存到一个单独的 Python 文件中(从该文件中您可以导入到任何其他 Python 文件中),也可以将它编码到文件中,您将在该文件中将 Excel 表加载到数据框中并应用它。
要应用您的 CAGR 函数,首先导入 Pandas 库:
import pandas as pd
Pandas 是一个开源的、易于使用的 Python 库,它可以将任何 csv 或 Excel 文件转换成 dataframe 用于数据分析。这是用 Python 进行任何数据分析编程的必备工具。
接下来,将 Excel 加载到熊猫数据框架中:
ExcelFile = 'ExcelTable.xlsx' #name of my Excel file
df = pd.read_excel(ExcelFile, index_col='Year')
请注意,我添加了一个参数 index_col 来将“Year”列指定为索引,将“Year Income(RM)”列作为 dataframe 中的唯一一列。
在数据框上执行打印功能,查看 Excel 表格是否成功转换为数据框:
print(df)
Looks like it came out right
现在,您有了一个可以应用 CAGR 函数的数据框架。
Python 中 CAGR 函数的关键,以及为什么它在 Python 中更容易使用,是下面的变量:
start_value = float(df.iloc[0])
end_value = float(df.iloc[-1])
num_periods = len(df)
这些变量将提取 CAGR 函数所需的参数,在这种情况下,输出返回:
**36000.00
102000.00
9**
这些变量的好处在于,即使数据集在增长,代码也不必更改。使用**。iloc** 方法与列表索引一起确保函数将始终使用第一行作为起始值,最后一行作为结束值,并且 len 函数将始终正确计算时间段的总数。
即使你多加了一年,代码也是一样的。比方说,您在数据帧中添加了一行:
df.loc[2019] =[84000]
对相同变量运行打印功能将根据新数据返回不同的输出:
**36000.00
84000.00
10**
如果您想为 CAGR 计算指定一个时间段,比如 2012 年到 2016 年之间的 5 年,您也可以使用**。loc** 方法很容易做到这一点:
start_value = float(df.loc[2012])
end_value = float(df.loc[2016])
num_periods = len(df.loc[2012:2016])
Slicing and dicing
现在,让我们通过使用变量作为输入参数,尝试将 CAGR 函数应用于该切片数据集:
result = cagr(start_value, end_value, num_periods)
print(result)**0.12801507993497308**
但是等等!输出以小数位数过多的浮点类型显示结果。虽然我个人更喜欢这种输出,但它通常对许多人来说并不友好。我们将需要字符串格式来表示百分比输出:
print("{:.2%}".format(result))**12.80%**
更加人性化。
这就是在 Python 中执行 CAGR 分析所需的全部内容!
Python Vs R:什么最适合机器学习
你是否在考虑建立一个机器学习项目,并在为你的项目选择正确的编程语言之间犹豫不决?好了,那么这篇文章就要帮你扫清与 Python 和 r 特性相关的疑惑了,先从基础开始吧。
r 和 Python 拥有相似的特性,是数据科学家最常用的工具。两者都是开源的,因此是免费的,然而 Python 是一种广泛有用的编程语言,而 R 是为统计分析而创建的。
在本文中,我们将探讨这两种语言的优缺点,以便您可以决定哪种选择最适合您。
计算机编程语言
Python 编程语言开发于上世纪 80 年代末,在推动谷歌内部基础设施方面发挥着至关重要的作用。Python 由热情的开发者组成,现在它已经被广泛应用于 YouTube、Instagram、Quora 和 Dropbox。Python 在 IT 业务中被广泛使用,并允许开发团队内部的简单协作。这样,如果你需要一种适应性强、多理由的编程语言,一个庞大的工程师支持网络,以及可扩展的人工智能包,那么 Python 就是最佳选择。
Python 的优势
● 通用语言 —如果你的项目需要的不仅仅是统计数据,Python 被认为是更好的选择。例如,设计一个功能性网站
● 平滑的学习曲线——Python 易学易用,让你更快找到熟练的开发者。
● 大量重要的库 — Python 使用了无数的库来管理、收集和控制信息。以 Scikit-realize 为例,它包括信息挖掘和调查工具,以支持利用 Python 实现令人难以置信的人工智能便利性。另一个名为 Pandas 的包为工程师提供了优越的结构和数据检查设备,有助于缩短改进时间。如果你的开发团队需要 R 的一个主要功能,那么 RPy2 就是你要找的。
● 更好的集成 —通常,在任何工程环境中,Python 都比 r 集成得更好。因此,无论设计者是否试图利用 C、C++或 Java 等低级语言,它通常都能更好地将不同的组件与 Python wrapper 结合起来。此外,基于 python 的堆栈很容易创建,因此很难将数据研究人员手头的剩余任务整合进来。
● 提高生产力—Python 的语法非常容易理解,就像其他编程语言一样,但相对于 r 来说是独一无二的。通过这种方式,它保证了开发团队的高盈利能力。
Python 的缺点
●包括非常少的统计模型包。
●由于全局解释器锁(GIL)的存在,Python 中的线程处理变得很棘手,很成问题。随后,受 CPU 限制的多线程应用程序比单线程应用程序运行得更慢。人工智能事业对于执行多重处理比利用多线程编程更有价值。
R
r 是由统计学家开发的,基本上是为统计学家开发的,任何开发人员通过查看它的语法就可以预测到。由于这种语言包含了机器学习中的数学计算,而机器学习又是从统计学中派生出来的,所以 R 成为了想要更好地理解底层细节并进行创新的人的正确选择。如果您的项目主要基于统计数据,那么 R 可以被认为是缩小项目范围的绝佳选择,因为这需要一次性深入数据集。例如,如果您喜欢通过将段落解构为单词或短语来分析文本语料库,以识别它们的模式,那么 R 是最佳选择。
R 的优势
● 适合分析 —如果数据分析或可视化是您项目的核心,那么 R 可以被视为最佳选择,因为它允许快速原型制作,并与数据集一起设计机器学习模型。
● 大量有用的库和工具 —与 Python 类似,R 包含多个包,有助于提高机器学习项目的性能。例如,Caret 通过其特殊的功能集来增强 R 的机器学习能力,这有助于高效地创建预测模型。r 开发人员从高级数据分析包中获益,这些包覆盖了建模前和建模后阶段,针对特定的任务,如模型验证或数据可视化。
● 适合探索性的工作——如果你在项目的开始阶段需要在统计模型中做任何探索性的工作,那么 R 使得编写它们变得更加容易,因为开发人员只需要添加几行代码。
R 的缺点
●陡峭的学习曲线——很难否认 R 是一种具有挑战性的语言,因此你可以找到非常罕见的专家来构建你的项目团队。
● 不一致 —由于 R 的算法来自第三方,所以可能会出现不一致的情况。每次你的开发团队使用一个新的算法,所有连接的资源都需要学习不同的方法来建模数据和进行预测。与此类似,每个新的包都需要学习,并且没有详细的 R 文档,因为它会对开发速度产生负面影响。
R vs. Python:选择哪一个?
说到机器学习项目,R 和 Python 都各有优势。尽管如此,Python 似乎在数据操作和重复性任务方面表现更好。因此,如果你计划建立一个基于机器学习的数字产品,这是正确的选择。此外,如果您需要在项目的早期阶段开发一个专门的分析工具,那么请选择 r。最终的选择取决于您想要使用哪种编程语言。直到那时——继续学习!
作者简介:
【https://www.tatvasoft.com】维卡什·库马尔在一家软件开发公司工作。他喜欢分享关于机器学习、人工智能和许多其他方面的新想法。除了日常的专业工作,他还喜欢烹饪和四处闲逛。您可以访问这里了解他的公司,并在Twitter和LinkedIN上关注他。
Python 和 SQL 构建数据管道的比较
作为一名 web 开发人员,我第一次接触数据库和 SQL 是使用对象关系模型(ORM)。我使用的是 Django 查询集 API,使用该接口的体验非常好。从那以后,我转变成了一名数据工程师,更多地参与到利用数据集来构建人工智能的工作中。我的职责是从用户应用程序中提取数据,并将其转化为可供数据科学家使用的东西,这个过程通常被称为 ETL。
正如故事所述,生产系统中的数据是混乱的,在任何人能够利用这些数据构建人工智能之前,需要进行大量的转换。有些 JSON 列每行有不同的模式,有些列包含混合的数据类型,有些行有错误的值。此外,还需要计算用户成为客户的时间以及他们在两次访问之间等待的时间。当我着手清理、聚合和设计数据特性时,我试图决定哪种语言最适合这项任务。在我的工作中,我每天都要整天使用 python,我知道它可以完成这项工作。然而,我从这次经历中学到的是,仅仅因为 python 可以完成这项工作并不意味着它应该这样做。
我第一次误判 SQL 是在我认为 SQL 不能完成复杂转换的时候
我们正在使用一个时间序列数据集,我们希望在一段时间内跟踪特定的用户。隐私法阻止我们知道用户访问的具体日期,所以我们决定将记录的日期标准化为用户第一次访问的日期(即第一次访问后 5 天等)。).对于我们的分析,了解自上次访问以来的时间以及自首次访问以来的时间是很重要的。a 有两个样本数据集,一个有大约 750 万行,大小为 6.5 GBs,另一个有 550 000 行,大小为 900 MB。
使用下面的 python 和 SQL 代码,我使用较小的数据集首先测试转换。Python 和 SQL 分别用 591 和 40.9 秒完成了任务。这意味着 SQL 能够提供大约 14.5 倍的速度提升!
# PYTHON
# connect to db using wrapper around psycopg2
db = DatabaseConnection(db='db', user='username', password='password')# grab data from db and load into memory
df = db.run_query("SELECT * FROM cleaned_table;")
df = pd.DataFrame(df, columns=['user_id', 'series_id', 'timestamp'])# calculate time since first visit
df = df.assign(time_since_first=df.groupby('user_id', sort=False).timestamp.apply(lambda x: x - x.min()))# calculate time since last visit
df = df.assign(time_since_last=df.sort_values(['timestamp'], ascending=True).groupby('user_id', sort=False)['timestamp'].transform(pd.Series.diff))# save df to compressed csv
df.to_csv('transform_time_test.gz', compression='gzip') -- SQL equivalent
-- increase the working memory (be careful with this)
set work_mem='600MB';-- create a dual index on the partition
CREATE INDEX IF NOT EXISTS user_time_index ON table(user_id, timestamp);-- calculate time since last visit and time since first visit in one pass
SELECT *, AGE(timestamp, LAG(timestamp, 1, timestamp) OVER w) AS time_since_last, AGE(timestamp, FIRST_VALUE(timestamp) OVER w) AS time_since_first FROM table WINDOW w AS (PARTITION BY user_id ORDER BY timestamp);
这种 SQL 转换不仅速度更快,而且代码可读性更好,因此更易于维护。这里,我使用了 lag 和 first_value 函数来查找用户历史中的特定记录(称为分区)。然后,我使用年龄函数来确定访问之间的时间差。
更有趣的是,当这些转换脚本应用于 6.5 GB 数据集时,python 完全失败了。在 3 次尝试中,python 崩溃了 2 次,我的电脑第三次完全死机了…而 SQL 用了 226 秒。
更多信息:
https://www.postgresql.org/docs/9.5/functions-window.html
http://www . PostgreSQL tutorial . com/PostgreSQL-window-function/
我第二次误判 SQL 是在我认为它不能展平不规则 json 的时候
对我来说,另一个改变游戏规则的因素是意识到 Postgres 与 JSON 配合得非常好。我最初认为在 postgres 中不可能展平或解析 json 我不敢相信我竟然这么傻。如果您想要关联 json,并且它的模式在行之间是一致的,那么您最好的选择可能是使用 Postgres 内置的能力来解析 json。
-- SQL (the -> syntax is how you parse json)
SELECT user_json->'info'->>'name' as user_name FROM user_table;
另一方面,我的样本数据集中有一半的 json 不是有效的 json,因此被存储为文本。在这种情况下,我面临一个选择,要么重新编码数据使其有效,要么删除不符合规则的行。为此,我创建了一个名为 is_json 的新 SQL 函数,然后可以用它在 WHERE 子句中限定有效的 json。
-- SQL
create or replace function is_json(text)
returns boolean language plpgsql immutable as $$
begin
perform $1::json;
return true;
exception
when invalid_text_representation then
return false;
end $$;SELECT user_json->'info'->>'name' as user_name FROM user_table WHERE is_json(user_json);
不幸的是,我发现 user_json 有不同的模式,这取决于用户使用的应用程序版本。尽管从应用程序开发的角度来看,这是有意义的,但是有条件地解析每行的每种可能性的成本确实很高。我注定要再次进入 python 吗…一点机会都没有!我在 stack-overflow 上发现了另一个函数,是一个叫 klin 的 postgres 大神写的。
-- SQL
create or replace function create_jsonb_flat_view
(table_name text, regular_columns text, json_column text)
returns text language plpgsql as $$
declare
cols text;
begin
execute format ($ex$
select string_agg(format('%2$s->>%%1$L "%%1$s"', key), ', ')
from (
select distinct key
from %1$s, jsonb_each(%2$s)
order by 1
) s;
$ex$, table_name, json_column)
into cols;
execute format($ex$
drop view if exists %1$s_view;
create view %1$s_view as
select %2$s, %3$s from %1$s
$ex$, table_name, regular_columns, cols);
return cols;
end $$;
这个函数能够成功地展平我的 json,并相当容易地解决我最糟糕的噩梦。
最终意见
有一个习语宣称 Python 是几乎做任何事情的第二好语言。我相信这是真的,在某些情况下,我发现 Python 和“最佳”语言之间的性能差异可以忽略不计。然而,在这种情况下,python 无法与 SQL 竞争。这些认识以及我所做的阅读完全改变了我的 ETL 方法。我现在的工作模式是“不要把数据移动到代码,把代码移动到你的数据”。Python 将数据移动到代码中,而 SQL 就地对其进行操作。更重要的是,我知道我只是触及了 sql 和 postgres 能力的皮毛。我期待着更多令人敬畏的功能,以及通过使用分析仓库来提高速度的可能性。
Python vs. Web 抓取工具抓取梦幻足球投影
Photo by HENCE THE BOOM on Unsplash
梦幻足球是一个乏味的游戏。这么说的话,不研究是不可能拿冠军的。这就是网络抓取派上用场的原因。在本教程中,你将学习如何建立一个网页抓取工具来完成这项工作。
现在,您可以迅速地将有价值的数据自动整理到一个电子表格中。这比通过复制和粘贴从各种来源查找统计数据要容易得多,也快得多。
为什么是网络抓取?
根据这份报告,普通球员每周会花 3 个小时来管理他们的球队,另外 9 个小时用来阅读趋势。大约 30%的球员在日常工作中管理他们的球队。
大量信息唾手可得。准确预测团队成员的表现是很困难的。你如何挑选二线球员并取得顶级成绩?您需要跟踪游戏统计数据,并找到隐藏的值。
什么是网页抓取?
网络抓取是一种从网站上自动提取数据的技术。传统上,你需要一个程序员来编写脚本。
至于现在,一个网页抓取工具代替了编码的劳动。抓取不再是程序员的特权。任何人都可以从互联网上提取有价值的信息,并将其保存到本地存储或云上。
在这篇文章中,我将带你通过如何提取幻想足球投影点从体育网站一样,fantasypros.com 与网页抓取工具。
没有必要记录整个页面。你甚至可以更有创意,通过与对手的团队进行并排比较来获得全面的分析。
然后我们将其与 Python 脚本进行比较。所以你会有一个想法,对我们所有人来说,尤其是梦幻足球运动员,保持跟踪统计数据是多么容易。
免责声明:我是梦幻足球的新手。本文不提供起草策略的专业建议。相反,这是一篇从统计学角度分享知识的文章。
使用 Octoparse 进行网页抓取
先决条件:
Octoparse, 一个非常直观的网页抓取工具。它帮助我完成了数据分析项目中的许多障碍。这是市场上最好的。你可以在这里下载。
创建项目:
打开 Octoparse,点击小加号,用高级模型构建一个新任务。输入网址,Octoparse 会用内置浏览器打开网页。我们可以通过点击页面来交互和提取数据。
第一个,点击第一行的玩家。注意,Octoparse 将网站解析成单个元素。它找到了相似元素并用红色突出显示。
这太棒了。遵循操作提示,并单击“选择所有子元素”整行已被选中。然后,Octoparse 会提醒您它找到了准备好被选中的相似行。按照指南,点击“全选”
请注意,现在所有行都被成功选中,并以绿色突出显示。
**接下来,**点击“提取循环中的数据”。恭喜你!你完成了一个爬虫。【下载爬虫
最后但并非最不重要的,保存任务并在你选择的提取类型中开始提取。您可以在本地、在云上提取,或者设置一个时间表。在这种情况下,我强烈建议制定一个时间表。爬虫会及时抓取网站。所以你总是保持更新。
提取的数据将以结构化格式交付,包括 Excel、txt 和 JSON。由于我们需要分析这些点,所以我将它们导出到 Excel 中,看起来是这样的。
使用 Python 进行 Web 抓取
可以在这里 阅读完整的 Python 作品 。我把这个过程分成几个步骤:
- 浏览到所需页面并复制 URL 以备后用。
- 仔细检查 HTML 代码,找到要提取的数据所在的位置。在本例中,我们寻找“TR”(表格行)
- 找到您想要的数据周围的唯一标识符,如 href 链接、类名、表行和表数据。
- 尝试从一行数据中提取不同的字段
- 经历几次试错迭代。
- 规范数据格式(当我们提取原始数据时,使用奇怪的格式可能会使数据看起来很有趣。您需要清理字符格式,使它们一致和可读。)
最后
网页抓取运动项目既快又容易。然而,有了网络抓取工具,你可以在简单的点击中完成整个过程。我花了 1 个小时阅读 Beautiful Soup 的文档,尝试如何定位精确的字段并编写 Python 代码。
然而,我花了不到 10 分钟的时间用 Octoparse 设置了提取。最好的部分是,一旦你有了爬虫,你可以设置一个时间表,让它自动提取。
对于播放器,您可以通过更轻松地设置提取爬虫来同时监视不同的站点源:
你收集的数据越多,你的分析就越全面。现在,你甚至会在消息出来之前就获得第一手数据!
11 月 26 日,Octoparse 正在进行他们最好的黑色星期五早期交易,所有商品都有 10%的额外折扣。他们绝对值得你花几分钟时间来检查他们的产品和交易。
原载于 2019 年 11 月 22 日【https://likegeeks.com。
Python 网络抓取重构
我在旧博客上的第一篇文章是关于一个网络抓取的例子。网络抓取是使用 API 之外的一种方式,例如 tweepy 来为你的分析或你的机器学习模型获取信息。真实数据并不总是在一个方便的 CSV 文件中。你需要自己去拿。
我学到的例子来自凯文·马卡姆在 Youtube 上的网络抓取演示视频(Twitter 标签: @justmarkham )。他的例子使用了一个循环来收集所有的信息。但是 Python 有一个比循环更有效、可读性更强的工具。那些是列表理解。如果你不确定这些是什么,看看下面的帖子了解更多。一旦你完成了,请继续阅读,学习如何通过从这所布朗克斯高中的职员页面上搜集姓名、职位和电子邮件地址来有效地进行网络搜集。
[## Python 基础-列表理解
学习 python 几周后,我开始真正掌握了窍门。我喜欢有条件的心流,而且…
medium.com](https://medium.com/@erikgreenj/python-basics-list-comprehensions-30ef0df40fea)
注。根据您阅读本文的时间,最终结果可能会有所不同,因为网页上的数据可能已经发生了变化。最终结果截至 2019 年 4 月 3 日*
酷!我们如何开始?
让我们从导入和获取网页开始。
我们导入requests
来获得带有requests.get()
的 web_page。pandas
稍后将用于清理我们的报废数据。bs4
库中的BeautifulSoup
将用于解析我们的糊状 HTML,以帮助我们获得我们想要的信息。我们将在下面这样做。
接下来,我们将研究来自web_page
的 HTML 代码,以找到我们需要的代码段。要查看网页的 HTML 代码,请转到该网页,右键单击并选择“查看页面源代码”。然后,您可以按 ctrl-f 找到一个职员的名字,以查看嵌入了他们的名字和信息的 HTML 代码。
如果您稍微滚动一下代码,您应该会注意到代码行中包含了一些信息,例如:
<title>……</title>
或者
<p>…..</p>
这些在 HTML 代码中被称为标签。在这些标签之间隐藏着我们想要获取的信息。
由于我们看到所需的信息在<div>
标签和class=’matrix-content’
标签之间,我们可以假设所有教师的信息都在该类的每个标签中。这就是为什么我们使用标签和类作为 soup 的find_all
属性的参数。
我们需要从第一个教师简介出现的索引开始,因为我们只收集教师信息。第一个出场的老师是“布罗根先生”。你可以使用 ctrl-f 在 HTML 代码中搜索他的名字。如果数的话(当然是从 0 开始),布罗根先生的指数是 29。这就是为什么我们从指数 29 开始重新定义结果。对结果长度的检查和对被移除的员工的心算证实了我们可以进入下一步了!
现在获取所有教师数据!对吗?
Photo by Florian Olivo on Unsplash
我们会的。在此之前,我们应该先看看如何从一个老师那里获得我们想要的信息。然后我们将把它推广到我们的理解列表中。让我们来看一个教师简介的 HTML 代码。我们将再次检查布洛根先生的信息:
<div class="matrix-content">
<h5>Mr. Brogan</h5>
<div class="matrix-copy"><p>
Special Education: Geometry, Particular Topics of Geometry</p>
<p>
<em>rbrogan31@charter.newvisions.org</em></p>
</div>
</div>
同样,我们需要确定包含教师姓名、职位和电子邮件的标签。花点时间试着自己回答这个问题,然后继续读下去,看看你是否正确。这将为研究您需要在 python 抓取代码中指示 HTML 的哪些部分提供良好的实践。还记得我之前给你们看的例子吗?
*教师姓名标签:*姓名在标记为<h5>
的标签之间。
位置标签:位置位于类别标签<div class=”matrix-copy”>
后的<p>
标签之间。
邮件标签:邮件在标签<p>
和<em>
之间。由于<em>
标签直接封装了电子邮件,这就是我们将在抓取代码中指出的标签。
太好了!现在我们已经找到了需要指示的标签,让我们为我们的第一位老师编写代码,以确定我们将如何遍历老师条目来获取所有数据!
首先,我们将我们的第一位老师定义为test_result
。
*教师姓名:*通过在<h5>
标签上使用find
方法,我们得到了带有我们教师姓名的代码行。但这并不能给我们没有标签的名字。我们不希望代码中有标签。因此,为了提取姓名文本,我们将把.text
添加到find
方法中,以获得标签的文本属性。
Position(s) :我们将使用与名称相同的find
方法,但是这次我们的参数将是标签<p>
。这样做可以得到我们的位置,但同样我们不希望附加标签。再次使用.text
返回以下内容……
'\n\tSpecial Education: Geometry, Particular Topics of Geometry'
这给了我们比我们想要的更多。具体来说,我们在开始时得到了新行(\n
)和制表符(\t
)的字符串代码。由于我们的信息是在一个字符串中,我们可以用我们的代码行删除不需要的部分,从字符串的任何地方删除这些字符。
电子邮件:获取这些信息要简单得多。再次使用带有标签<em>
的find
方法作为我们的参数。使用.get_text()
方法有助于我们做到这一点,因为一些电子邮件嵌入在多个<em>
标签中。
现在我们得到了我们想要的所有数据!
没错!所以我们开门见山吧。
首先,我们初始化一个数据框对象。然后,我们使用列表理解结合来自test_result
的代码来获取所有教师的姓名和职位。我们还利用这些列表理解来创建数据帧df
的前两列。
当我第一次运行电子邮件收集的代码时,我遇到了一个属性错误。这就是变量管理器方便的地方。检查网页或 HTML 代码会发现“Veninga 女士”在<em>
标签中没有电子邮件地址。它位于第二组<p>
标签之间。因为页面很小,你可以这样做,但是对于较大的信息集合,你最好在列表理解产生错误的地方打印。
为了解决这个问题,我们将尝试创建一个get_email
函数,除了用<p>
上的find_all
方法设置第二组<p>
标签内的所有电子邮件,然后使用索引来获得我们想要的<p>
标签。我们还将删除多余的文本,就像获取职位信息一样。
与此同时,其他人的电子邮件将照常被删除。再次运行代码使我们能够成功地获得所有条目。可以通过检查记录列表的长度来证明这一点(它应该返回 66)。您可以使用df.shape[0]
来检查您的数据中的行数(66 个教师的 66 行)。
真快!我们去分析这些数据吧!
我们可以…但是我们会发现我们收集的数据有错误。您可以检查的一件事是,如果您有重复的条目,并删除它们。有些教师可能会教授多个科目(如数学和英语),因此他们的名字会出现多次。
通过对df.duplicated
的所有布尔值求和,我们得到值 11。所以我们有 11 个老师的名字出现了不止一次。然后,我们使用df.drop_duplicates
保留教师姓名的第一个条目,并丢弃其余条目。最后,我们将数据帧导出到一个 CSV 文件中,用于将来的分析。
最后的想法
网络抓取给你一种神奇的感觉,因为一旦你找到你需要的标签,你就可以从任何网站获取信息。我希望这个演练对那些考虑学习如何用 Python web scrap 的人有所帮助。
当然,可以做一些特征工程来帮助分析。我不打算包括这些细节,因为我想把重点放在网页抓取方面。
选项包括:
1.创建一个性别列,方法是在该时期按标题拆分姓名,然后使用 pandas 将标题映射到适当的性别。
2.将职位分开(因为大多数教师似乎教授不止一种类型的班级)。
对于熊猫练习,你可以尝试自己做上面的。
Photo by Thao Le Hoang on Unsplash
然后,我们可以转向 Tableau 或matplotlib
进行可视化和统计,以回答与教师人数和其他特许公立布朗克斯学校相比,这些数据的问题。
直到下一次,
约翰·德杰苏斯
Python 的集合模块——高性能容器数据类型。
Python 超级有用的集合模块的快速概述。
如果实现很难解释,这是个坏主意:Python 的禅
Python 是一种非常强大的语言,这种强大的力量很大一部分来自于它支持 模块化编程 。模块化编程本质上是将一个大而复杂的编程任务分解成更小且更易管理的子任务/ 模块的过程。模块就像乐高积木可以捆绑在一起创建一个更大的任务。
模块化在编写代码时有很多优势,比如:
- 可重用性
- 可维护性
- 简单性
函数、模块和包都是 Python 中促进代码模块化的构造。
目标
通过这篇文章,我们将探索 Python 的集合模块。该模块旨在改进功能,并为 Python 的通用内置容器(如 dict、list、set 和 tuple)提供替代方案。
介绍
让我们从快速浏览模块和包的概念开始这篇文章。
组件
模块只不过是一个可以在另一个模块中调用的. py 脚本。py 脚本。模块是包含 Python 定义和语句的文件,有助于实现一组函数。文件名是模块名加上后缀.py
。使用import
命令从其他模块导入模块。让我们导入数学模块。
*# import the library*
import math#Using it for taking the log
math.log(10)
2.302585092994046
Python 的内置模块
Python 有无数的内置模块,并且已经为你能想到的几乎所有用例创建了包。点击查看完整列表。
在探索 Python 中的模块时,两个非常重要的函数派上了用场——dir
和help
函数。
- 内置函数
[dir()](https://docs.python.org/3/library/functions.html#dir)
用于找出每个模块中实现了哪些功能。它返回字符串的排序列表:
print(dir(math))
- 在模块中找到我们想要的函数后,我们可以在 Python 解释器中使用
help
函数来了解更多信息:
help(math.factorial)
包装
包是堆叠在一起的相关模块的集合。核心机器学习包 Numpy 和 Scipy 由数百个模块组成。这里是在 SciPy 中可用的部分子包列表。
现在让我们跳到本文的实际目标,了解 Python 的集合模块。这只是一个概述,详细的解释和例子请参考官方 Python 文档。
集合模块
[集合](http://Container datatypes)是一个内置的 Python 模块,它实现了专门的容器数据类型,为 Python 的通用内置容器(如[dict](https://docs.python.org/3.6/library/stdtypes.html#dict)
、[list](https://docs.python.org/3.6/library/stdtypes.html#list)
、[set](https://docs.python.org/3.6/library/stdtypes.html#set)
和[tuple](https://docs.python.org/3.6/library/stdtypes.html#tuple)
)提供了替代方案。
本模块中一些有用的数据结构包括:
1\. [namedtuple()](https://docs.python.org/3.6/library/collections.html#collections.namedtuple)
存储在普通元组中的数据只能通过索引来访问,如下例所示:
plain_tuple = (10,11,12,13)plain_tuple[0]
10plain_tuple[3]
13
我们不能给存储在元组中的单个元素命名。现在,在简单的情况下可能不需要这个。然而,如果一个元组有许多字段,这可能是必要的,也会影响代码的可读性。
在这里, namedtuple 的功能开始发挥作用。它是一个用于具有名为字段的的元组的函数,可以看作是内置元组数据类型的扩展。命名元组为元组中的每个位置赋予意义,并允许更具可读性、自文档化的代码。存储在其中的每个对象都可以通过一个唯一的(人类可读的)标识符来访问,这使我们不必记住整数索引。我们来看看它的实现。
from collections import namedtuple
fruit = namedtuple('fruit','number variety color')guava = fruit(number=2,variety='HoneyCrisp',color='green')apple = fruit(number=5,variety='Granny Smith',color='red')
我们构造 namedtuple 的方法是,首先传递对象类型名称(fruit ),然后传递一个包含各种字段的字符串,作为字段名之间有空格的字符串。然后,我们可以调用各种属性:
guava.color
'green'apple.variety
'Granny Smith'
在 Python 中定义不可变类时,命名元组也是一个节省内存的选项。
2.计数器
Counter 是一个 dict 子类,帮助计算可散列对象。元素存储为字典键,而对象计数存储为值。让我们看几个关于计数器的例子。
#Importing Counter from collections
**from** **collections** **import** Counter
- 用绳子
c = Counter('abcacdabcacd')
print(c)Counter({'a': 4, 'c': 4, 'b': 2, 'd': 2})
- 使用列表
lst = [5,6,7,1,3,9,9,1,2,5,5,7,7]
c = Counter(lst)
print(c)Counter({'a': 4, 'c': 4, 'b': 2, 'd': 2})
- 带句子
s = 'the lazy dog jumped over another lazy dog'
words = s.split()
Counter(words)Counter({'another': 1, 'dog': 2, 'jumped': 1, 'lazy': 2, 'over': 1, 'the': 1})
计数器对象除了支持所有字典可用的方法之外,还支持三种方法:
- 元素()
返回每个元素的计数,如果元素的计数小于 1,则忽略它。
c = Counter(a=3, b=2, c=1, d=-2)
sorted(c.elements())['a', 'a', 'a', 'b', 'b', 'c']
- 最常见([n)
返回最常见元素及其计数的列表。元素的数量必须指定为 n。如果没有指定,它将返回所有元素的计数。
s = 'the lazy dog jumped over another lazy dog'
words = s.split()
Counter(words).most_common(3)[('lazy', 2), ('dog', 2), ('the', 1)]
使用 Counter()对象时的常见模式
sum(c.values()) # total of all counts
c.clear() # reset all counts
list(c) # list unique elements
set(c) # convert to a set
dict(c) # convert to a regular dictionary c.items() # convert to a list like (elem, cnt)
Counter(dict(list_of_pairs)) # convert from a list of(elem, cnt)
c.most_common()[:-n-1:-1] # n least common elements
c += Counter() # remove zero and negative counts
3.默认字典
字典是一种有效的方式来存储数据,以便以后检索,它有一组无序的键:值对。键必须是唯一且不可变的对象。
fruits = {'apple':300, 'guava': 200}
fruits['guava']200
如果值是整数或字符串,事情就简单了。然而,如果值是列表或字典等集合的形式,则值(空列表或字典)必须在第一次使用给定键时初始化。defaultdict 自动化并简化了这些东西。下面的例子将使它更加明显:
d = {}
print(d['A'])
这里,Python 字典抛出一个错误,因为‘A’当前不在字典中。现在让我们用 defaultdict 运行相同的示例。
from collections import defaultdict
d = defaultdict(object)
print(d['A'])<object object at 0x7fc9bed4cb00>
相反,defaultdict
将简单地创建您试图访问的任何项目(当然前提是它们还不存在)。defaultdict 也是一个类似字典的对象,它提供了字典提供的所有方法。然而,不同之处在于它将第一个参数(default_factory)作为字典的默认数据类型。
4.有序直接
一个 OrderedDict 是一个字典子类,它记住键第一次被插入的顺序。当遍历一个有序字典时,条目按照它们的键第一次被添加的顺序返回。因为有序字典会记住它的插入顺序,所以它可以与排序结合使用来创建一个有序字典:
- 正规字典
d = {'banana': 3, 'apple': 4, 'pear': 1, 'orange': 2}
- 按关键字排序的字典
OrderedDict(sorted(d.items(), key=**lambda** t: t[0]))OrderedDict([('apple', 4), ('banana', 3), ('orange', 2), ('pear', 1)])
- 按值排序的字典
OrderedDict(sorted(d.items(), key=lambda t: t[1]))OrderedDict([('pear', 1), ('orange', 2), ('banana', 3), ('apple', 4)])
- 字典按关键字串的长度排序
OrderedDict(sorted(d.items(), key=lambda t: len(t[0])))OrderedDict([('pear', 1), ('apple', 4), ('banana', 3), ('orange', 2)])
这里需要注意的一点是,在 Python 3.6 中,常规的字典都是 插入有序即 字典记住插入条目的顺序。在这里阅读讨论。
结论
集合模块还包含一些其他有用的数据类型,如 deque、Chainmap、UserString 等等。然而,我已经分享了我在日常编程中使用的一些方法来使事情变得简单。关于详细的解释和用法,请访问官方 Python 文档页面。
Python 的一个线性图形创建库,带有汉斯·罗斯林风格的动画
形象化
动画,单线图。它拥有一切
我清楚的记得海风来的时候。我真的受够了 Matplotlib。为了创建甚至简单的图形,我不得不运行这么多的 StackOverflow 线程。
我本可以花在思考如何呈现数据的好主意上的时间却被用来处理 Matplotlib。这很令人沮丧。
Seaborn 比 Matplotlib 好得多,但它也需要大量代码才能得到一个简单的“好看”的图形。
当 Plotly 出现时,它试图解决这个问题。当加上熊猫时,plotly 是一个很好的工具。
仅仅使用iplot
函数,你就可以用 Plotly 做这么多事情。
但还是,不是很直观。至少对我来说不是。
我仍然没有切换到 Plotly,只是因为我已经花了足够多的时间与 Seaborn 一起“快速”地做事情,我不想再花更多的时间去学习一个新的可视化库。我在 Seaborn 中创建了自己的函数来创建我最需要的可视化效果。然而这仍然是一种变通办法。我已经放弃了拥有更好的东西的希望。
绘声绘色地表达图中的。是不是很棒?
根据 Plotly Express 的创作者(显然也创作了 Plotly), Plotly Express 之于 Plotly,犹如 Seaborn 之于 Matplotlib。
围绕 Plotly.py 的简洁、一致的高级包装器,用于快速数据浏览和图形生成。
我只是想尝试一下。
创造者们让人们开始尝试它变得容易了吗?
为所欲为的俏皮话? ✅
标准化功能?学会创建散点图,你就差不多学会了这个工具——✅
互动图表?✅
**动画?**赛车条形图、时间散点图、—✅地图
免费开源? ✅
只是先睹为快,看看在这篇文章结束时我们将能够创造什么(以及更多)。 使用单行代码。
好了,谈够了,让我们开始吧。
首先是数据集——有趣、令人沮丧又令人振奋
我们将使用我从 Kaggle 获得的自杀数据集。这个数据集是根据来自联合国、世界银行和世界卫生组织的数据汇编而成的。 数据集积累了预防自杀的灵感。我总是支持对数据的这种良好利用。
你可以找到这篇文章的所有代码,并在这个 Kaggle 内核中运行它
首先,我将进行一些数据清理,以添加大陆信息和国家 ISO 代码,因为它们在以后会有所帮助:
import pandas as pd
import numpy as np
import plotly_express as px# Suicide Data
suicides = pd.read_csv("../input/suicide-rates-overview-1985-to-2016/master.csv")
del suicides['HDI for year']
del suicides['country-year']# Country ISO Codes
iso_country_map = pd.read_csv("../input/countries-iso-codes/wikipedia-iso-country-codes.csv")
iso_country_map = iso_country_map.rename(columns = {'English short name lower case':"country"})# Load Country Continents file
concap =pd.read_csv("../input/country-to-continent/countryContinent.csv", encoding='iso-8859-1')[['code_3', 'continent', 'sub_region']]
concap = concap.rename(columns = {'code_3':"Alpha-3 code"})correct_names = {'Cabo Verde': 'Cape Verde', 'Macau': 'Macao', 'Republic of Korea': "Korea, Democratic People's Republic of" ,
'Russian Federation': 'Russia',
'Saint Vincent and Grenadines':'Saint Vincent and the Grenadines'
, 'United States': 'United States Of America'}def correct_country(x):
if x in correct_names:
return correct_names[x]
else:
return xsuicides['country'] = suicides['country'].apply(lambda x : correct_country(x))suicides = pd.merge(suicides,iso_country_map,on='country',how='left')
suicides = pd.merge(suicides,concap,on='Alpha-3 code',how='left')suicides['gdp'] = suicides['gdp_per_capita ($)']*suicides['population']
让我们看看自杀数据:
我还会按大洲对数据进行分组。老实说,我这样做只是为了展示这个库的威力,因为这篇文章的主要目标仍然是创建令人敬畏的可视化效果。
suicides_gby_Continent = suicides.groupby(['continent','sex','year']).aggregate(np.sum).reset_index()suicides_gby_Continent['gdp_per_capita ($)'] = suicides_gby_Continent['gdp']/suicides_gby_Continent['population']suicides_gby_Continent['suicides/100k pop'] = suicides_gby_Continent['suicides_no']*1000/suicides_gby_Continent['population']# 2016 data is not full
suicides_gby_Continent=suicides_gby_Continent[suicides_gby_Continent['year']!=2016]
suicides_gby_Continent.head()
我们创建的最终数据:
简单易用
我们准备好可视化我们的数据。来时隐晦地表达到的时间。我可以通过一个简单的:
pip install **plotly_express**
并将其导入为:
import plotly_express as px
现在让我们用它创建一个简单的散点图。
suicides_gby_Continent_2007 = suicides_gby_Continent[suicides_gby_Continent['year']==2007]px.scatter(suicides_gby_Continent_2007,x = 'suicides/100k pop', y = 'gdp_per_capita ($)')
不太鼓舞人心。没错。让我们一步一步地做得更好。让我们按洲给点上色。
px.scatter(suicides_gby_Continent_2007,x = 'suicides/100k pop', y = 'gdp_per_capita ($)',color='continent')
好但不励志。还没有。
点看起来好小。没错。让我们增加点的大小。怎么会?参数可能是什么…
px.scatter(suicides_gby_Continent_2007,x = 'suicides/100k pop', y = 'gdp_per_capita ($)',color='ContinentName',size ='suicides/100k pop')
你能看出每个洲都有两个点吗?它们代表男性和女性。让我在图表中展示一下。我们可以用几种方法来说明这种区别。 我们可以用不同的 ***symbol***
或者用不同的 ***facets***
来表示男女
让我给他们两个看看。
px.scatter(suicides_gby_Continent_2007,x = 'suicides/100k pop', y = 'gdp_per_capita ($)', size = 'suicides/100k pop', color='ContinentName',symbol='sex')Orpx.scatter(suicides_gby_Continent_2007,x = 'suicides/100k pop', y = 'gdp_per_capita ($)', size = 'suicides/100k pop', color='continent',facet_col='sex')
Fig1: Symbol, Fig2: Facets
在符号图中,三角形代表男性,圆形代表女性。我们已经开始从图表中看到一些好的信息。以为例:
- 至少在 2007 年的数据中,男性和女性的自杀率存在显著差异。
- 2007 年欧洲男性极易自杀?
- 收入差距似乎对自杀率没有太大影响。亚洲的人均 GDP 比欧洲低,自杀率也比欧洲低。
- 男性和女性之间似乎没有收入差距。
还不励志?嗯。 让我们添加一些动画 。这应该不难。我会添加更多的参数,
- 它指定了我们的动画维度。
- 使用
range_y
和range_x
的 x 和 y 值范围 text
用大洲标注所有点。有助于更好地可视化数据
px.scatter(suicides_gby_Continent,x = 'suicides/100k pop', y = 'gdp_per_capita ($)',color='continent',
size='suicides/100k pop',symbol='sex',animation_frame='year', animation_group='continent',range_x = [0,0.6],
range_y = [0,70000],text='continent')
等待 gif 图显示。
在 Jupyter 笔记本中,你将能够停止可视化,悬停在点上,只需查看特定的大陆,并通过交互做更多的事情。
一个命令就能提供如此多的信息。我们可以看到:
- 从 1991 年到 2001 年,欧洲男性的自杀率非常低。
- 大洋洲即使有相当高的人均国内生产总值,它仍然容易发生自杀事件。
- 与其他国家相比,非洲的自杀率较低。
- 就美洲而言,自杀率一直在逐渐上升。
我以上所有的观察都需要更多的分析。但这就是在一张图上有这么多信息的意义所在。它会帮助你提出很多假设。
以上剧情风格被称为汉斯·罗斯林剧情以其创始人命名。
在这里,我想让你们看看这个来自汉斯·罗斯林的演示,他使用 Gapminder 数据来解释收入和寿命是如何随着时间的推移而出现的。看到了。太棒了。
Hans Rosling Gapminder Visualization….
功能标准化
到目前为止,我们已经了解了散点图。花了这么多时间去学一门图表课。在我的帖子的开始,我告诉你这个库有一种标准化的功能。
让我们具体看看欧洲的数据,因为我们看到欧洲男性的自杀率很高。
european_suicide_data = suicides[suicides['continent'] =='Europe']
european_suicide_data_gby = european_suicide_data.groupby(['age','sex','year']).aggregate(np.sum).reset_index()
european_suicide_data_gby['suicides/100k pop'] = european_suicide_data_gby['suicides_no']*1000/european_suicide_data_gby['population'] # A single line to create an animated Bar chart too.
px.bar(european_suicide_data_gby,x='age',y='suicides/100k pop',facet_col='sex',animation_frame='year',
animation_group='age',
category_orders={'age':['5-14 years', '15-24 years', '25-34 years', '35-54 years',
'55-74 years', '75+ years']},range_y=[0,1])
就这样,我们也学会了如何制作条形图的动画。在上面的函数中,我为轴提供了一个category_order
来强制分类的顺序,因为它们是有序的。其余的一切还是老样子。
我们可以看到,从 1991 年到 2001 年,75 岁以上男性的自杀率非常高。这可能会增加男性的整体自杀率。
想使用地图查看一个国家的自杀率是如何降低的?这就是我们在数据中获得国家 ISO 代码的原因。
这需要多少行?你猜对了。一个。
suicides_map = suicides.groupby(['year','country','Alpha-3 code']).aggregate(np.sum).reset_index()[['country','Alpha-3 code','suicides_no','population','year']]suicides_map["suicides/100k pop"]=suicides_map["suicides_no"]*1000/suicides_map["population"]px.choropleth(suicides_map, locations="Alpha-3 code", color="suicides/100k pop", hover_name="country", animation_frame="year",
color_continuous_scale=px.colors.sequential.Plasma)
上图显示了不同国家的自杀率随时间的变化,根据我们从图中得到的信息,所需的编码工作是最小的。我们可以看到:
- 很多国家都不见了
- 非洲国家的数据很少
- 几乎整个亚洲都不见了。
只要看到上面的图表,我们就能很好地理解我们的数据。
时间轴上的动画也增加了很多价值,因为我们能够使用一个图表来查看所有数据。
这可以帮助我们发现数据中隐藏的模式。你不得不承认,它看起来也很酷。
结论
这只是 Plotly Express 的一个预览。您可以使用这个库做许多其他事情。
我喜欢这个库的主要原因是它试图简化图形创建的方式。以及这些图表开箱后看起来有多酷。
想想在 Seaborn 或 Matplotlib 甚至 Plotly 中创建相同的图形需要多长时间。你将会更加感激图书馆所提供的力量。
Plotly 的这个项目缺少一些文档,但是我发现这些函数都有很好的文档记录。在 Jupyter 中,您可以看到使用Shift+Tab
的函数定义。
同样根据它的公告文章:“Plotly Express 是完全免费的:凭借其许可的开源 MIT 许可证,你可以按照你喜欢的方式使用它(是的,甚至在商业产品中!)."
因此,现在没有借口推迟这一设想。开始吧…
你可以找到这篇文章的所有代码,并在这个 Kaggle 内核中运行它
如果你想了解创建可视化的最佳策略,我想从密歇根大学调用一门关于 数据可视化和应用绘图 的优秀课程,它是一个很好的 数据科学专业的一部分,Python 本身就是其中之一。一定要去看看
将来我也会写更多初学者友好的帖子。关注我在 媒体 或者订阅我的 博客 了解他们。一如既往,我欢迎反馈和建设性的批评,可以通过 Twitter @mlwhiz 联系到我