Python 列表理解
Python 列表终极指南!
成为列表理解专家
列表理解是处理 Python 列表时最强大的特性之一。这个功能已经触手可及。不需要导入任何库或模块,因为列表理解是 Python 的一个内置特性。
如何写列表理解
List Comprehension 可用于创建仅在一行中包含限制(用数学语句编写)的列表。除此之外,列表理解可以用来从另一个列表中创建一个新列表。
列表理解语法很容易理解,别担心,它很简单。做列表理解,需要先做一个方括号[]
。在括号内,您需要使用 for 关键字,后跟一个或多个您选择的子句。这里的表达可以是任何东西,你可以倾注你的创造力。
基本语法
[**expression** for **element** in **list** if **conditional**]
以上列表理解等同于:
for **element** in **list**:
if **conditional**:
**expression** #appends the resulting element to the list
例子
当然,我会给你提供一些例子!否则这篇文章就不完整。我将从最简单的开始,继续讨论更高级的应用程序。
生成新列表
1.制作一个由前 5 个自然数组成的新列表。
ls = [i for i in range(1, 6)] #ls = [1, 2, 3, 4, 5]
2.制作前 5 个偶数自然数的新列表。
ls = [i for i in range(2, 11, 2)] #ls = [2, 4, 6, 8, 10]
或者使用 if 来说明如何使用条件来创建新列表。
ls = [i for i in range(2, 11) if i % 2 == 0] #ls = [2, 4, 6, 8, 10]
3.列出前 5 个平方数
ls = [i**2 for i in range(1, 6)] #ls = [1, 4, 9, 16, 25]
使用我们声明的函数
列表理解的条件不需要明确地写在方括号内。你可以预先声明一个函数,然后在列表理解中调用它。在下一个例子中,我们将这样做。
4.列出 1 到 30 之间的质数
import mathdef isPrime(n):
if n == 1:
return False
elif n == 2:
return True
elif n % 2 == 0:
return False
else:
for i in range(3, int(math.sqrt(n)) + 1, 2):
if n % i == 0:
return False
return Truels = [i for i in range(1, 31) if isPrime(i)]
#ls = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29]
从另一个列表创建新列表
列表理解的另一个有用的用法是从另一个列表中创建一个新列表。你可以这样想,你正在从原始列表中选择满足你需求的元素。然后,将这些项目分组到一个新列表中。以下是一些例子:
5.只取原始列表中的偶数
ls = [15, 6, 1, 3, 10]result = [i for i in ls if i % 2 == 0] #result = [6, 10]
6.查找一个列表中也存在于另一个列表中的元素
ls = [15, 6, 1, 3, 10]
ls2 = [1, 2, 3, 4, 5, 6]result = [i for i in ls if i in ls2] #result = [6, 1, 3]
最后的话
Python 列表理解是您在编程工具箱中绝对需要的一项技术。它功能强大,易于在任何地方实现。过一段时间,相信我,你会习惯的。在这一点上,你可以随时轻松地使用它。如果您觉得这篇文章很有用,并且想了解更多关于 Python 的其他技巧,您可以阅读这篇文章:成为 Python“一行程序”专家。
最诚挚的问候,
弧度克里斯诺
Python 中的列表理解
列表理解简介
Python 提供了列表理解结构,使得定义列表、过滤列表、基于现有列表创建新列表等等变得容易。与传统的“for-loops”相比,list comprehension 是用 python 创建列表的一种更简洁、可读性更强的替代方法。在这篇文章中,我们将讨论如何使用列表理解在 python 中创建列表。我们也将在每个例子中比较列表理解方法和使用传统的“for-loops”。
我们开始吧!
首先,让我们考虑在“for-loop”中生成 1-10 的正整数列表的任务。让我们初始化一个名为“pos_list”的列表:
pos_list = []
通过在“for-loop”中迭代“range()”方法返回的生成器,我们可以将值追加到该列表中:
for i in range(1, 11):
pos_list.append(i)
让我们打印结果列表:
print(pos_list)
这很简单。现在假设我们想生成一个偶数整数的列表。我们可以添加一个条件来检查元素是否能被 2 整除,并仅在条件为真时追加:
even_list = []
for i in range(1, 11):
if i%2 == 0:
even_list.append(i)
让我们打印结果:
print(even_list)
虽然使用“for-loop”可以完成工作,但列表理解提供了一种更简单的方法,可以用更少的代码获得相同的结果。首先,让我们看看如何使用列表理解来生成正整数的原始列表:
lc_pos_list = [i for i in range(1,11)]
print(lc_pos_list)
将这与我们不得不使用“for-loop”的最初三行代码进行比较:
for i in range(1, 11):
pos_list.append(i)
print(pos_list)
现在让我们使用列表理解来生成偶数列表:
lc_even_list = [i for i in range(1,11) if i%2]
print(lc_even_list)
不过,与使用“for-loops”的五行代码相比,这只使用了两行代码:
even_list = []
for i in range(1, 11):
if i%2 == 0:
even_list.append(i)
print(even_list)
我们也可以使用列表理解很容易地过滤列表。让我们考虑使用“for-loop”从正整数列表中过滤小于 5 的值的任务:
pos_list_gt_5 = []
for i in pos_list:
if i >= 5:
pos_list_gt_5.append(i)
print(pos_list_gt_5)
使用列表理解:
lc_pos_list_gt_5 = [i for i in pos_list if i >= 5]
print(lc_pos_list_gt_5)
如你所见,我们使用了更少的代码。接下来,让我们考虑使用列表理解来迭代多个列表的任务。假设我们有一个从 1 到 10 的整数列表,和另一个从 20 到 30 的整数列表:
pos_list2 = []
for i in range(20, 30):
pos_list2.append(i)
print(pos_list)
print(pos_list2)
让我们编写一个“for-loop ”,对两个列表和进行迭代,并将它们的元素的乘积追加到一个新列表中:
multiply_list = []
for i, j in zip(pos_list, pos_list2):
multiply_list.append(i*j)
print(multiply_list)
我们可以使用列表理解来执行相同的操作:
lc_multiply_list = [i*j for i,j in zip(pos_list, pos_list2)]
print(lc_multiply_list)
我们甚至可以应用条件。让我们将乘积小于 200 的元素的乘积相加:
lc_multiply_list_filter = [i*j for i,j in zip(pos_list, pos_list2) if i*j < 200]
print(lc_multiply_list_filter)
我就讲到这里,但是我鼓励你自己去研究代码。
结论
总之,在这篇文章中,我们将讨论如何使用 python 中的列表理解来构造列表。我们表明,虽然我们可以使用“for-loops”完成相同的任务,但列表理解提供了一种更容易和更易读的方法来构建列表。我希望你觉得这篇文章有用/有趣。这篇文章中的代码可以在 GitHub 上找到。感谢您的阅读!
Python 列表方法
Python 必须提供的列表方法的总结
马库斯·温克勒在 Unsplash 上的照片
什么是列表,什么是列表方法?
在 Python 中,列表是数据片段的集合。列表由方括号[ ]包围,每个项目由逗号(,)分隔,可以包含从零到无穷大的任何项目(或者您的计算机允许的任何数量)。字符串、数字、布尔值,甚至其他列表都可以是列表中的项目。列表是有序的和可变的(可改变的),这意味着每个项目被分配到一个特定的索引,可以排序,并有能力被改变。一旦你有了一个列表,你就可以使用所谓的“方法”来操作这个列表。像字符串方法一样,要使用列表方法,只需编写列表,后跟。【方法】()。例如,要在列表[‘意大利香肠’,‘香肠’,‘蘑菇’]上运行 append() 方法,您只需编写[‘意大利香肠’,‘香肠’,‘蘑菇’]。追加(‘洋葱’);如果该列表被设置为一个变量,您应该使用 variable.append('onion ')。正如您在 append() 示例中看到的,一些方法接受所谓的“参数”,这些参数放在括号中,进一步定义了该方法将做什么。Python 语言有很多内置的方法,比如 append() ,允许你轻松地改变列表。
append()和 extend()
append() 方法允许您将另一个项目添加到列表的末尾。该方法使用一个必需的参数,这是您希望添加到列表中的项目。
Syntax: *list*.append(*item*)**toppings = ['pepperoni', 'sausage', 'mushroom']**toppings.append('onion')
--> ['pepperoni', 'sausage', 'mushroom', 'onion']
extend() 方法类似于 append(),因为它允许您添加到列表中;但是,extend()方法允许您添加另一个 iterable(列表、元组、集合等)中的所有项。)作为单独的项目而不是一个项目添加到列表的末尾。该方法采用一个必需的参数,即 iterable。
Syntax: *list*.extend(*iterable*)**toppings = ['pepperoni', 'sausage', 'mushroom']
more_toppings = ['onion', 'bacon']**toppings.**extend**(more_toppings)
--> ['pepperoni', 'sausage', 'mushroom', 'onion', 'bacon'] *To contrast...**toppings.****append****(more_toppings)
--> ['pepperoni', 'sausage', 'mushroom', ['onion', 'bacon']]*
如上所述,当 extend()方法用于 iterable 时,iterable 中的每一项都被添加到列表中,作为不再有界的单独项。相反,当 append()方法使用 iterable 作为参数时,整个 iterable 作为一项添加到列表中。密切注意列表中的逗号和括号总是很重要的。
pop()和 remove()
pop() 方法允许您从列表中移除指定索引值处的元素。该方法可以接受一个可选参数,即您希望删除的索引的整数值——默认情况下,pop()将删除列表中的最后一项,因为默认值为-1。
Syntax: *list*.pop(*index*)**toppings = ['pepperoni', 'sausage', 'mushroom']**toppings.pop(1)
--> ['pepperoni', 'mushroom']toppings.pop()
--> ['pepperoni', 'sausage'] *If you wanted to retrieve the removed item...*extra = toppings.pop(1)
extra --> 'sausage'
像 pop()方法一样, remove() 方法允许您从列表中删除一个项目。不过,remove()方法删除列表中第一个出现的指定值。该方法使用一个必需的参数,即您希望移除的项目。
Syntax: *list*.remove(*item*)**toppings = ['pepperoni', 'sausage', 'mushroom']**toppings.remove('sausage')
--> ['pepperoni', 'mushroom']
对于 pop()和 remove(),如果参数超出范围或不在列表中,您将分别得到一个错误。
排序()和反转()
方法按照一定的标准对列表进行排序。该方法可以带两个可选参数。第一个参数是设置 reverse=True 或 reverse=False 。默认情况下,该参数设置为 reverse=False ,如果列表仅包含字符串,则按字母顺序排列;如果列表仅包含数字,则按升序排列。第二个参数允许您将一个 key= 设置为一个函数,如果它比 sort()方法的默认排序更复杂,您可以使用该函数来指定您希望列表如何精确排序。这可能是一个内置的 Python 函数,一个你在程序中其他地方定义的函数,或者是你写的一个内嵌的λ函数。
Syntax: *list*.sort(reverse=True|False, key=function)**toppings = ['pepperoni', 'sausage', 'mushroom']**toppings.sort()
--> ['mushroom', 'pepperoni', 'sausage']toppings.sort(reverse=True)
--> ['sausage', 'pepperoni', 'mushroom']toppings.sort(reverse=True, key=lambda x: len(x))
--> ['pepperoni', 'mushroom', 'sausage']
** Sorted in reverse order by length of the topping name* **prices = [1.50, 2.00, 0.50]**prices.sort(reverse=False)
--> [0.50, 1.50, 2.00]prices.sort(reverse=True)
--> [2.00, 1.50, 0.50] **pies = [['bacon', 'ranch'], ['sausage', 'peppers']]**pies.sort(reverse=True)
--> [['sausage', 'peppers'], ['bacon', 'ranch']]
** Sorts iterators by their first value*
方法只是颠倒了列表中条目的顺序。该方法不带任何参数。
Syntax: *list*.reverse()**toppings = ['pepperoni', 'sausage', 'mushroom']**toppings.reverse()
--> ['mushroom', 'sausage', 'pepperoni'] **prices = [1.50, 2.00, 0.50]**prices.reverse()
--> [0.50, 2.00, 1.50]
计数()
方法的作用是:返回一个列表中指定条目出现的次数。该方法接受一个必需的参数,这是您希望计算其计数的项目。如果您希望找出哪些项目在列表中出现了不止一次,这种方法会很有用。
Syntax: *list*.count(*item*)**toppings = ['pepperoni', 'sausage', 'mushroom', 'sausage']**toppings.count('sausage')
--> 2toppings.count('pepperoni')
--> 1toppings.count('bacon')
--> 0
索引()
index() 方法返回指定项目第一次出现的索引。该方法使用一个必需的参数,这是您希望查找其索引的项目。如果该项目不在列表中,您将得到一个错误。
Syntax: *list*.index(*item*)**toppings = ['pepperoni', 'sausage', 'mushroom', 'sausage']**toppings.index('mushroom')
--> 2toppings.index('pepperoni')
--> 0
插入()
方法的作用是:将一个指定的条目插入到一个指定索引的列表中。该方法采用两个必需的参数——您希望插入值的整数索引和您希望插入的项目。
Syntax: *list*.insert(*index*, index)**toppings = ['pepperoni', 'sausage', 'mushroom']**toppings.insert(1, 'onion')
--> ['pepperoni', 'onion', 'sausage', 'mushroom']
复制()
copy() 方法只是返回你的列表的一个副本。该方法不带参数。
Syntax: *list*.copy()**toppings = ['pepperoni', 'sausage', 'mushroom']**toppings**2** = toppings.copy()
toppings**2** --> ['pepperoni', 'sausage', 'mushroom']
清除()
clear() 方法只是从一个列表中删除所有的条目,留下一个空列表。该方法采用无参数。
Syntax: *list*.clear()**toppings = ['pepperoni', 'sausage', 'mushroom']**toppings.clear()
--> []
仅此而已。有了上述这些方法,您应该可以对 Python 列表执行所需的操作。更多信息和例子,请务必查看 w3schools 列表。
祝你编码愉快,一定要看看我下面关于 Python 字符串方法的博客!
Python 中最常用的字符串方法的总结
towardsdatascience.com](/python-string-methods-7ac76ed7590b)
参考资料:
Python 有一组内置的方法,可以用在列表/数组上。注意:Python 没有对…的内置支持
www.w3schools.com](https://www.w3schools.com/python/python_ref_list.asp)
Python:蒙特卡洛遇上体育分析
使用模拟来收敛棘手的概率分布
信用:Pixabay
大约一个月前,当我在研究生院的最后一个学期和找工作的时候,我面试了一家体育分析公司。我的背景是商业分析,特别是在新颖的商业应用中使用 NLP、网络分析和推荐系统。总而言之,我不是理想的候选人。
在进入任何累人的数据科学面试问题之前,我接受了一次带回家的测试;这并不令人吃惊。许多公司知道这个过程可能成功也可能失败。因此存在一些固有的“我们只是在浪费时间”的风险。把测试带回家是公司消除这种风险的好方法;这对每个人都很好,除了求职者。如果你的提交被认为不令人满意,公司已经在你身上浪费了比预期更多的时间,并将立即转向下一个候选人。为您提供高质量的反馈是数据科学团队不必要的负担。在他们的辩护中,一些角色得到了成百上千的申请者。指导每个人如何做得更好是不可行的。尽管如此,作为一名数据科学家,执行大量的带回家测试并不一定能提高你的水平。让你进步的是从你的错误中学习——这是你的责任(也是你自己的责任)。)
我搞砸了带回家的考试,花了几个小时绞尽脑汁思考我本可以做得更好的地方,然后完全搁置了这个话题。直到昨晚我突然想到一个主意!不过我言过其实了,还是讨论一下面试问题吧。
在这次采访中,我需要(A)重现当前的 NBA 彩票概率,( B)为修改后的彩票产生新的概率。
NBA 彩票的运作方式如下:球队按常规赛表现排名,最好的球队排在最后,最差的球队排在第一。我的意思是,在有限数量的球队被选为第一,第二,…第 n 选秀之后,剩下的球队将按照他们常规赛表现的(相反)顺序(最好的最后,最差的第一— 希望让接下来的赛季更有趣。)在实际的 NBA 选秀中,从 14 支球队中选出 4 支球队。在提议的草案系统中,必须从 16 支队伍中挑选出 5 支队伍。
挑选队伍的方法是从彩票机中取出四个球,并确定球的独特组合来自哪个队伍。然后球被重新插入(该组合已被使用,但这些相同的球参与了许多更独特的组合网尚未检索)。)每个团队都分配到不同数量的组合。最差的团队组合最多,而最好的团队组合最少。
我完全不知道如何开始。我的统计知识较少植根于概率论,更多地与机器学习相结合。所以我做了一些谷歌搜索,发现了一篇 2020 平方的文章,以确定性的方式解决了离散概率分布。
9 月 28 日,NBA 理事会批准了对 NBA 选秀抽签系统的修改。这些变化是…
squared2020.com](https://squared2020.com/2017/09/30/how-nba-draft-lottery-probabilities-are-constructed/)
注意我说确定性的时候。这被证明是我心中的一个定位点。我决心理解这些方法、代码等,并复制我自己的方法。如果你跟随教程,你会注意到第一轮的选择只是,选择的概率(组合数除以总组合数。)第二轮的条件是任何其他队伍先被选中。并且这个过程在前四个选秀中重复。然而,一旦我们到了第五轮,我们不再选择队伍,而是简单地将剩下的队伍按照他们当前的顺序添加到被选择的队伍中。(我从来没有通过第四轮选秀!)
确定性地解决这个问题是一场噩梦,也是对一个人掌握概率论的真实证明。这就是我没有被选中参加下一轮面试的原因。
大约一个月后,我坐在沙发上,突然想到——也许我应该用蒙特卡罗方法从分布中取样?我暂停了在网飞的冒险,开始写代码。结果呢?这是可行的,当彩票设计发生变化时,它比确定性地写这个更具可扩展性。
我的方法
Python 非常适合生成随机数。事实上,您可以从给定概率分布的池中随机选择一个值。给定分配给每个团队的组合数量,确定它们的概率是微不足道的。
我定义了两个不同的函数,试验和模拟。选拔赛是抽签的一次试运行,根据他们的选择概率选出四名候选人,剩下的队伍被添加到名单中。在我的设计中,当一个给定的团队被选中时,我将他们的值编入索引,并从 pool 和 dist 变量(列表)中与该索引对应的元素中删除。但是,一旦从 dist 变量中删除了一个概率,它的总和就不再是 1。我没有考虑到这一点,但很高兴地得知 python 的随机包将根据需要为您调整这一点!
模拟,有点棘手。给定提供的回合数,试验函数将被执行 x 次,结果将被附加到一个列表中。您将得到一个嵌套列表,其中外部列表包含 x 个元素,每个 x 元素都是长度为 14 的试运行。在这一点上,我们使用列表理解遍历排名的每个位置。我们不是查看 1000 个元素的嵌套列表(每个元素包含 14 个值),而是创建 14 个元素的嵌套列表(每个元素包含 1000 个值。)这是真正的秘制酱料。第一份这样的名单将包含第一次选秀被选中的每支球队。第二个这样的列表将包含第二次选秀被抽样的每支球队。诸如此类。
使用 Counter 函数(来自 collections),可以得到一个的计数,哪个队在哪个地方呆了多少次?“从这里开始,只需将每个计数除以总和(模拟的总数)就可以得到每个位置的概率分布。你会注意到这个矩阵是面向列的,而 NBA 官方的彩票概率分布是面向行的。我们只需将嵌套列表传递给 numpy 并进行转置。嘣。完成了。但愿我上个月就想到了这一点!
在下面的最终代码中,我注释掉了常规赛变量,并在每行下面定义了一个建议的系统变量。(实际上,它在建议的系统下找到了分布。)经过 5,000,000 次迭代,它收敛于精确值!
结果(提示,与维基百科比较)
感谢阅读!如果你认为我的内容没问题,请订阅:)
Python 多线程与多处理
基准测试并发任务执行的两种方法:Python 中的多线程和多处理。
克里斯·里德在 Unsplash.com 的照片
当处理大量要执行的任务时,人们宁愿不要顺序执行任务,因为这是一个漫长、缓慢且相当无聊的过程。相反,我们想要的是我们的程序同时启动所有的任务,这样它们可以并排完成。
并行性或并发任务执行并不是一个新概念,大多数主流语言都支持它。Python 也不例外,它确实提供了一个非常简洁的模块,可以很容易地用来以并行或并发的方式运行任务。我在这篇文章中的目标是提供关于 python 中多线程和多处理如何工作的简要概述,并且我将对两者的性能进行基准测试。本文将涵盖以下主题:
- 并发和并行的区别。
- Python 中的多线程。
- Python 中的多处理。
- 多线程和多处理的基准测试。
- 什么样的节目用哪种方法比较好。
并发和并行的区别
并发性和并行性是两个经常被混淆的术语,它们虽然相同,但却有着非常不同的含义。
并发本质上被定义为同时处理大量工作或同一程序的不同工作单元。
在编程环境中,同一程序的不同部分被分配给处理器的不同内核,这些内核独立执行每个部分并相互通信,从而同时完成更多的工作。
另一方面,并行性可以简单地定义为
同时做同一程序的大量工作以加快执行时间。
在编程环境中,同一程序的不同部分并行执行,并且每个部分的执行同时发生,因此加快了程序的执行时间。
Python 中的多线程
在 Python 中,我们可以使用 concurrent.futures 模块实现多线程的功能。它为启动异步任务提供了一个高级 API。 ThreadPoolExecutor 类提供了一个启动和管理线程的接口。
import concurrent.futures
with concurrent.futures.ThreadPoolExecutor() as executor:
executor.map(function_name, iterable)
我们用上下文管理器创建 executor 对象,并在我们想要并行执行的方法上调用 map 函数。 map 函数将为输入 iterable 的每个值创建一个线程。
Python 中多线程的工作原理:
尽管我们说 python 支持多线程,但是幕后发生的事情却非常不同。在 python 中,每个进程都在一个内核上执行。因此,当我们创建同一进程的多个线程时,每个线程都在同一内核上执行,因此共享资源和内存空间。为了防止一个线程因为共享相同的资源而改变另一个线程的执行结果,Python 引入了**“全局解释器锁”**的概念。
本质上,它一次只允许一个线程使用共享的内存空间和资源,并在线程之间进行智能上下文切换,因此没有线程可以改变其他线程的工作。因此,在 python 中使用多线程的并行性是一个相当牵强的术语,因为我们并没有以这种方式实现真正的并行性。
Python 中的多重处理
在 Python 中,我们可以使用同一个 concurrent.futures 模块实现多处理的功能。 ProcessPoolExecutor 类提供了一个启动和管理多个进程的接口。
import concurrent.futures
with concurrent.futures.ProcessPoolExecutor() as executor:
executor.map(function_name, iterable)
就像在多线程中一样,我们用上下文管理器创建 executor 对象,并在我们想要并行执行的方法上调用 map 函数。 map 函数将为输入 iterable 的每个值创建一个过程。
Python 中的多处理是如何工作的:
在多处理的情况下,每个进程在不同的内核上运行,具体取决于机器上的内核数量。因此,在进行多重处理时不需要 GIL,因为所有进程都是独立的。因此,多处理实际上为我们提供了 python 中的实际并行性。但它只有在正确的情况下才会起作用。如果我们有 12 个任务和一个 4 核机器,那么每个核心一个进程将是理想的情况,给出我们想要的真正的并行性。如果我们产生的进程多于内核的数量,每个进程将竞争资源,这将导致上下文切换,从而导致并发性而不是并行性。
Python 中多线程和多处理的基准测试
在这一节中,我将分享我通过两个不同的实验获得的结果,以确定哪种并发执行方法更好,以及适合哪种任务。
大体上,我们将要执行的任务分为涉及 CPU 上大量计算的计算密集型任务或涉及大量 I/O 操作的 I/O 密集型任务,例如在目录之间移动文件或发出大量 HTTP 请求。
I/O 密集型任务
以下是我对 I/O 密集型任务的实现。
这个方法执行程序中的 I/O 操作。我们得到一个图像文件名的列表,并一次一个地将它传递给这个方法。它只是打开文件,执行灰度转换,创建图像的缩略图,并将其保存在已处理的目录中。
我们首先通过简单的串行执行来执行这个方法,然后通过多线程和多处理来执行。
计算密集型任务
以下是我对计算密集型任务的实现。
调用此方法时,计数变量会随着 9⁹.范围内的每个值而递增
同样,我们首先通过简单的串行执行,然后通过多线程,再通过多处理来执行这个方法。对于每个场景,compute _ intensive _ process方法被调用 4 次。
基准测试结果
在对 I/O 密集型操作和计算密集型操作执行上述方法后,获得了以下结果:
正如我们所见,在 I/O 密集型操作中,串行执行的性能最差,其次是多处理,多线程执行的性能次之。尽管我们说过多线程实际上并没有实现真正意义上的并行,但在 I/O 操作的情况下,我们并没有使用 CPU 进行大量计算,而是发出 I/O 请求并等待响应。
为什么多重处理在 I/O 操作的情况下性能很差?
原因是新进程的产生本身就是一个昂贵的操作,因此增加了创建和执行多个任务的开销。但它的性能仍然优于串行执行。
然而,在计算密集型操作的情况下,我们可以清楚地看到,多处理提供了最佳性能,令人惊讶的是,串行执行提供了第二好的性能,而多线程提供了最差的性能。
如前所述,单个进程在单个内核上执行,因此以并发或并行方式执行。
为什么多线程的性能不如串行执行?
这是因为一次只执行一个线程,线程之间的上下文切换会增加开销,从而增加总开销,降低整体性能。例如,如果串行执行需要 100 秒来完成 5 个任务,那么以串行方式执行的多线程需要 100 秒+上下文开销来完成同样的 5 个任务。
结论
正如我们在上面看到的,多线程和多处理在完成特定类型的任务时都有自己的优势。对于涉及大量 I/O 操作的任务,最好使用多线程;对于涉及大量 CPU 计算的任务,最好使用多线程。
我的这个特殊用例的完整项目可以在我的 GitHub 简介这里找到。
[## furqanshahid 85-Python/Python-多线程-Vs-多线程处理
在本模块中,我们将对 python 的多线程和多处理进行基准测试。我们正在进行 I/O 操作和计算…
github.com](https://github.com/furqanshahid85-python/Python-Multi_Threading-Vs-Multi_Processing)
如果你喜欢这篇文章,请在 medium 上关注我,看看我的其他精彩文章。就这些了谢谢:)。
Python Numba 或 NumPy:理解区别
有例子支持的简短描述。
帕特里克·托马索在 Unsplash 上的照片
N umPy 和 Numba 是两个很棒的用于矩阵计算的 Python 包。这两种方法都可以有效地处理多维矩阵。在 Python 中,列表的创建是动态的。向这样的列表追加值将动态地增加矩阵的大小。NumPy 的工作方式不同。它以固定的大小构建数组对象。添加或删除任何元素都意味着在内存中创建一个全新的数组。在本文中,我们正在寻找一种有效的对象结构来解决一个简单的问题。
要讨论的不仅是如何创建数组对象,还有如何对这些数组应用科学操作,尤其是扫描数组。当我们对它们应用一些昂贵的逻辑时,性能是拥有这些库的主要动机。例如,当我们开发机器学习(ML)模型时,尤其是在生产环境中,我们会花费合理的时间来优化生成训练数据的代码,应用任何所需的数据转换或任何其他 ETL 操作。使用一些编译过的编程语言如 C 或 Fortran 是理想的,但是这需要我们到处构建一些包装器来将管道返回到 Python。
在 Python 中实现数组
让我们举一个简单的例子:首先,我们将用 python 创建一个包含一千万个值的简单列表。较大的数字会很容易地突出性能上的差异。
# We need to import the random package to fillup the array with some random values.import random
array = []
我用的是 IPython 如果你是在 Jupyter 笔记本上运行这段代码,那么我推荐使用内置的 magic (time)。
%%time
for i in range(0,10000000):
val = random.randint(0,1000)
array.append(val)
计算频率
让我们在这个列表中搜索有多少行包含值 999?
%%time
freq = 0
for value in array:
if value == 999:
freq = freq + 1
我的机器用了 461 毫秒,这个函数找到了值 999 的 10184 个实例。现在让我们看看如何使用 NumPy 数组完成同样的工作。
import numpy as np# We use the same list created earlier:
np_array = np.array(array)
# To get a sense of the size of the bumpy array, simply call up the function 'shape'
print('The size of the numpy array: ', np_array.shape)
搜索 NumPy 数组中包含值 999 的行只有一行代码:
%%time
result = np.where(np_array == 999)
除了编写一些指令之外,我的机器花了 12.6 ms 来完成与列表数组相同的工作。
请注意,NumPy 数组的索引机制类似于任何普通的 Python 列表。
现在,我们将通过对数组值引入一些数学运算来使这个例子更有趣一些。首先,我们将从原始列表中构造三个向量(X,Y,Z ),然后使用 NumPy 做同样的工作。
def apply_operation_list(array):
# We need to build an X vector from the original array
# The math operations in this function represents some random logic.
x_array = []
for array_v in array:
x_array.append(array_v * 2)
# Building the Y vector
y_array = []
for array_v, x_array_v in zip(array, x_array):
y_array.append(array_v + x_array_v)
# Building the Z vector
z_array = []
for array_v, x_array_v, y_array_v in zip(array, x_array, y_array):
if x_array_v == 0:
z_array.append(0)
else:
z_array.append((array_v - x_array_v ) + y_array_v)
return x_array, y_array, z_array%%time
x_array, y_array, z_array = apply_operation_list(array)
在列表上应用操作花费了 3.01 秒。
让我们用 Numpy 定义同样的函数:
def apply_operation_numpy(array):
# We need to build an X vector from the original array
# The math operations in this function represent some random logic.
x_array = array * 2
# Building the Y vector
y_array = array + x_array
# Building the Z vector
z_array = ( array - x_array ) + y_array
return x_array, y_array, z_array
使用 Numpy,只用了 132 ms。
数字巴
原始照片由 Faris Mohammed 在 Unsplash 上拍摄
Numba 与 Python 配合得非常好,它给了你使用你最喜欢的数学库的特权,但是要编译成本机指令[2]。使用 Numba 很简单,不需要您改变编写函数的方式:
# Loading the Numba package
# Jit tells numba the function we want to compile
from numba import jit
请注意,与上面定义的 Numpy 函数相比,我们需要做的所有更改。
[@jit](http://twitter.com/jit)
def apply_operation_numba(array):
# We need to build an X vector from the original array
# The math operations in this function represents some random logic.
x_array = array * 2
# Building the Y vector
y_array = array + x_array
# Building the Z vector
z_array = ( array - x_array ) + y_array
return x_array, y_array, z_array
使用 Numba,计算三个向量只需要 71.5 毫秒。
选哪个?
“NumPy 是使用 Python 进行科学计算的基础包。其中包含:一个强大的 N 维数组对象、复杂的(广播)函数、集成 C/C++和 Fortran 代码的工具、有用的线性代数、傅立叶变换和随机数功能”[1]
NumPy 是一个巨大的容器,可以压缩向量空间并提供更有效的数组。最显著的优势是那些容器在执行数组操作时的性能。
另一方面,Numba 旨在提供镜像 python 函数的本机代码。Python 可以被看作 Numba API 代码的包装器。根据我的经验,每当已经提供的 Numpy API 不支持我们在向量上执行的操作时,我们就使用 Numba。如果实现的定制函数在我们的环境中不够快,那么 Numba 可以帮助我们在 Python 解释器中生成函数。这也是来自 Numba 文档的建议。
前面提供的例子没有显示出差异有多大?这是真的,因为我们只搜索单个值的频率。让我们通过计算一列中所有值的频率来重复这个实验。
在 Python 中,避免嵌套循环(O^2)的最有效方法是使用函数 count() 。只需一行代码,我们就可以计算整列的频率:
%%time
count = {x:x_array.count(x) for x in x_array}
但是,根据您的处理能力,这个函数可能需要几个小时来完成 1000 万条记录。接下来让我们看看 Numpy 能提供什么:
%%time
x_elements_np, x_counts_np = np.unique(x_array_np, return_counts=True)
使用 Numpy 计算一个百万值列的频率需要 388 毫秒。一个大的性能救济!请注意,该函数仅通过计算不同值的频率来增强。这个例子说明了在大数据环境中使用嵌套循环是多么不现实。
直到最近,Numba 还不支持 np.unique()函数,但如果与 return_counts 一起使用,您仍然不会得到任何好处。这只是为了说明有时 Numpy 可能是最好的选择。
最后,接下来的两个图显示了使用不同数据对象结构的运行时性能。x 轴代表数据大小从 10,000 行到 10 亿行的增量。
下图显示了 Numby with Numba 库的性能。请注意,该数字可能因数据大小而异。图中的数字表示重复实验五次的平均值。
更多示例
计算奇异值分解
频率示例只是一个可能不足以给人留下深刻印象的应用,所以让我们选择 SVD 作为另一个示例。SVD 是一种众所周知的无监督学习算法。它允许我们将一个大矩阵分解成多个小矩阵的乘积。奇异值分解在最大似然法中有许多应用,用于降低维数。这里是一篇推荐文章,供进一步阅读。
下面的例子只使用了两个维度(列),行数与前面的例子相同。
from scipy.linalg import svd# We will consider in this example only two dimensions.
# m x 2
# U: m x r
# s: Diagonal matrix r x r
# VT: n x r
U, s, VT = svd(array_np[:, 0:2])
Sigma = np.zeros((array_np.shape[0], 2))
Sigma[:2, :2] = np.diag(s)
如果您尝试运行该代码,您可能会得到与下面的失败类似的错误:“ValueError:需要太大的工作数组—无法使用标准的 32 位 LAPACK 执行计算。”。这是因为 lapack-lite 的内部实现使用int
作为索引。对于像我们的数组这样的大小,它肯定会导致溢出。我们要么减小向量的大小,要么使用另一种算法。如果 SVD 函数与 Numba 一起使用,我们也不会得到任何明显的好处,因为我们调用的是 LAPACK SVD 函数。
矩阵乘法
矩阵乘法是另一个展示 Numba 如何有助于提高处理时间的例子。即使没有 Cuda,我们也可以实现更好的性能。让我们一步步来看这个例子。正如我们之前所做的,我们将使用 Python list 实现一个函数。
def matrix_multiplication(A,B):
row, col_A = A.shape
col_B = B.shape[1] result = np.zeros((row,col_B)) for i in range(0,row):
for j in range(0,col_B):
for k in range(0,col_A):
result[i,j] += A[i,k]*B[k,j]
return result
对于 1000 万行,列表处理乘法运算相当快。运行时间只有 1 分 7 秒。使用 Numpy,完成同样的工作需要 95 秒。
def matrix_multiplication_numpy(A,B):
result = np.dot(A,B)
return result %%time
result = matrix_multiplication_numpy(array_np, array_np)
现在用 Numba 代替 Numby,我们用一个简单的函数减少了高成本的乘法运算,结果只用了 68 秒,减少了 28%的时间。
[@jit](http://twitter.com/jit)
def matrix_multiplication_numba(A, B, result):
for i in range(result.shape[0]):
for j in range(result.shape[1]):
tmp = 0.
for k in range(A.shape[1]):
tmp += A[i, k] * B[k, j]
result[i, j] = tmp
下图显示了使用 Python 列表、Numby 和 Numba 库的矩阵乘法的性能。
本出版物中提供的示例已在 15 英寸 2018 MacBook Pro 上运行,容量为 16 GB,使用 anaconda 发行版。这些例子中使用的代码可以在 my Github repo 中找到。使用 GPU 环境可以提高性能,但在这次比较中没有考虑到这一点。
参考
NumPy 官方网站,可在线访问 https://numpy.org
Numba 官方网站,可在 http://numba.pydata.org在线访问
使用 Wordbatch Apply 的 Python 单行分布式加速
使用 Wordbatch Apply-decorator 通过分布式 Map-Reduce 处理任何函数
Python 单行分布式加速
并行分布式 Python
由于半导体制造工艺的进步,计算硬件正在经历一个快速发展的时期,64 核消费 CPU 和 80 核服务器 CPU 将于今年上市。在经历了十年的停滞发展后,这给 CPU 性能带来了质的飞跃。
在 Python 程序中利用这种增加的并行计算能力最好是使用多处理库,因为全局解释器锁(GIL)使高效的线程级并行变得复杂。然而,多处理限于单个计算节点,并且设置多处理任务需要为要并行化的每个任务提供额外的样板代码。
最近的分布式 Python 处理框架,如 Ray 、 Dask 和 Spark 支持 Python 进程和数据的分布,很像多处理,但跨越了一个工人网络。Wordbatch 是一个工具包,支持将这些框架用作可交换的分布式后端。它提供了 orchestrator 类批处理程序,该程序将处理任务的管道分布在分布式后端或本地后端,如多处理或韩一菲。最基本的管道是 Apply-class,它接受一个输入函数,并在后端将其作为单个 Map-Reduce 操作运行。
简单的分布式 Map-Reduce 与应用装饰器
Apply 是一个最小的处理管道,它接受一个函数或方法,并将其作为对任何可迭代数据(如列表、Numpy 数组、Pandas 系列或 DataFrame)的 minibatch Map-Reduce 操作来执行。从 Wordbatch 1.4.4 开始,你可以导入一个装饰器包装器来应用,称为 decorator_apply,或者简单地导入为 Apply。Apply 可以用作 Python“@”装饰器,或者在函数上调用它来分发,就像本文中所做的那样。简单来说:
**from** wordbatch.pipelines **import** decorator_apply **as** apply
results = apply(function_or_method)(iterable_data)
这为列表操作产生了一个单行的 Map-Reduce 约定,可以方便地用于整个计算密集型 Python 程序,特别是人工智能和数据科学管道。
上面的例子使用默认的 Python 多处理后端,所有可用的内核都在计算节点上。我们可以通过传递一个已经用后端初始化的批处理程序对象来选择后端,也可以选择传递用于分布式处理的后端句柄。使用 Ray,这将按如下方式完成:
**import** ray
ray.init(redis_address=IP+":"+PORT) # Or ray.init() for local-only
batcher = Batcher(backend="ray", backend_handle=ray)results = apply(function_or_method, batcher)(iterable_data)
通过更改 Batcher 的 backend 和 backend_handle 参数,可以很容易地选择后端。事实上,后端是可交换的,因此只需在处理运行之间更改 batcher.backend 和 batcher.backend_handle 属性就可以更改调度程序。
Apply 最初是作为一个基本的最小管道模板添加到 Wordbatch 的。然而,如熟悉 Map-Reduce 框架的人所知,整个数据处理流水线可以作为大型计算节点网络上的 Map-Reduce 操作来执行。除了这篇博文中展示的特性工程管道,Apply 的其他实际任务包括分发:
- 通过数据/参数子集的人工智能模型训练
- 基于数据/参数子集的人工智能模型推理
- 按输入子集划分的一般计算密集型函数
- 通过超参数配置进行超参数优化
以下代码片段改编自基准测试脚本,该脚本使用不同的后端调度程序运行基准测试。这些例子可以很容易地调整为现实世界的用例。
加速全球和本地功能
作为第一个例子,我们可以将一个全局函数“normalize _ text”分布在一个可迭代的文本字符串上,并将结果作为列“text”存储在数据帧“df”中:
**def** normalize_text(text):
text= **" "**.join([word **for** word **in** re.compile(**'[\W+]'**).sub(**" "**,text.lower()).strip().split() **if** len(word)>1])
**return** textdf[**'text'**] = apply(normalize_text, batcher)(texts)
对全局函数和变量的引用会自动发送到每个新进程。这使得所有必需对象的序列化变得微不足道,但是对于较长的程序,留在全局命名空间中的不必要变量会增加并行化开销。局部函数的用法和全局函数一样,但是标准的 Python 多处理不会序列化局部函数。多重处理的韩一菲分叉和分布式后端将处理本地功能。
Lambda 函数和对象方法
其他类型的函数也同样容易加速,只是有一些小问题。我们可以加速一个λ函数:
df[**'first_word'**] = apply(**lambda** x: x.split(**" "**)[0], batcher)(df[**'text'**])
就像本地函数一样,lambda 函数不是由标准 Python 多处理序列化的。有很多解决方法,比如使用韩一菲,或者只是将 lambda 函数定义为一个全局函数。
一个对象方法可以用同样的方式加速:
**from** nltk.stem.porter **import** PorterStemmer
stemmer= PorterStemmer()df[**'first_word_stemmed'**] = apply(stemmer.stem)(df[**'first_word'**])
使用对象方法时,方法调用不会对原始对象进行更新,因为该方法是在并行副本上执行的。如果需要更改,Apply 的基本用法是不够的。在许多情况下,一种变通方法是使用更复杂的管道,既处理已处理的列表数据,又更新并行化的对象。
熊猫集团运营部
最后,Pandas GroupBy 聚合是一种非常常见的数据处理操作,经常形成人工智能和数据科学处理管道中的瓶颈,例如特征工程任务。这里有几种使用 Apply 的方法,例如:
batcher.minibatch_size = 200
group_ids, groups = zip(*df[[**'first_word_stemmed'**, **'text'**]].groupby(**'first_word_stemmed'**))
res = apply(**lambda** x: x[**'text'**].str.len().agg(**'mean'**), batcher)(groups)
df[**'first_word_stemmed_mean_text_len'**] = df[**'first_word_stemmed'**].map(
{x: y **for** x, y **in** zip(group_ids, res)})
使用 GroupBy 的组 id 将聚集结果映射回原始数据帧。为了清楚起见,操作扩展为三行。Batcher 的 minibatch 大小从默认的 20000 修改为 200,因为与原始 DataFrame 行相比,需要迭代的组数量相对较少。
任何按组并行化的 GroupBy 操作的一个问题是,真实数据的组分布不均匀,通常遵循幂律分布,大多数组只有很少的成员。这通常会抵消并行化带来的任何好处,因为与收益相比,简单的按组操作会导致较大的并行化开销。一个简单的非最优解决方案是通过散列和宁滨来绑定分组变量,以产生一个不太稀疏的分组变量,然后在每个绑定的 GroupBy 内进行原始的 GroupBy 操作,如下所示:
batcher.minibatch_size = 10
df[**'first_word_stemmed_hashbin'**] = [hash(x) % 500 **for** x **in** df[**'first_word_stemmed'**]]
_, groups = zip(*df[[**'first_word_stemmed'**, **'text'**,
**'first_word_stemmed_hashbin'**]].groupby(**'first_word_stemmed_hashbin'**))
res = pd.concat(apply(**lambda** x: x.groupby(**'first_word_stemmed'**).apply(**lambda** z: z[**'text'**].str.len().agg(**'mean'**)), batcher)(groups))
df[**'first_word_stemmed_mean_text_len'**] = df[**'first_word_stemmed'**].map(res)
与未绑定的并行 GroupBy 相比,“first_word_stemmed”变量现在是在每个小批内处理的未绑定变量,而“first_word_stemmed_hashbin”是将数据拆分到分布式小批中的已绑定分组变量。这里,500 个箱用于宁滨变量,批次的小批量大小减少到 10,因为每个小批量现在平均有更多的数据。这些的最佳值取决于数据。
迷你批处理加速选项:缓存和 Numba 矢量化
Apply 还提供了通过缓存和矢量化实现每迷你批次加速的基本选项。Cache 使用所选大小的 functools.lru_cache 来存储每个已处理的 minibatch 中的结果,并且应该在有多个重复值并且每个函数的计算成本都很高时使用。矢量化使用函数的 Numba 矢量化,可以在输入和输出变量类型事先已知的情况下使用。
以词干提取为例,可以简单地添加缓存:
df[**'first_word_stemmed'**] = apply(stemmer.stem, cache=1000)(df[**'first_word'**])
运行加速选项、后端和任务的组合揭示了一些需要解决方法的边缘情况。例如,用 lru_cache 缓存不能直接处理数据帧,因为数据帧是可变的,而 lru_cache 只接受不可变的输入数据。
对于基本情况,支持使用 Numba 加速小批量处理。下面显示了一个具有两个输入变量列的示例:
**def** div(x, y):
**return** 0 **if** y==0 **else** x / ydf[**'len_text'**] = df[**'text'**].str.len().astype(int)
df[**'len_text_normalized'**] = df[**'text_normalized'**].str.len().astype(int)#Without Wordbatch, plain zip comprehension
df[**'len_ratio'**] = [div(x, y) **for** x, y **in** zip(df[**'len_text'**], df[**'len_text_normalized'**])]#With Batcher, no Numba
df[**'len_ratio'**] = apply(**lambda** x:div(*x), batcher)(df[[**'len_text'**,
**'len_text_normalized'**]].values)#With Batcher and Numba vectorization
df[**'len_ratio'**] = apply(div, batcher, vectorize=[float64(int64, int64)])(df[[**'len_text'**, **'len_text_normalized'**]].values)
这里,用于向量化“div”函数的 Numba 变量类型被定义为参数“vectorize=[float64(int64,int 64)”]。Float64 定义输出变量类型,int64s 定义输入变量类型。Batcher 的输入通过从 DataFrame 列“len_text”和“len_text_normalized”中选择二维 Numpy 数组来构造。
Numba 的主要警告是,它仍然不是一个完全开发的框架,对于比这个更复杂的例子,需要很好地理解 Numba 的内部结构。例如,对数组输入进行矢量化需要 Numba guvectorize,而不是矢量化装饰器,并且需要进行一些更改。Numba 可以为数字多维数组操作提供超过原生 Python 代码的显著加速,包括使用 Cuda 的 GPU 加速。但是由于这些设置和扩展都很复杂,一个更有前途的小批量加速方法可能是使用用 C、Cython 或 Rust 等完全低级语言编写的 Python 扩展。
基准
可以将加速比与没有应用的基线函数进行比较,并与批处理程序的不同调度器后端进行比较。下面的数字是在每个后端分别运行基准脚本得到的。测试时间来自使用单个 i9–9900k CPU,具有 8 个内核和 16 个线程,所有内核均以 4.9GHz 运行。所有测试都是在来自 Tripadvisor 点评的超过 128 万行文本数据上进行的,除了 GroupBy 聚合之外,还使用了 5000 个小批量的函数。
第一个示例应用于分发全局文本规范化函数。这为每个被处理的字符串做了相对复杂的处理,所以韩一菲和射线后端的加速几乎是核心线性的,射线的加速是 6.9 倍,韩一菲的加速是 6.6 倍。
继续使用 lambda 函数分割每个 textline 的第一个单词,使用 object 方法对第一个单词进行词干处理,我们获得了较小的加速,但仍然比不使用 Wordbatch 或使用单个进程作为后端要快好几倍。对于分割,韩一菲加速了 1.8 倍,雷加速了 3.2 倍。对于词干,韩一菲是 3.1 倍,雷是 5.14 倍。这两种操作仍然相对复杂,因此尽管存在并行化开销,但还是会有所收获。
通过比较 GroupBy 平均聚合,我们得到了第一个并行化没有直接加速的例子。由于大多数分布式组只有几个成员,并且每个组的平均操作非常简单,因此并行化开销决定了简单并行化 GroupBy 的结果。当使用带有 Ray 后端的 binned GroupBy 时,我们仍然比不使用 Batcher 获得了 1.9 倍的加速。如果在每个入库的 GroupBy 中执行更密集的操作列表,加速将再次接近线性。
当应用于词干分析时,缓存似乎有很大的影响。由于输入是高度重复的,并且每个项目的成本很高,因此缓存在这种情况下是可行的。最好的结果是在没有批处理程序的情况下实现的,其中 1000 个项目的 lru_cache 实现了 0.37 秒,而没有缓存时实现了 10.72 秒。在 Ray 后端的每个迷你批处理中应用缓存,我们得到 0.42 秒。如果缓存非常有用,最好不要使用并行化。一些用例可以同时受益于并行化和迷你批处理内缓存。
最后,当在两列之间分配一个简单的除法函数时,我们可以看看 Numba 矢量化工作得如何。在每个 minibatch 中编译 Numba 函数的成本远远超过了并行化这个简单函数的收益。这里的“no Wordbatch”基线额外使用了列表理解,而不是 Numpy 矢量化数组除法,这样会快很多倍。总的来说,分布式 Numba 矢量化仅对更复杂的数学表达式有用,但这些可能会受到当前 Numba 能力的限制。
总结想法
这篇博文介绍了 Wordbatch 工具包中最近添加的 apply-decorator 的基本用例。这提供了一个简单的一行调用,它接受任何 Python 函数或方法,并基于 Map-Reduce 将它分布在所选的后端,或者在本地,或者分布在诸如 Ray、Dask 或 Spark 之类的调度器上。正确使用可以使许多处理任务的可用内核数量接近线性加速。
充分利用 apply-decorator 需要对并行化的计算成本和并行化的功能有所了解。如果与并行化开销相比,这些方法在计算上非常简单,那么收益很小。如果相反,那么就接近线性加速。
对于现实世界的人工智能管道,最好在最高级别进行并行化,将整个管道功能分布在大型迷你批处理上。这降低了向进程传输单个数据的并行化成本,并且每个大型小批量都可以用完整的流水线来处理。使用 apply-decorator,可以通过在大型组上分配每个数据帧的函数来实现这一点,如 binned GroupBy 示例所示。或者,Wordbatch 有一个相关的类 ApplyBatch 及其 decorator 函数 decorator_apply_batch。这些功能类似于应用,但将函数直接应用于平均分割的批处理,并且不需要像分箱分组那样设置可变宁滨。我的第一篇博文比较了 Python 分布式 AI 后端给出了一个使用 ApplyBatch 的例子。
Python:在线贝叶斯 A/B 测试!
针对 A/B 测试的 Beta 分布、二项式可能性和共轭先验的速成课程
信用:Pixabay
常客背景
如果你和我一样,早在你对数据科学、机器学习等感兴趣之前,你就已经通过社会科学初步接触了统计学。在心理学、社会学等领域,一项研究通常要进行一段时间(可能是几天、几个月甚至几年)。)在新实验的情况下,收集结果,产生平均值和方差的最大似然估计,并构建置信区间。
或者,如果我们假设事情不像现任模型(又名零假设)所描述的那样运行,我们参考现有数据,检查均值、方差和置信区间,通过研究收集数据(从总体中抽取样本),并将样本均值与置信区间进行比较。如果样本均值是极值,我们拒绝零假设,得出结论,“在位模型的均值和方差不准确, 需要更多的研究 。“或者我们只是未能拒绝现任模型的发现。极端是什么意思?这有点主观——这意味着根据现有模型的分布和任意阈值,这一发现是极不可能的。(即,给定分布,我们的样本只有 1%的时间被观察到如此之大(或更大)或如此之小(或更小),我们武断地决定,如果这样的发现有 5%的发生几率(或更小的概率),我们将拒绝现有模型。)
**这是频率主义者的范式。**它非常适合这样的情况:我们有足够的时间来收集足够的数据,以便在根据我们的理解采取行动之前,非常确定我们感兴趣的人群的形状和中心趋势。不幸的是,这种方法不适合资源(时间和数据)短缺的情况,我们需要根据目前已知的信息做出最佳决策。
输入贝叶斯统计!
在我们进入贝叶斯统计之前,让我们回顾一下频率主义范式的一些关键要素。支配分布的参数是固定的,我们只需要收集一个足够大的样本用于我们的估计(样本统计)以收敛于真实的总体参数。假设我们的参数是固有的标量值(固定数值),我们通过置信区间来表达我们对这些值的信心(换句话说,根据当前参数的最大似然估计,一个观察值将有多可能)。)
贝叶斯模型拒绝了参数是固定的这一观点,而是将它们视为分布本身。这使得数学变得非常非常不愉快。然而,它允许我们从一个关于参数*(先验)的信念开始,将这个信念与观察到的数据(似然)本身联系起来,考虑所有其他的可能性(证据),并返回新的更新的信念(后验。换句话说,它是为在线学习而优化的,在线学习的一个广泛应用的例子是 A/B 测试。*
在我们开始编码之前,让我们先谈谈策略——如果你以前谷歌过贝叶斯统计,你可能听说过像马尔可夫链蒙特卡罗(MCMC 方法)这样的术语。发明这些方法是为了从极其难以(如果不是不可能的话)分析整合的分布中取样,并且它们很大程度上以证据为导向。 见下面第 4 节关于贝叶斯法则的论述。
贝叶斯法则总结起来就是(A)选取一个先验分布;这是你对感兴趣的参数的推断前信念。然后(B)选择一种可能性,以先前的信念为条件,模拟数据有多令人惊讶(或不令人惊讶)。最后,©将分子与证据进行比较。从字面上看,这是在参数可能出现的任何值下观察到数据的概率。证据评估为常数值,其 缩放 分子,使得它是有效的概率分布(积分为 1。)
事实上,你的模型越复杂,计算证据就越痛苦。输入**共轭先验。**如果后验(您对参数的更新信念)与前一个具有相同的形状(并且通常很容易计算),那么有一些前验和可能性的组合是“彼此共轭的”!)
在这篇文章中,我们将利用共轭先验,以减少计算的复杂性!一个完美的例子是贝塔先验,二项式似然配对,其中贝塔分布描述了后验分布和。
首先,欧拉贝塔函数 与贝塔分布 不同;现在就把这一点记下来,你会省去很多困惑。见下文第 1 节。事实上,欧拉贝塔函数是贝塔分布的分母(见第 3 节。)其目的是缩放分子,使贝塔分布整合为 1 ( 共同主题… )
二项式分布描述了伯努利试验的总和。θ的 x 次方(有多少次成功)乘以 1-θ的 n-x 次方(n 次试验失败)。)第一项,读作“*n choose x”*是在 n 次试验中总共成功出现 x 次的组合数(不考虑顺序)。对于我们的 A/B 测试,我们可以讨论任何二进制的成功/失败(老虎机转化率与网站总点击率等),这是一个合理的可能性选择。
β-二项式共轭证明第 1 部分
我警告过你证据非常非常糟糕,不是吗?请参见下面的第 4.3 节。值得注意的是,我们的策略依赖于将常数从积分中分解出来,即 n-Choose-x 除以欧拉β函数。在此之后,我们可以很容易地组合类似的项,得到看起来非常类似于另一个欧拉贝塔函数(指数值已经改变,但这将被证明是有益的。)
β-二项式共轭证明第二部分
最后,我们把它结合在一起。是的,这有点可怕。请注意,在等式 12 中,我们将 n-Choose-x 和欧拉β函数从分子中剔除。在步骤 13 中,这些值相互抵消。在等式 14 中,我们看到分母(再次)具有与欧拉β函数相同的形式,因此我们相应地替换它。现在,我们看到分子和分母遵循一个友好的模式——贝塔分布。这太棒了!我们看到,为了计算我们的后验分布,我们只需要四个元素——A、B、X 和 N,其中 A 和 B 是我们的先验信念,X 是成功的次数,N 是试验的总数。整齐!
β-二项式共轭证明第三部分
让我们来探索一下 Beta 发行版有多通用和有用。我鼓励你尝试这个绘图工具。这有助于理解为什么 Beta 分布对影响二元结果的参数建模如此有用。
质量均匀分布
具有相等 a 和 b 值(小于 1)的尾部质量的体积
质量的体积偏向 1,a 值越大
b 值越高,质量越偏向 0
质量体积偏向 0.5,a 和 b 值相等(大于 1)
让我们从一个简单的单臂强盗例子开始编码吧!我们将从统一的先验开始,并逐渐收敛到真实参数(25%的成功几率,对于赌场来说过高。对于那些不知道的人来说,独臂强盗就是一台老虎机。你投入一枚硬币(或指定的费用)拉动手臂,游戏将开始,各种图标旋转图标将对齐(支付!)还是没有(扫兴。)
独臂强盗
独臂强盗贝叶斯更新代码
好的,那么这段代码在做什么呢?首先,我们做一些常规进口。接下来,我们定义一个名为 beta 的类,它接收a
和b
作为参数。我们将这些初始化为 1,每个(又名均匀先验。)然后,我们创建一个列表,其中每个元素基于范围(0,1)中的随机值数组为 1 或 0—如果值低于. 25,则返回 1,否则返回 0。这个列表平均值应该接近 0.25,但是,由于只生成 100 个样本,它可能会有一点变化。接下来,我们定义一个函数,experiment
,它将这个列表分成大小相等的批次;对于每一批,我们找到成功和失败,并相应地更新我们的 Beta 类实例的a
和b
参数。因为我们已经涵盖了上面所有的数学知识,所以这一部分很简单!
考虑下图。顺序如下:蓝色、橙色、绿色和红色。蓝色是我们的均匀先验,我们对θ接收的任何特定值没有偏见。在 orange 中,我们将 25%的数据输入到模型中;因此,根据可能性,我们对我们的制服先验不太有信心(吃角子老虎机不太可能是完全随机的;事实上,它似乎并不经常得到回报。)用橙色绘制的后验分布成为绿色的先验信念。并且该先验信念同样被更新,收敛于参数的真实值。递归地,前一批的后一批成为新批的前一批。请注意,方差在每次迭代中都在缩小(这一点随着峰值变得更加明显而变得明显——它变得更高、更紧。)
基于均匀先验的贝叶斯更新
我们已经研究了数学,并看到贝叶斯统计如何帮助我们迭代地“聚焦”真实参数,但这不完全是 A/B 测试。接下来是什么?
接下来,让我们考虑一下为什么这对 A/B 测试如此有利。注意,在每次迭代中,我们更新了我们的信念,缩小了均值的方差。在我们有多个强盗(恰当地命名为多臂强盗或 MAB)的情况下,将一半资金花在两台机器中的第一台上,然后再探索第二台机器是一个糟糕的策略。我们必须同时作为科学家和商人来思考。这是“探索马得利”的一个很好的继续如果我们只玩一台机器,我们会将(这台机器的)方差缩小很多——但这并没有回答最根本的问题,它是不是最好的机器?同样,如果我们平等地玩所有的机器,我们会对它们的均值和方差有一个很好的了解——但是我们没有利用这些信息。
有几种算法可以回答这个问题;例子有 epsilon greedy,UCB1 等。这些主题很好地介绍了强化学习,但是我们有失去焦点的风险。因此,我们将保持简单;我们将在 固定顺序 中轮换每台机器,并使用与上一次迭代几乎相同的方法更新每个 Beta 类对象的属性。请注意,我做了一些简单的代码修改,例如(A)每个分布都有一个唯一的颜色,以及(alpha(透明度的倒数)从接近 0 开始,并随着每次迭代向 1 增加。我的目标是,你可以(从字面上)看到我们对每台机器的参数的估计是如何集中在它们的实际值上的。
MAB 贝叶斯更新代码
统一先验的 MAB 贝叶斯更新
结论
正如你从上面所看到的,红色的估计值每一轮都朝着它的真实值(0.25)增加,而蓝色和绿色的值都简单地估计它们的平均值高于真实参数值。这表明,更多的数据总是有助于微调我们对数据的理解,利用和探索真的很重要。我以后很可能会在 epsilon-greedy,UCB1 等上面写!我希望这些代码块、(乏味的)证明以及我对直观解释的尝试已经帮助你对 A/B 测试的贝叶斯方法感到更舒服了。
如果有任何你希望我在未来写的特定主题,请评论!
感谢您的阅读——如果您认为我的内容还可以,请订阅!😃
Python OpenCV:构建类似 Instagram 的图像过滤器
使用图像变换技术构建图像过滤器的 Python OpenCV 教程。
OpenCV 是为解决大量计算机视觉任务而构建的库。它包含了大量的基本和高级特性,非常容易掌握,并且可用于多种编程语言。
在这篇文章中,我们将应用一些基本的图像变换技术,以获得图像过滤器。对于那些习惯了所有图像编辑软件的人来说,这些滤镜可能对你来说很基本。但是我认为对于第一次接触 OpenCV 的人来说,它们是很棒的,因为它们允许我们在不编写大量代码的情况下学习一些基本原理。😀
本文原载于 程序员背包博客 。如果你想阅读更多这类的故事,一定要访问这个博客。
更感兴趣?在 Twitter 上关注我,地址是@ b _ dmarius,我会在那里发布每一篇新文章。
介绍
在这一节中,我将解释计算机如何存储和处理 RGB 图像背后的逻辑。如果你已经熟悉这些东西,请跳到下一节,我们将跳到更高级的细节。
图像中的基本单位数据是像素。像素只是图像中的一个点,并存储为范围在[0,256]内的数字。RGB 模型代表红绿蓝并告诉我们,对于每个像素,我们存储红、绿、蓝的强度(我们将这些通道称为)作为一个从 0 到 256 的数字。如果所有 3 个通道都是 256,则像素为白色;如果所有 3 个通道都是 0,则像素为黑色。如果相应的通道是 256 并且所有其他通道是 0,则像素是完全红色/绿色/蓝色的。你明白了,每种颜色都是由这三个通道混合而成的。
所以图像是像素的集合。比方说,如果我们的图像是 300x200,那么我们将把它存储为 300 行 200 列的 2D 数组,其中每个单元是一个像素。但是我们从上面知道,对于每个像素,我们将存储所有 3 个通道的信息,所以这实际上给了我们一个 3D 数组。
OpenCV 卷积和内核
这是完全由理论组成的最后一部分。如果你想直接跳到实用部分,请见下一节。
现在我们知道了图像是如何作为像素存储的,我们可以学习对这些像素应用变换。
卷积是通过将图像分割成称为窗口的小部分,并对每个部分应用称为内核的操作符来变换图像的操作。
内核通常是一个固定的、包含数字的小型 2D 数组。内核中的一个重要部分是中心,称为锚点。
OpenCV 过滤器—内核示例
卷积通过以下步骤执行:
- 将内核放置在图像的顶部,内核锚点位于预定像素的顶部。
- 在内核数和被内核重叠的像素值之间执行乘法,对乘法结果求和,并将结果放在锚点下方的像素上。
- 重复这个过程,在图像上的每个可能的位置滑动图像顶部的内核。
如果你想知道如何选择核的值,请注意最流行的核是图像处理科学家大量研究的结果。你当然可以试着选择你自己的内核,但是对于最基本的转换,我们已经有了很好的内核,可以提供很好的结果。
项目设置
我们需要安装 2 个 python 包,然后就可以开始了。
pip3 install opencv-python
pip3 install scipy
我们将导入单个图像(这是我亲自拍摄的图像),并且对于每个转换,我们将制作该图像的副本。然后,我们通过一个单独的方法应用转换,这样我们可以保持代码的整洁。最后,我们将把结果保存为一个单独的图像。下面是基本流程:
initialImage = cv2.imread("image1.jpg")
blurredImage = gaussianBlur(copy.deepcopy(initialImage))
cv2.imwrite("blurred.jpg", blurredImage)
OpenCV 内核和卷积变换
在这一部分,我们将使用一组预定义的内核来应用卷积,以获得漂亮的效果。对于其他类型的转换,请跳到下一节。对于每一个转换,我将向你们展示内核和代码,在这一节的最后,我将展示一个画廊,展示初始图像和结果。
图像锐化
Python OpenCV 滤镜-图像锐化
这是用来锐化图片细节的内核。我们将使用 OpenCV 库中的 filter2D 方法,它将为我们执行卷积。
def sharpen(image):
kernel = np.array([[-1, -1, -1], [-1, 9, -1], [-1, -1, -1]])
return cv2.filter2D(image, -1, kernel)
棕褐色效果
Python OpenCV 滤镜—棕褐色
def sepia(image):
kernel = np.array([[0.272, 0.534, 0.131],
[0.349, 0.686, 0.168],
[0.393, 0.769, 0.189]])
return cv2.filter2D(image, -1, kernel)
模糊效应
为了达到这个效果,我们可以使用一个基本的内核,就像上面所有的一样,但是结果是相当蹩脚的。幸运的是,OpenCV 实现了高斯模糊,它将为我们完成这项工作。我们需要做的就是:
def gaussianBlur(image):
return cv2.GaussianBlur(image, (35, 35), 0)
浮雕效果
Python OpenCV 滤镜-浮雕
def emboss(image):
kernel = np.array([[0,-1,-1],
[1,0,-1],
[1,1,0]])
return cv2.filter2D(image, -1, kernel)
这是我们内核转换结果的图库。
这是最初的图像
从左到右,从上到下:模糊,浮雕,棕褐色,锐化
OpenCV —基于像素值的变换
接下来,我们将增加图像的亮度。为此,我们需要做的就是导航到图像的每个像素,然后导航到特定像素的每个通道。然后,我们将每个通道的值增加一个特定的值。这将给我们一个很好的亮度效果。
为了将我们从所有这些代码中解放出来,OpenCV 框架实现了一个方法,可以完全做到这一点。
def brightnessControl(image, level):
return cv2.convertScaleAbs(image, beta=level)
这是我们亮度控制方法的结果。
左—明亮的图像,右—原始图像
OpenCV —查找表转换
我们将使用这种类型的变换来使图像看起来更温暖和更寒冷,但首先让我们看看我们如何使用查找表。
一个查找表只是一个简单的值对集合,如下所示:【值 1,值 2,…。,valueN],[modifiedValue1,modifiedValue2,…,modifiedValueN] 这背后的逻辑很简单——我们将获取每个像素值,并用查找表中的相应值替换它(意味着用 modifiedValue1 等替换 value1 )。
现在的问题是,有很多值需要替换(从 0 到 256),为此创建一个查找表是一个痛苦的过程。但是幸运再次站在我们这边,因为我们有一个工具可以利用它。
来自 scipy 包的 单变量线 平滑方法可以帮助我们。这种方法只取几个参考值,并试图找到一种方法来修改我们提供的参考值范围内的所有其他值。
基本上,我们将定义一个简短的查找表,如下所示
【0,64,128,256】,【0,80,160,256】
并应用 UnivariateSpline 变换,该变换将填充[0,256]范围内的其余值。
构建好查找表后,我们只需将它们应用于特定的通道。为了获得一个温暖的图像,我们将为图像中的所有像素增加红色通道的值,减少蓝色通道的值。为了获得冷图像,我们要做相反的事情:增加蓝色通道的值,减少红色通道的值。绿色通道在这两种情况下都不会被触及。
def spreadLookupTable(x, y):
spline = UnivariateSpline(x, y)
return spline(range(256))def warmImage(image):
increaseLookupTable = spreadLookupTable([0, 64, 128, 256], [0, 80, 160, 256])
decreaseLookupTable = spreadLookupTable([0, 64, 128, 256], [0, 50, 100, 256])
red_channel, green_channel, blue_channel = cv2.split(image)
red_channel = cv2.LUT(red_channel, increaseLookupTable).astype(np.uint8)
blue_channel = cv2.LUT(blue_channel, decreaseLookupTable).astype(np.uint8)
return cv2.merge((red_channel, green_channel, blue_channel))def coldImage(image):
increaseLookupTable = spreadLookupTable([0, 64, 128, 256], [0, 80, 160, 256])
decreaseLookupTable = spreadLookupTable([0, 64, 128, 256], [0, 50, 100, 256])
red_channel, green_channel, blue_channel = cv2.split(image)
red_channel = cv2.LUT(red_channel, decreaseLookupTable).astype(np.uint8)
blue_channel = cv2.LUT(blue_channel, increaseLookupTable).astype(np.uint8)
return cv2.merge((red_channel, green_channel, blue_channel))
这里的结果和预期的一样。
左侧—暖图像,中间—原始图像,右侧—冷图像
这很有趣!我们在这里看到了一点数学,一点代码,学到了很多关于计算机如何存储和处理图像,以及我们如何使用它来获得图像的美丽变换。如果你喜欢这篇文章,并想了解更多,那么请确保你在 Twitter 上关注我,因为不久我将发布另一篇关于使用 OpenCV 构建类似 Instagram 的面具的文章。
本文原载于 程序员背包博客 。如果你想阅读更多这类的故事,一定要访问这个博客。
非常感谢您阅读本文!有兴趣了解更多吗?在 Twitter 上关注我,地址是@ b _ dmarius,我会在那里发布每一篇新文章。
Python 操作者从零开始!!!—初学者指南
在本教程中,您将学习 python 中的一个基本且重要的概念——运算符,以及它们的语法和示例。
来源:oddsockvideogames,viaapk pure(CCO)
什么是运营商?
运算符是一个字符或字符集,可以用来对操作数执行期望操作并产生最终结果。
最终结果完全取决于所使用的运算符类型。
例如,假设您想要执行 5+3 的数学计算。现在 5 和 3 都称为操作数, + 是执行加法的运算符,最终结果是 8 。
只是提醒一下,本教程的完整代码可以在下面我的 GitHub 资源库 中找到:
permalink dissolve GitHub 是超过 5000 万开发人员的家园,他们一起工作来托管和审查代码,管理…
github.com](https://github.com/Tanu-N-Prabhu/Python/blob/master/Python_Operators.ipynb)
运算符的类型
塔努·南达·帕布拍摄的照片
Python 支持的不同类型的运算符如下:
- 算术运算符
- 关系运算符
- 逻辑运算符
- 按位运算符
- 赋值运算符
- 身份运算符
- 隶属操作符
算术运算符
算术运算符用于执行数学运算,如加、减、乘、除、模、底除和指数。
塔努·南达·帕布拍摄的照片
例子
下面是一个简单的 python 代码片段,可以作为参考:
**# Assigning values to variables** a = 10
b = 5**# Addition** print('a + b =', a + b)**# Subtraction**
print('a - b =', a - b)**# Multiplication**
print('a * b =', a * b)**# Division**
print('a / b =', a / b)**# Modulus**
print('a % b =', a % b)**# Floor Division**
print('a // b =', a // b)**# Exponent**
print('a ** b =', a ** b)
当您运行上述 python 脚本时,您将得到以下输出提示
a **+** b = **15**
a **-** b = **5**
a ***** b = **50**
a **/** b = **2.0**
a **%** b = **0**
a **//** b = **2**
a ****** b = **100000**
重要注意事项
- 如果你用任何一个数字除以 0 ,你会得到一个错误提示
**ZeroDivisionError**
。不要用零(0)除任何东西。 - 除法运算符生成的结果将始终是浮点数(用小数点表示)。
- 底数除法返回去掉小数点后位数的商(答案或除法的结果)。但是,如果操作数(被除数和除数)之一是负的,则结果是 floored,即,远离零舍入(意味着,朝向无穷大的负数)。
关系运算符
顾名思义,关系运算符或比较运算符用于比较值。这些运算符的返回类型不是**True**
就是**False**
。不同的比较运算符是大于、大于或等于、小于、小于或等于、等于和不等于。
塔努·南达·帕布拍摄的照片
例子
下面是一个简单的 python 代码片段,可以作为参考:
**# Assigning values to variables** a = 10
b = 5**# Greater than** print('a > b =', a > b)**# Lesser than** print('a < b =', a < b)**# Equal to** print('a == b =', a == b)**# Not equal to** print('a != b =', a != b)**# Greater than or equal to** print('a >= b =', a >= b)**# Lesser than or equal to** print('a <= b =', a <= b)
当您运行上述 python 脚本时,您将得到以下输出提示
a **>** b = **True**
a **<** b = **False**
a **==** b = **False**
a **!=** b = **True**
a **>=** b = **True**
a **<**= b = **False**
重要注意事项
- 关系运算符也称为比较运算符。
- 比较运算符可用于比较两个以上的值。例如
5 **>** 3 **<** 1
将导致**False**
。 - 这些也被称为关系运算符,因为它比较值,然后决定它们之间的关系。比如
5 **>** 4
关系是这样的 5 大于 4 答案是**True**
。这里的关系是大于。
逻辑运算符
逻辑运算符用于评估操作数之间的条件。不同类型的操作员有**and**
、**or**
和**not**
。
塔努·南达·帕布拍摄的照片
为了使事情更清楚,您可以参考下面给出的逻辑运算符的真值表:
例子
下面是一个简单的 python 代码片段,可以作为参考:
**# Assigning values to variable** a = True
b = False**# Logical and** print('a and b is',a and b)**# Logical or** print('a or b is',a or b)**# Logical not** print('not a is',not a)
当您运行上述 python 脚本时,您将得到以下输出提示
a **and** b is **False**
a **or** b is **True**
**not** a is **False**
重要注意事项
- 逻辑运算符也被称为布尔运算符。
- 如果操作数不是布尔值,那么它将自动转换为布尔值进行计算。
- 逻辑运算符可以应用于任何类型的值。例如,它们可以应用于如下所示的琴弦。在这种情况下,和运算符返回第一个假值,如果有空值或假值,则返回最后一个值。或返回第一个真值,否则返回最后一个值。
字符串上的逻辑“与”运算符
a = ""
b = "Python"
a **and** b**''**
字符串上的逻辑“或”运算符
a = ""
b = "Python"
a **or** b'**Python**'
- 在
**and**
、**or**
两种情况下,评估都是从左到右进行的。
按位运算符
按位运算符在二进制级别上对操作数进行运算。这意味着按位运算符直接查看整数的二进制数字或二进制位。因此得名按位(逐位操作)。不同类型的按位运算符有按位与、或、非、异或、右移和左移。
塔努·南达·帕布拍摄的照片
例子
下面是一个简单的 python 代码片段,可以作为参考:
**# Assigning values to variables** a = 10
b = 11**# Bitwise AND** print('a & b is',a & b)**# Bitwise OR** print('a | b is',a | b)**# Bitwise XOR** print('a ^ b is',a ^ b)**# Bitwise NOT** print('~a is',~a)**# Bitwise Left Shift** print('a << b is',a << b)**# Bitwise Right Shift** print('a >> b is',a >> b)
当您运行上述 python 脚本时,您将得到以下输出提示
a **&** b is **10**
a **|** b is **11**
a **^** b is **1**
**~**a is **-11**
a **<<** b is **20480**
a **>>** b is **0**
重要注意事项
- 逐位运算符对位进行运算,并对操作数执行逐位运算。
- 无论传递的操作数是什么类型,按位运算符都会分别将其转换为一系列二进制数字。例如,如果一个操作数是 2 ,那么它的二进制格式就是 10 ,同样, 9 将被渲染为 1001 等等。下面是不包括左移和右移运算符的位运算符的真值表。
塔努·南达·帕布拍摄的照片
赋值运算符
顾名思义,赋值操作符用来给变量赋值。让我给你举个简单的例子。
a = 5
很多时候,人们在阅读上面一行代码时会犯错误。人们说“ a 等于 5 ”,这听起来可能是正确的,但从程序上来说是不正确的。正确的做法是:
值 5 被分配给变量‘a’
因为赋值操作符的工作方式是把右边的值赋给左边的变量。所以记住它的从右到左。
塔努·南达·帕布拍摄的照片
例子
下面是一个简单的 python 代码片段,可以作为参考:
**# Assigning the values to variables** a = 15
b = 5**# Simple assignment operator** b = a
print('b = a: ',b)**# ADD AND operator** b += a
print('b += a: ', b)**# SUBTRACT AND operatpr** b -= a
print('b -= a: ', b)**# MULTIPLICATION AND operator** b *= a
print('b *= a: ', b)**# DIVISION AND operator** b /= a
print('b /= a: ', b)**# FLOOR AND operator** b //= a
print('b //= a: ', b)**# MODULUS AND operator** b %= a
print('b %= a: ', b)**# EXPONENT AND operator** b **= a
print('b **= a: ', b)**# LESS THAN AND operator** b <= a
print('b <= a: ', b)**# GREATOR THAN AND operator** b >= a
print('b >= a: ', b)**# BINARY AND operator** a &= 5
print('a &= 5: ', a)**# BINARY OR operator** a |= 5
print('a |= 5: ', a)
当您运行上述 python 脚本时,您将得到以下输出提示
b **=** a: **15**
b **+=** a: **30**
b **-=** a: **15**
b ***=** a: **225**
b **/=** a: **15.0**
b **//=** a: **1.0**
b **%=** a: **1.0**
b ****=** a: **1.0**
b **<=** a: **1.0**
b **>=** a: **1.0**
a **&=** 5: **5**
a **|=** 5: **5**
笔记
- 我们可以为更多的操作符扩展赋值操作符,比如
**-, /, *, //, %, <<, >>, &, |, **, ^**
。例如:**a **= 5**
,将是**a = a**5**
,答案将是**298023223876953125**
。确保您编写的操作符后跟赋值操作符。
特殊操作员
python 编程语言中有两种特殊运算符,如下所示:
标识运算符
顾名思义,identity 操作符比较两个或更多 python 对象的 id (identity) ,比如变量、值等等。换句话说,别人说同样可以用恒等运算符比较两个对象的内存位置。有两种类型的恒等运算符,即**is**
和**is not**
。
塔努·南达·帕布拍摄的照片
例子
下面是一个简单的 python 代码片段,可以作为参考:
**# Assigning values to variables** a = 10
b = 11**# Identity is operator** print('a is b is',a is b)**# Identity is not operator**
print('a is not b is',a is not b)
当您运行上述 python 脚本时,您将得到以下输出提示
a **is** b is **False**
a **is not** b is **True**
重要注意事项
- 通常,标识运算符不比较值或对象本身。相反,它会比较 id(身份)。下面是一个例子:
**# Assigning the values to variables** a = 5
b = 5
c = a**# Getting the id of the variables** print(id(a))
print(id(b))
print(id(c))**# Comparing the id of a and c** print(a is c)
要比较 id,您可以使用 python 中的**id**
函数。它返回内存位置的 id 。
id of a is: **10914624**
id of b is: **10914624**
id of c is: **10914624**
**True**
成员运算符
成员运算符用于验证特定元素是否是序列的部分。现在一个序列可以是一个列表、字符串、集合、字典和元组**。这两个成员操作符是**in**
和**not in**
。**
塔努·南达·帕布拍摄的照片
例子
下面是一个简单的 python 代码片段,可以作为参考:
**# Assigning a string value to a variable** a = "Python"**# Type of the variable** print(**type**(a))**# Checking whether 'y' is present in the variable a or not** print('y' **in** a)**# Checking whether 'P' is present in the variable a or not** print('p' **not in** a)
当您运行上述 python 脚本时,您将得到以下输出提示
<class '**str**'>
**True
True**
重要注意事项
- 虽然我们可以在字典上使用成员运算符,但是有一件事你应该知道,即我们只能测试键的存在,而不能测试值的存在,如下所示:
**# Dictionary with key as 1, 2 and values as 'A' and 'B'** a = {1: "A", 2: 'B'}**# Using 'in' operator** print(2 **in** a)**# Using 'not in' operator** print(3 **not in** a)
因此,上面的输出将是:
**True**
**True**
干得好,干得好伙计们,你们已经到达教程“ Python 操作者从头开始”的结尾了!!!—初学者指南”。我希望读完这篇教程后,你对 python 操作符有了更深的了解。如果你对教程有任何疑问,请通过下面的评论区告诉我。我试着尽快回答。好的,下次见,祝你愉快,注意安全。
Python 包焦点:Faker
为任何情况生成测试数据的简单方法。
马库斯·斯皮斯克在 Unsplash 上的照片
有如此多的 Python 包,对于正在学习这门语言的人来说,知道有哪些工具可供您使用可能会让人不知所措。我希望发现一些鲜为人知但功能强大且有用的包,以帮助您踏上 Python 之旅。
那么,Faker 是什么?
根据他们的文档,Faker 是一个‘为你生成虚假数据的 Python 包’。“无论你是需要引导数据库、创建好看的 XML 文档、填充持久性以对其进行压力测试,还是匿名化来自生产服务的数据,Faker 都适合你。”
这立即吸引了我,因为它对学生、寻求建立文件夹/项目工作的专业人员,或者任何想要修补各种其他库但缺乏适当数据集的人来说都有很大的价值。在我自己的工作中,我经常想建立某种仪表板,但需要确保我使用的数据可以安全地共享。Faker 替我处理这些,因为我可以快速生成项目所需的几乎任何数据集。
安装和入门
这就像用 pip 安装一样简单:
pip install Faker
在那里,我们创建了一个 Faker“生成器”,我们将对生成的数据进行多次调用:
from faker import Faker
fake = Faker()
Faker 有许多所谓的内置提供者——从伪造的条形码、公司名称、电话号码、文件名等等。标准提供商的完整列表可在这里找到。利用它们非常简单!例如,假设我们正在构建一个测试数据库,并想要一个公司名称及其口号的列表。为此,我们将运行以下代码:
如果我们想要一些公司人员的数据——姓名、地址、职位等,会怎么样?使用 fake.profile()可以生成一个字典配置文件:
这是一个相当广泛的列表,但是如果您想要构建一些更具体的东西呢?您可以从标准提供商那里构建自己的配置文件:
播种
如果您正在进行测试,那么在您的生成器上设置一个种子来从相同的数据集进行提取可能会有所帮助。
这和使用 seed()方法一样简单:
from faker import Faker
fake = Faker()
Faker.seed(1234)
本地化
Faker 的另一个非常酷的特性是能够改变生成的数据集的本地化。默认情况下,它是英语美国,但有一大堆其他选项。例如,如果我们想生成一堆假的瑞典名字会怎么样?没问题!
当您创建 Faker 生成器时,只需将 locale 作为参数传递就可以了。通过向生成器传递一个区域设置列表,您甚至可以使用多个区域设置:
在上面的示例中,每个名称都是从 3 个区域列表中随机选择的。但是,您可以使用权重来创建符合您需求的分布。
总的来说,Faker 是一个非常通用和实用的库,我个人认为它有巨大的使用价值。
我鼓励您访问他们的文档以查看所有可用的可能提供商,因为这只是一个简短的概述。
AWS Lambda 中的 Python 包变得简单
创建自己的 Lambda 层的简单指南,这样你就可以在 Lambda 函数中使用任何 Python 包
AWS Lambda 是一个多功能的无服务器工具。有了 Lambda,你几乎可以运行任何你喜欢的东西——只需编写代码并上传到 Lambda。您每使用 100 毫秒就要付费,因此您只需为消耗的计算时间付费。
Lambda 支持 Python,如果你有使用它的经验,这是一个很好的选择。然而,Lambda 的一个缺点是默认情况下你不能导入你信任的包,比如熊猫。
我将向您展示导入 Lambda 函数所需的任何包的最简单方法。我将以熊猫为例,但是同样的方法也可以用于其他的包。
步骤 1:在 AWS 中启动一个 Cloud9 Linux 实例
- 在 AWS 服务中搜索 Cloud9
- 点击“创建环境”
- 将您的环境命名为您喜欢的名称(例如 python_package_test ),然后单击下一步
- 保持环境默认设置(为环境创建一个新的 EC2 实例;t2.micro 亚马逊 Linux 等…)并点击下一步
- 点击“创建环境”,您就可以开始了
步骤 2:创建你的熊猫 Lambda 层
- 在底部的终端中逐行键入以下代码。pip install pandas 命令可以替换为您选择的软件包。您也可以安装多个软件包*。
mkdir folder
cd folder
virtualenv v-env
source ./v-env/bin/activate
pip install pandas
deactivate
- 然后逐行输入以下代码来创建图层
mkdir python
cd python
cp -r ../v-env/lib64/python3.7/site-packages/* .
cd ..
zip -r panda_layer.zip python
aws lambda publish-layer-version --layer-name pandas --zip-file fileb://panda_layer.zip --compatible-runtimes python3.7
步骤 3:添加熊猫层到 Lamda 函数中
- 转到 AWS Lambda 服务并单击“创建函数”
- 命名您的函数,将运行时设置为“Python 3.6”,然后单击“创建函数”
- 点击功能设计器中的“层”,然后点击“添加层”
- 在名称下拉列表中,你应该看到你的熊猫层。点击“添加”。
- 让我们用下面的脚本来测试这一点。注意,我也可以导入 numpy,因为它是熊猫包的依赖项:
import numpy as np
import pandas as pddef lambda_handler(event, context):
df2 = pd.DataFrame(np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]),columns=["a", "b", "c"])
number = np.pi
print(df2)
print(number)
**成功了!**我们可以看到熊猫数据帧输出和 pi 的数字,证明 numpy 和熊猫已经成功导入。
*限制:一个 Lambda 函数一次最多可以使用 5 层,但是,该函数和所有层的总解压缩大小不能超过 250 MB。
Python: Pagerank 遇上体育分析
重新利用一万亿美元的运动队排名算法
信用:Pixabay
前几天我发表了一篇关于使用蒙特卡罗模拟来收敛彩票概率分布的文章。名义上,它比我以前的一些文章更快地获得了关注。这可能意味着(A)读者对蒙特卡洛方法非常感兴趣,或者(B)读者对体育分析非常感兴趣。我敢打赌,体育分析是这里的主导因素,这篇文章将作为一个实验!(我很 meta,我知道。)
概观
这完全是一个“端到端”的项目,首先通过 SQLite 访问数据库数据,通过 Pandas 进行数据清理/管理,通过 NetworkX 进行网络建模,最后将我们的结果与官方的“种子”进行比较。
但是我想得太多了。我们的数据来自 NCAA(美国全国大学生体育协会)2019 年男子篮球常规(和后)赛季(s),仅限于 ACC(大西洋海岸会议)。我们的目标是挖掘输赢模式的数据,根据赢得联盟冠军的可能性对球队进行排名。
最后,让我们讨论一下“万亿美元算法。“22 年前,谷歌在加州门洛帕克成立,为庞大的信息网络带来了秩序和相关性。这个算法 Pagerank ,是对一个已建立的网络度量——特征向量中心性的改编。该算法期望接收一个转换矩阵作为参数;这个转移矩阵量化了网页之间的链接。当一个页面链接到另一个页面时,它与另一个页面共享部分可信度。很大程度上,共享的数量取决于(首先讨论的)节点有多少个出站链接。迭代执行该算法,直到特征向量收敛。这个特征向量就是page rank;换句话说,它包含了每个页面相对于其他页面的排名。Pagerank 已经被应用到学术期刊的网络中,在那里一篇非常受欢迎的文章链接到一篇祖父文章或开创性的文章。高人气的文章与开创性的文章分享了它的大部分可信度。由此可以得出结论,尽管许多文章没有引用这篇开创性的文章,但它们通过这篇高人气文章间接受到了这篇文章的影响。这才是 pagerank 真正的美好和强大。
我们需要对数据进行预处理,以便 Pagerank 可以将其作为参数接收。考虑两个团队,一个几乎整个赛季都没有输过,另一个几乎整个赛季都输了——但是——失败的团队碰巧打败了胜利的团队。在体育运动中,这通常被称为冷门。并且 Pagerank 将能够提取该事件的意义和全球背景。我们只需要一种有效的方法将输赢模式编码成一个矩阵。没有绝对正确或错误的答案,但有些方法比其他方法更好;我鼓励你集思广益,找出改进这种方法的方法,亲自实施,并将结果与 NCAA ACC 官方种子进行比较。
方法
在我的方法中,我迭代了每一场常规赛,并找到了分差。比如甲队赢乙队 20 分;ergo B 队给20 点可信度给a 队图可以是有向的,也可以是无向的。在我们的例子中,我们希望 引导 节点(团队)之间的边(关系),这样就有了一个单向关系。B 队给 A 队 20 分,不是 A 队给 B 队 20 分。网络可以在两个给定节点之间有多条边(多重图),也可以在两个给定节点之间有一条边(简单图)。)我已经简化了网络,这样,在两个给定的队相互比赛两次的情况下,同一个队赢了两次,连接他们的边将是他们获胜的平均值。
我将让你来决定这个方法是否可以改进。例如,如果 A 队以 90–80 击败 B 队,则差值为 10 分。然而,如果 A 队以 20 比 10 击败 B 队,那么差距仍然是 10 分。我们应该对此做些什么吗?我将让读者来决定、实现和评估:)
密码
存储库可以在这里找到,数据库等等。
但是为了简单起见,我也做了一个 GitHub 要点:
我的结果是:
NCAA ACC 常规赛排名
我们和官方种子相比如何?
鸣谢:维基百科
总的来说,我们做得不错!我们的结果和 NCAA 官方的种子不一样;这是由于(当然)他们使用了完全不同的算法。值得注意的是,我们将杜克大学列为第二名,而 NCAA 将他们列为第三名——最终杜克大学赢得了联盟冠军(万岁)。然而,我们种子选手佛罗里达州立大学比我们低得多,他们进入了最后一轮。
我想知道我们能做些什么来改善我们的结果!还记得我们讨论过 90–80 和 20–10 都反映了 10 的增量吗?也许答案就在那里。我鼓励你探索这种可能性,并分享你的成果。
感谢阅读!如果你认为我的内容没问题,请订阅:)
Python 熊猫和 SQLite
使用 SQLite 存储您的熊猫数据帧为您提供了一个持久存储,并提供了一种轻松选择和过滤数据的方法
马库斯·温克勒在 Unsplash 上的照片
SQLite 数据库是 Python 的内置特性,而且非常有用。它不是 SQL 的完整实现,但是它具有个人数据库甚至数据驱动网站后端所需的所有特性。
和熊猫一起用很简单,真的很有用。您可以将数据帧永久存储在一个表中,并在需要时将它们直接读入一个新的数据帧。
但不仅仅是存储方面如此有用。您可以使用简单的 SQL 命令来选择和过滤数据:这样您就不必处理数据帧本身。
我将使用 SQLite 演示一些简单的技术,并使用我最喜欢的伦敦天气数据集演示熊猫。它来自英国气象局的公共领域数据,你可以从我的 Github 账户下载。
这里的所有代码都是用 Jupyter 笔记本写的,但是作为一个独立的 Python 程序也应该运行得很好。
让我们从导入库开始:
import sqlite3 as sql
import pandas as pd
import matplotlib.pyplot as plt
显然,我们需要 SQLite 和 Pandas 库,我们还会得到 matplotlib,因为我们要绘制一些图形。
现在我们来获取数据。在一个 csv 文件中有大约 70 年的温度、降雨量和日照数据。我们是这样下载的:
weather = pd.read_csv('https://github.com/alanjones2/dataviz/raw/master/londonweather.csv')
这是它看起来的样子。记录一年中每个月的数据。气温以摄氏度为单位,降雨量以毫米为单位,“太阳”是一个月的总日照时数。
现在我们要将数据帧保存在 SQLite 数据库中。
首先,我们打开一个到新数据库的连接(如果数据库不存在的话,这将创建一个数据库),然后在数据库中创建一个名为天气的新表。
conn = sql.connect('weather.db')
weather.to_sql('weather', conn)
除非原始数据发生变化,否则我们不需要再次运行这段代码,事实上,我们不应该这样做,因为如果已经存在同名的表,SQLIte 将不允许我们在数据库中创建新表。
让我们假设我们已经运行了上面的代码一次,并且我们有我们的数据库,天气,和表,也被称为天气。我们现在可以启动一个新的笔记本或 Python 程序来完成本教程的其余部分,或者简单地注释掉代码来下载和创建数据库。
所以,我们有一个数据库,里面有我们的天气数据,现在我们想把它读入一个数据帧。下面是我们如何将数据库表加载到数据帧中。
首先,我们像以前一样连接到数据库。然后我们使用 Pandas 的 sql_read 方法读入数据。但是要做到这一点,我们必须发送
对数据库的查询:
SELECT * FROM weather
这是你能做的最简单的 SQL 查询。这意味着从名为 weather 的表中选择所有列并返回数据。下面是代码(查询作为字符串传递)。
conn = sql.connect('weather.db')
weather = pd.read_sql('SELECT * FROM weather', conn)
当然,这只是复制原始数据帧,所以让我们使用 SQL 的强大功能来加载数据的一个子集。
我们将获得相隔 50 年的几年的数据,并比较它们,看看是否有任何明显的差异。
让我们从获取 2010 年的数据开始。
我们将像以前一样使用 read_sql 创建一个名为 y2010 的新数据帧,但是查询略有不同。我们添加一个 WHERE 子句。这只选择关键字 where 后的条件为真的数据。因此,在这种情况下,我们只获得 year 列中的值为“2010”的数据行。
y2010 = pd.read_sql('SELECT * FROM weather WHERE Year == 2010', conn)
您可以看到,唯一返回的数据是 2010 年的。
这是 Tmax 的线图。
现在让我们再来一次,但是是 50 年前的 1960 年
y1960 = pd.read_sql('SELECT * FROM weather WHERE Year == 1960', conn)
现在让我们画出这两年的 Tmax 。
ax2010 = y2010.plot(y='Tmax')
ax = y1960.plot(y='Tmax',color = 'red', ax=ax2010)
ax.legend(['2010','1960'])
有趣的是,2010 年似乎比 1960 年更热也更冷。天气越来越极端了吗?我们需要做更多的分析来得出这个结论。
也许我们可以从找出最热的年份开始,比如温度超过 25 度的年份。
我们通过使用带有条件的 WHERE 子句来实现这一点, Tmax > 25 ,即选择 Tmax 大于 25 度的行
但是,由于我们只对最高温度感兴趣,我们将只选择我们感兴趣的列。我们通过修改 SQL 查询中的 SELECT 子句来做到这一点。
我们没有选择*(即所有列),而是列出了我们想要返回的列:Year、Month 和 Tmax。
high = pd.read_sql('SELECT Year,Month,Tmax FROM weather WHERE Tmax > 25', conn)
那是按年份顺序的。也许按温度顺序排会更有意思。
我们也可以用 SQL 做到这一点。我们加上 ORDER BY 子句,并给出应规定顺序的列。DESC 的意思是降序(省略它,您将得到默认的升序)。
high = pd.read_sql('SELECT Year,Month,Tmax FROM weather WHERE Tmax > 25 ORDER BY Tmax DESC', conn)
因此,2018 年高居榜首,2006 年紧随其后。但是 1983 年和 1995 年分别排在第三和第四位,所以这里没有明显的模式显示温度随着时间的推移而变热。
有一点我们可以看到,最热的月份是在夏天!(谁会想到呢。)但为了确保万无一失,让我们绘制一个直方图。
high.plot.hist(y='Month', xticks=high['Month'])
是的,最热的月份往往是七月。所以,如果我们在寻找一种趋势,为什么我们不看看几十年来的七月,看看气温是如何变化的。
这是另一个查询:
july = pd.read_sql('SELECT Year,Month,Tmax FROM weather WHERE month == 6', conn)
这给了我们一个所有年份的温度表,但只有当月份等于 6 时,即 7 月。
现在,我们将绘制一个 Tmax 值随时间变化的条形图。
july.plot.bar(x='Year', y='Tmax', figsize=(20,5));
我们可以做一个回归图,看看是否有任何趋势,但坦率地说,看看这个条形图,没有明显的趋势。
因此,通过对伦敦气温数据的这种极其有限的分析,我们无法得出气候变化是否正在影响伦敦人的任何结论(但从个人经验来看,我可以说那里最近感觉相当温暖)。
但是我希望我已经展示了 SQLite 在与 Pandas 一起使用时是一个有用的工具,并且如果您以前没有使用过 SQL,您可以看到如何,即使是我在这里使用的很小一部分,可能会使您的数据分析受益。
一如既往,感谢阅读。如果你想知道我什么时候发表新文章,请考虑在这里注册一个电子邮件提醒。
如果你不是一个媒体订阅者,那就注册吧,这样你就可以每月花 5 美元阅读尽可能多的文章。在这里注册,我将赚取一小笔佣金。
获取代码
你可以在我的 Github 页面上找到这篇文章和其他文章的样本代码。
Python 熊猫数据帧到 Google Sheets for Tableau 公共直播
博客的第二部分—
T 他的博客是【LIVE Tableau 公共可视化自动化 ETL 的一部分,分为三个部分,即:
- 启用 Google Sheets API 并创建凭证
- 将 Python 连接到 Google Sheets 并导出数据帧
- 使用 Google Sheets 启用 Tableau 公共直播
1.启用 Google Sheets API 并创建凭证
https://console.developers.google.com/
访问您的 Google API 控制台是连接 python 脚本和 google sheets 的第一步。循序渐进地启用 google sheets API 并创建所需的凭证。
1.1 创建一个项目
- 点击左上角下拉菜单中的选择一个项目,将会打开一个弹出窗口
- 在弹出窗口的右上角,点击’新建项目,这将带您进入一个新窗口
- 给你的项目取一个合适的名字(例如:covid-viz-data)。您可以保持’位置* '字段不变,并点击’创建
1.1 GIF 创建项目
1.2 启用 Google Sheets API 和 Google Drive API
- 从左侧窗格中选择’库’(在’仪表板’下),这将把您重定向到’ API 库’窗口,其中有一个搜索 API 和服务的选项
- 在字段’ 中键入 Google Sheets API '并从结果中选择它,这将重定向到 Google Sheets API 窗口
- 点击启用,为您在上一步中创建的项目启用它
类似地,启用 Google Drive API ,因为稍后导入的 python 库可能会使用上述两个 API 的功能。
启用 Google Sheets API 的 1.2 GIF
1.3 创建访问 API 的凭证
您可以在 Google Sheets API 窗口的右侧选择“创建凭证,或者如果您在仪表板上,从步骤 1 开始。
-
从左侧窗格中选择“凭证”(在“库”下)。这将在右侧向您显示’凭证窗格
-
点击顶部的“创建凭证”选项,这将显示一个下拉菜单
-
单击下拉菜单中的“帮助我选择选项,这将重定向到标题为“将凭证添加到您的项目”的窗格
-
在这个’将凭证添加到您的项目中’窗格中跟随:
步骤 1: ’ 找出您需要哪种凭证 ’
您正在使用哪个 API?— Google Sheets API
你会从哪里调用这个 API?— Web 服务器 您将访问哪些数据?— 应用数据 您打算将此 API 与 App Engine 或 Compute Engine 一起使用吗?— 不,我现在没有使用它们 ,单击“*我需要什么凭证?*根据我们输入的建议步骤 2:创建服务帐户
服务帐户名称— (例如:covid-data-update)
角色— 编辑器(从下拉列表中选择“项目,然后选择“编辑器”)
服务帐户 ID —自动创建的
密钥类型—
这将要求您保存凭证。允许访问您的云资源的 json 文件— 因此安全地存储它 。
1.3 GIF 到 1.3 创建访问 API 的凭证
1.4 在中与客户电子邮件共享谷歌表单。json 凭证文件
现在我们有了访问 API 的秘密凭证(保存在。json 格式),我们需要确保目标 google sheet 与以该格式生成的客户端电子邮件共享。json 文件。
- 从中的“ client_email ”键复制电子邮件地址值。json 文件
- 创建一个新的 google 工作表,用于导出数据
- 打开表单后,点击右上角的共享
- 粘贴复制的电子邮件地址,并授予其“Can e dit ”访问权限
1.4 GIF 与客户端电子邮件共享谷歌表。json 凭证文件
恭喜你!我们已经成功地从 Google API 控制台启用了 Google Sheets API ,并创建了通过 python 脚本访问该 API 的凭证。现在,让我们进入第二部分。
2.将 Python 连接到 Google Sheets 并导出数据帧
https://developers.google.com/sheets/api/quickstart/python
虽然 Google 创建了 Google Sheets API,但是他们当然没有留下来为 Python QuickStart 创建一个精美的指南。我强烈推荐参考它,因为额外的文档/指南提供了使用 Google Sheets 的巨大可能性。
那为什么是这个博客呢?
快速入门要求你安装 3 个库,分别是Google-API-python-client、 google-auth-httplib2 、 google-auth-oauthlib 。但是,从大量的 python 库来看,我们有:
pyg Sheets——一个简单、直观的 python 库,通过 google Sheets API v4 访问 Google 电子表格
这一部分将利用 pyghseets 导出数据框架到谷歌表。让我们开始吧:
import sys
!{sys.executable} -m pip install pygsheets
为什么我没有使用!pip install pyghseets
?在 Jupyter 中,上面的代码确保您运行的是与当前 Python 内核相关联的 pip 版本。另外,仅供参考,shell 环境和 Python 可执行文件是断开的。点击此处了解更多信息。
import pygsheetsclient = pygsheets.authorize(service_file='/Users/eklav/**credentials.json**')
print("-----------------Authorized--------------------")
安装完成后,下一步当然是导入库。然后,使用[authorize](https://pygsheets.readthedocs.io/en/stable/reference.html#authorization)
功能创建一个变量client
,用 google 帐户认证 python 脚本(或应用程序)。
sheet = client.open('COVID-19')
print("-----------------Sheet Opened------------------")
现在,使用[open](https://pygsheets.readthedocs.io/en/stable/reference.html#pygsheets.client.Client.open)
功能按标题(例如:新冠肺炎’)返回电子表格(在上一部分创建),并将其赋给变量sheet
。
wks = sheet[0]
print("-----------------First Sheet Accessed----------")
为了访问第一个工作表,将工作表sheet
的第0个索引分配给变量wks
。
wks.set_dataframe(df_COVID,(1,1))
print("-----------------Data Updated------------------")
现在,使用工作表wks
的.set_dataframe
功能将’ df_COVID ‘*和’ (1,1) ‘分别赋值给’ df ‘和’ start '参数。
值“ (1,1) ”是指电子表格的 A1 单元格。
要了解 dataframe ’ df_COVID 是如何策划的,请参考博客的第一部分—从 GitHub 提取数据并自动运行或调度 Python 脚本。
这就把我们带到了这一部分的结尾。我们启用了 API 和凭证,使用它们将 Python 连接到 Google Sheets——您的免费云存储。
3.使用 Google Sheets 启用 Tableau 公共直播
正如在母博客中提到的,最终是时候打破 Tableau Public 不能拥有实时数据的神话了。到目前为止,我们已经有了一个由 Windows 任务调度器自动运行的 Python 脚本,它反过来更新 Google 工作表。这张表将成为我们在 Tableau Public 上发布的仪表盘或可视化数据的来源。
3.1 将 Tableau 连接到 Google Sheet
我将不再详述一个众所周知的步骤——从 Tableau 起始页中选择一个数据源。如果需要,请遵循下面的 GIF:
3.1 GIF 将 Tableau 连接到 Google Sheet
好了,现在你可以根据需要用来自你的 Google Sheets 的实时数据创建可视化了
3.2 在 Tableau Public 上发布工作簿
虽然你有一个与谷歌工作表的实时连接,它必须直接发布工作簿。登录https://public.tableau.com服务器,分享即可。
但是,问题就在这里!将弹出以下错误:
您要发布到的 Tableau 服务器要求对数据源启用提取。使用数据菜单启用以下数据源的提取:
需要数据提取"
现在的问题是— 数据提取???
从 Google Sheets 获取数据的全部意义在于拥有实时连接。这就是我们认为 Tableau Public 只处理摘录的地方。
但是,Tableau 还有更多的服务——不要眨眼,直接连接到“ Extract ”。现在,当您共享时,勾选显示以下内容的复选框并保存:
“保持我的数据与 Google 工作表同步,并嵌入我的 Google 凭据”
点击“保存”按钮后,您将被重新定向到 Tableau Public,您的仪表板已发布。向下滚动并注意到“请求更新按钮。此按钮用于强制更新仪表板,从 Google 工作表中访问最新数据。不然不然我经历的 Tableau 每天自动刷新仪表盘。
答对了。
3.2 在 Tableau Public 上发布工作簿的 GIF
参考
感谢您的阅读!我希望这个博客揭示了 Tableau 公共有趣的一面。继续下去,让你的可视化生活。如果这篇文章是有帮助的,分享它。
请在评论中告诉我:
- 如果你觉得这有用或没用,或者
- 如果您想了解 Tableau 可视化中使用的不同功能的如何使用
来自 JHU CSSE GitHub Repo 的 ETL 创建 Tableau 可视化。ETL: Extract 中执行的步骤。GitHub 中的 csv 文件…
github.com](https://github.com/eklavyasaxena/COVID-19) [## Eklavya Saxena -印度|职业简介| LinkedIn
精通数据的分析师和有抱负的数据科学家,拥有 2 年以上的销售或客户行业经验…
www.linkedin.com](https://www.linkedin.com/in/eklavyasaxena/)
Node.js 应用程序中的 Python 和 Pandas
使用 Python-Pandas 和 node . js-Express 的新冠肺炎时间序列聚合器 API 示例
NodeJS 和 Python 插图作者 Chaeyun Kim
在数据科学领域,我们都知道 Python 是处理数据管理、分析、可视化、ML 等等的最佳工具之一。同样, Node.js 是用于服务器端的 JavaScript 运行时环境,根据其经济效益、快速响应时间等,它是最流行的 web 架构工具之一。从编程语言流行指数看(PYPL): Python 和 JavaScript 是前 1 & 3 大流行语言。
编程语言流行 2020 年 5 月(截图来自http://pypl.github.io/PYPL.html作者)
从我的经验来看,我看到大多数 Python 爱好者都试图用 Python 写所有东西。大多数 Node.js Volk 试图用 JavaScript 进行数据清理和操作…(对于所有的回调有点痛苦…)互联网上的一些博客试图比较 Node.js 和 Python,发现 Node.js 在 Web 开发方面很棒,Python 在数据科学方面很棒。实际上,我们不需要总是坚持使用同一种编程语言,因为有很多方法可以一起使用它们。在本文中,我将向您展示一个如何从 Node.js web 应用程序中使用 Python 脚本的示例。
示例:
让我们用 Node.js & Pandas 构建新冠肺炎时间序列聚合器 API
在本文中,我将展示一个如何创建一个简单的 API web 应用程序的示例,在该应用程序中,用户可以使用 Node.js 和 Express 框架请求新冠肺炎数据集和时间序列聚合间隔。有趣的是,我们将把 Python 和 Pandas 完成的时间序列聚合也集成到应用程序中。
支持时序聚合的新冠肺炎快速 API 的简单节点(启用)
随后,开发人员可以使用这样的 API 应用程序,通过任何 JavaScript 库在 web 应用程序中轻松呈现图表/地图/仪表板;如 Apexchart.js 、 Highchart.js 或 Chart.js 。下图显示了对每日新冠肺炎数据集使用简单 Apexchart 的示例。
用 Apexchart.js 展示新冠肺炎确诊病例演示(作者)
下图显示了该应用程序的整体结构:
本例的系统架构(作者)
A 部分:Python 和 Pandas 用于时间序列分析
首先,让我们从 Python 应用程序开始,我将从我的关于用 Python 对新冠肺炎时间序列进行分析的文章中选取一个例子。简而言之,我们将使用一个简单的 Python 脚本来读取和分析从 JHU CSSE 到熊猫数据帧的新冠肺炎数据集。然后,按“国家/地区”级别对数据集进行分组,转置数据帧,将日期/时间列设置为索引列,对确诊新冠肺炎病例最多的国家进行排序,并根据输入的国家编号选择排名靠前的国家。之后,用输入的时间间隔对数据集进行重采样,并返回*。json* 输出给用户。以下脚本显示了整个脚本:
covid 19 aggregator . py(作者示例脚本)
这个脚本有两个参数;第一个参数(arg1)是确诊新冠肺炎病例最多的国家数量,第二个参数(arg2)是累计时间间隔。例如,如果我们想要获得来自前三个国家的已确认的新冠肺炎病例和每月的汇总数据,那么我们可以使用以下命令运行 Python 脚本:
**$ python covid19aggregator.py 3 'M'**
然后,您将获得以下结果(截至 2020 年 5 月 10 日):
执行带有 3/‘M’个参数的covid 19 aggregator . py的示例结果(作者的示例结果)
您可以看到时间索引被写成了时间戳,我们将这样保存它,因为它便于以后在基于 web 的可视化工具中使用。
B 部分:带有 Express 应用程序的 Node.js
在这一部分中,我们将从创建一个简单的 Node.js & Express 应用程序开始。您可以使用以下命令启动项目并安装 express:
**$ npm init
$ npm install express --save**
然后,使用下面的示例脚本为 Node.js web 服务器创建一个server.js
文件。如果你是新来的快递,请看这里的文档。在第 1 部分中,我们在端口 3000 上创建了一个简单的 express 应用程序。你可以换到任何你喜欢的港口。在第 2 部分中,我们定义了一个 GET 请求路由到“http://localhost:3000/covid _ 19 _ time series”,并使用child_process
生成 Python 子进程,还将 URL 查询参数‘numberOfCountries’
和‘aggregationInterval’
传递给 Python 进程。您可以在这里看到完整的 Node.js 服务器脚本:
NodeJS 服务器 。js 与衍生covid 19 aggregator . pyPython 进程(作者示例脚本)
完成了!现在,这个 Node.js 服务器已经完成,当用户调用以获取 COVID 19 数据时,它会生成 Python 子流程。让我们通过运行一个服务器来测试它:
**$ node server.js** server running on http://localhost:3000
让我们在 web 浏览器上查看结果:
(在这里,您可以传递任意数量的国家/聚合间隔)
[**http://localhost:3000/covid_19_timeseries?numberOfCountries=5&aggregationInterval=M**](http://localhost:3000/covid_19_timeseries?numberOfCountries=5&aggregationInterval=M)
网络浏览器上的示例结果。(作者截图)
所以,大概就是这样。现在,您已经有了一个用 Node.js 和 Python 构建的简单 API 应用程序。请注意,这个示例是为了展示将它们集成在一起的概念,可以在 Node.js 中改进这些脚本以获得更好的 API 路由结构,在 Python 中改进这些脚本以获得更复杂的数据操作工作流。
结论
本文展示了一个示例,说明如何使用 NodeJS 通过 Pandas 生成一个 Python 进程,并将结果直接返回给 API 调用。通过这种方式,我们可以灵活地将所有很酷的数据 Python 脚本与流行的 JavaScript web 框架集成在一起。我希望你喜欢这篇文章,并发现它对你的日常工作或项目有用。如果您有任何问题或意见,请随时给我留言。
关于我&查看我所有的博客内容:链接
安全健康健康**!💪**
感谢您的阅读。📚
Python 熊猫迭代数据帧
了解使用 Python 迭代熊猫数据帧的不同方法
由 Maicol Santos 在 Unsplash 上拍摄
介绍
作为一名数据科学家,我们有时会遇到低质量的数据。为了取得成功,我们需要能够在任何分析之前有效地管理数据质量问题。谢天谢地,有几个强大的开源库,我们可以利用它们来有效地处理数据,比如 Pandas 。今天我们将看看我们可以循环访问一个数据帧并访问其值的不同方法。迭代数据帧可以被合并到初始探索性数据分析之后的步骤中,以开始清理原始数据。
入门指南
熊猫是什么?
对于那些刚接触数据科学或不熟悉 Pandas 的人来说,Pandas 是一个用 Python 编写的开源库。它提供了易于使用的现成功能来接收和分析关系数据。Pandas 支持多种文件类型的消费,例如 CSV、Excel、JSON、XML、HTML 和 SQL 等等。
安装熊猫
安装 Pandas 最简单的方法是使用 PyPI ,它通过在终端运行命令pip install pandas
使用pip
从源代码安装 Pandas。具体操作系统的详细安装说明可以在 Pandas 入门页面上找到,或者如果您正在使用 PyCharm 说明可以在学习 Pandas Profiling 中找到。
熊猫数据框
对于大多数人来说,Pandas 将数据收集到两个对象中的一个:
- 系列:系列是一维 ndarry 对象。
- 数据帧:数据帧是一个二维数据结构,包含带标签的行和列。
显示系列和数据帧的构造函数的 Python 代码片段。
迭代数据帧
对于本例,我们已经创建了一个带有明确命名的行和列的 DataFrame,以帮助您入门并演示数据结构。通常,我们只命名列,并允许行索引自动生成一个数值范围。
Python 代码片段将生成一个 3 列 5 行的熊猫数据帧。
控制台输出显示了上述 Python 代码片段的结果。
执行完 Python 代码片段后,您应该会收到类似上面的输出。在这里,您可以清楚地看到 Pandas DataFrame 对象是如何使用一系列行和列来构造的。
DataFrame.iterrows()
遍历数据帧的第一种方法是使用 Pandas .iterrows()
,它使用索引行对遍历数据帧。
展示如何使用熊猫的 Python 片段。iterrows()内置函数。
控制台输出显示了使用。iterrows()。
在 DataFrame 上调用了.iterrows()
之后,我们就可以访问作为行标签的index
和代表行本身值的序列row
。上面的代码片段利用了Series.values
,它为被引用的行返回每列中所有值的 n 数组。
在.iterrows()
中,row
变量是 Series 类型,这意味着我们可以通过数据帧的指定列来访问值。例如,如果我们只对column_a
感兴趣,我们可以使用下面的代码片段只返回那些值。
Python 代码片段显示了如何在使用?iterrows()。
上面的代码片段还演示了一种通过row.get('column_name', default_value)
而不是row['column_name']
来检查行中的列的更安全的方法。如果我们在寻找一个不存在的列,row['column_name']
会引发一个KeyError
异常。
DataFrame.itertuples()
迭代数据帧的下一个方法是.itertuples()
,它返回一个迭代器,包含代表列名和值的名称元组。
显示熊猫语法的 Python 片段。itertuples()内置函数。
显示调用结果的控制台输出。数据帧上的 itertuples()。
这个方法仍然提供了通过语法row.column_name
隔离单个列的能力。如果我们只需要返回一个元组,我们可以将name=None
和index=False
传递给.intertuples()
,这将从每一行中删除指定的列和索引。
最佳实践
虽然能够使用.iterrows()
和.itertuples()
迭代数据帧很方便,但一般来说,建议不要这样做,因为对于较大的数据帧,性能会很慢。通常,当人们想要迭代一个数据帧时,需要添加一个计算列或者重新格式化一个现有的列。Pandas 通过其内置函数.apply()
提供这种类型的功能。.apply()
函数为更新数据帧提供了更有效的方法。熊猫申请权力用户深入了解熊猫.apply()
。
摘要
Pandas 提供了几种方法,我们作为数据科学家可以使用这些方法来迭代数据帧,比如.iterrows()
和.itertuples()
。.iterrows()
和.itertuples()
都提供了强大的安全方法来访问数据帧行值。虽然许多具有编程背景的新数据科学家可能倾向于熟悉数据帧上的循环,但 Pandas 通过内置的 apply 函数提供了一种更有效的方法。
感谢您花时间阅读我们的故事,我们希望您发现它有价值!
Python 熊猫合并数据帧
在 Python 中合并熊猫数据帧的快速操作指南
介绍
作为数据科学家,我们经常会发现我们需要同时分析来自多个数据源的数据。为了成功实现这一点,我们需要能够使用各种方法有效地合并不同的数据源。今天我们将看看如何使用 Pandas 内置的.merge()
函数,通过几种不同的连接方法来连接两个数据源。
入门指南
对于那些刚刚接触数据科学或者还没有接触过 Python Pandas 的人,我们建议首先从 Pandas 系列& DataFrame Explained 或 Python Pandas 迭代 DataFrame 开始。这两篇文章都将为您提供安装说明和今天文章的背景知识。
熊猫合并
Pandas 内置函数.merge()
提供了一个强大的方法,使用数据库风格的连接来连接两个数据帧。
句法
上面的 Python 片段显示了熊猫的语法。merge()函数。
因素
right
—这将是您要加入的数据框架。how
—在此,您可以指定两个数据框的连接方式。缺省值是inner
,但是,对于左外连接,您可以通过left
,对于右外连接,您可以通过right
,对于全外连接,您可以通过outer
。on
—如果两个数据帧都包含一个共享列或一组共享列,那么您可以将它们作为键传递给on
进行合并。left_on
—在此,您可以指定一列或一列标签,您希望将这些标签加入左侧数据框。当您希望在两个数据框架中连接的列的名称不同时,此参数非常方便。right_on
—与left_on
相同的条件适用于右侧数据帧。left_index
—如果您想使用索引连接左侧数据框,则通过True
。right_index
—如果您想使用索引连接正确的数据帧,则通过True.
sort
—如果你希望连接的键按字典顺序排序,你可以在这里输入True
。suffixes
—如果两个数据框架共享列标签名,则可以指定要应用于重叠的后缀类型。左侧默认为_x
,右侧默认为_y
。indicator
—如果您想标记行的来源,您可以将该参数设置为True
。该标志将指示行关键字是只出现在左数据帧中,还是出现在右数据帧中,还是同时出现在两个数据帧中。validate
—在这里,您可以查看数据帧是如何连接的,以及键之间的关系。您可以通过下面的1:1
来检查左右数据帧中的键是否唯一,1:m
来检查合并的键是否只对左数据帧唯一,m:1
来检查合并的键是否只对右数据帧唯一。
实际应用
下面我们将通过几个例子来学习如何使用合并功能。下面提供的代码片段将帮助您创建两个数据帧,我们将在故事的剩余部分使用它们。
上面的 Python 片段创建了两个数据帧,您可以使用它们来继续下面的示例。
上面的控制台输出显示了执行 Python 片段创建两个数据帧的结果。
内部连接
内部连接方法是 Pandas merge default。当您传递how='inner'
时,返回的数据帧将只包含来自两个数据帧之间共有的连接列的值。
上面的 Python 片段演示了如何使用内部连接来连接两个数据帧。
上图显示了内部连接两个数据帧后的控制台输出。
从上面控制台输出的屏幕截图中,我们可以看到内部连接对两个数据帧的影响。由于值a
、c
、e
和f
不在两个数据帧之间共享,因此它们不会出现在控制台输出中。输出还演示了在处理两个数据帧之间的共享列标签时的默认后缀应用程序。
左连接
Pandas left join 的功能类似于 SQL 中的 left outer join。返回的数据帧将包含来自左侧数据帧的所有值,以及在来自右侧数据帧的合并期间匹配连接键的任何值。
上面的 Python 片段显示了使用左连接合并两个数据帧的语法。
上面的屏幕截图显示了使用左连接合并两个数据帧的控制台输出。
如果右边的数据帧与合并列NaN
中的值不匹配,则将在返回的数据帧中插入而不是数字。
右连接
熊猫右连接执行与左连接类似的功能,但是连接方法应用于右数据帧。
上面的 Python 代码片段显示了使用 Pandas right join 合并两个数据帧的语法。
上面的屏幕截图显示了使用右连接合并两个数据帧的结果。
如上所述,在左侧数据帧中,右侧不存在的任何键都将插入一个NaN
值。
外部连接
Pandas 外部连接合并了两个数据帧,实质上反映了左右外部连接的组合结果。外部连接将从左右两个数据帧返回所有值。当 Pandas 在合并数据帧中找不到值时,将使用NaN
来代替。
上面的 Python 片段显示了使用外部连接来连接两个数据帧的语法。
上面的屏幕截图显示了使用外部连接合并两个数据帧的结果。
当使用外部连接合并两个数据帧时,了解新数据帧中的记录来自何处有时会很有用。您可以通过将indicator=True
作为参数传递给.merge()
函数来查看记录的来源,该函数将创建一个名为_merge
的新列。
摘要
要想成为一名成功的数据科学家,您需要经常同时熟练地处理来自多个数据源的数据。我们经常需要组合数据源,有时是为了丰富数据集或在当前数据中合并历史快照。Pandas 使用内置的.merge()
函数为连接数据集提供了一个强大的方法。Pandas .merge()
函数在连接类型方面提供了灵活性,您可以创建这些类型来实现所需的输出。
感谢您花时间阅读我们的故事,我们希望您发现它有价值!