# 八、随机性 > 原文:[Randomness](https://github.com/data-8/textbook/tree/gh-pages/chapters/08) > 译者:[飞龙](https://github.com/wizardforcel) > 协议:[CC BY-NC-SA 4.0](http://creativecommons.org/licenses/by-nc-sa/4.0/) > 自豪地采用[谷歌翻译](https://translate.google.cn/) 在前面的章节中,我们开发了深入描述数据所需的技能。 数据科学家也必须能够理解随机性。 例如,他们必须能够随机将个体分配到实验组和对照组,然后试图说明,观察到的两组结果之间的差异是否仅仅是由于随机分配,或真正由于实验所致。 在这一章中,我们开始分析随机性。 首先,我们将使用 Python 进行随机选择。 在`numpy`中有一个叫做`random`的子模块,它包含许多涉及随机选择的函数。 其中一个函数称为`choice`。 它从一个数组中随机选取一个项目,选择任何项目都是等可能的。 函数调用是`np.random.choice(array_name)`,其中`array_name`是要从中进行选择的数组的名称。 因此,下面的代码以 50% 的几率求值为`treatment`,50% 的机率为`control`。 ```py two_groups = make_array('treatment', 'control') np.random.choice(two_groups) 'treatment' ``` 上面的代码和我们迄今运行的所有其他代码之间的巨大差异在于,上面的代码并不总是返回相同的值。 它可以返回`treatment`或`control`,我们不会提前知道会选择哪一个。 我们可以通过提供第二个参数来重复这个过程,它是重复这个过程的次数。 ```py np.random.choice(two_groups, 10) array(['treatment', 'control', 'treatment', 'control', 'control', 'treatment', 'treatment', 'control', 'control', 'control'], dtype=' 1 + 1 True ``` `True`表示比较是有效的;Python 已经证实了`3`和`1 + 1`的关系的这个简单事实。 下面列出了一整套通用的比较运算符。 | 比较 | 运算符 | True 示例 | False 示例 | | --- | --- | --- | --- | | 小于 | `<` | `2 < 3` | `2 < 2` | | 大于 | `>` | `3 > 2` | `3 > 3` | | 小于等于 | `<=` | `2 <= 2` | `3 <= 2` | | 大于等于 | `>=` | `3 >= 3` | `2 >= 3` | | 等于 | `==` | `3 == 3` | `3 == 2` | | 不等于 | `!=` | `3 != 2` | `2 != 2` | 注意比较中的两个等号`==`用于确定相等性。 这是必要的,因为 Python 已经使用`=`来表示名称的赋值,我们之前看到过。 它不能将相同的符号用于不同的目的。 因此,如果你想检查`5`是否等于`10/2`,那么你必须小心:`5 = 10/`2返回一个错误信息,因为 Python 假设你正试图将表达式`10/2`的值赋给一个名称,它是数字`5`。相反,你必须使用`5 == 10/2`,其计算结果为`True`。 ```py 5 = 10/2 File "", line 1 5 = 10/2 ^ SyntaxError: can't assign to literal 5 == 10/2 True ``` 一个表达式可以包含多个比较,并且它们都必须满足,为了整个表达式为真。 例如,我们可以用下面的表达式表示`1 + 1`在`1`和`3`之间。 ```py 1 < 1 + 1 < 3 True ``` 两个数字的平均值总是在较小的数字和较大的数字之间。 我们用下面的数字`x`和`y`来表示这种关系。 你可以尝试不同的`x`和`y`值来确认这种关系。 ```py x = 12 y = 5 min(x, y) <= (x+y)/2 <= max(x, y) True ``` ### 字符串比较 字符串也可以比较,他们的顺序是字典序。 较短的字符串小于以较短的字符串开头的较长的字符串。 ```py 'Dog' > 'Catastrophe' > 'Cat' ``` 我们回到随机选择。 回想一下由两个元素组成的数组`two_groups`,`treatment`和`control`。 为了看一个随机分配的个体是否去了实验组,你可以使用比较: ```py np.random.choice(two_groups) == 'treatment' False ``` 和以前一样,随机选择并不总是一样的,所以比较的结果也不总是一样的。 这取决于是选择`treatment`还是`control`。 对于任何涉及随机选择的单元格,多次运行单元格来获得结果的变化是一个好主意。 ## 比较数组和值 回想一下,我们可以对数组中的很多数字执行算术运算。 例如,`make_array(0, 5, 2)*2`等同于`make_array(0, 10, 4)`。 以类似的方式,如果我们比较一个数组和一个值,则数组的每个元素都与该值进行比较,并将比较结果求值为布尔值数组。 ```py tosses = make_array('Tails', 'Heads', 'Tails', 'Heads', 'Heads') tosses == 'Heads' array([False, True, False, True, True], dtype=bool) ``` `numpy`方法`count_nonzero`计算数组的非零(即`True`)元素的数量。 ```py np.count_nonzero(tosses == 'Heads') 3 ``` ## 条件语句 在许多情况下,行动和结果取决于所满足的一组特定条件。例如,随机对照试验的个体如果被分配给实验组,则接受实验。赌徒如果赢了赌注就赚钱。 在本节中,我们将学习如何使用代码来描述这种情况。条件语句是一个多行语句,它允许 Python 根据表达式的真值选择不同的选项。虽然条件语句可以出现在任何地方,但它们通常出现在函数体内,以便根据参数值执行可变的行为。 条件语句总是以`if`开头,这是一行,后面跟着一个缩进的主体。只有当`if`后面的表达式(称为`if`表达式)求值为真时,才会执行主体。如果`if`表达式的计算结果为`False`,则跳过`if`的主体。 让我们开始定义一个返回数字符号的函数。 ```py def sign(x): if x > 0: return 'Positive' sign(3) 'Positive' ``` 如果输入是正数,则此函数返回正确的符号。 但是,如果输入不是正数,那么`if`表达式的计算结果为`false`,所以`return`语句被跳过,函数调用没有值(为`None`)。 ```py sign(-3) ``` 所以,让我们改进我们的函数来返回负数,如果输入是负数。 我们可以通过添加一个`elif`子句来实现,其中`elif`是 Python 的`else, if`的缩写。 ```py def sign(x): if x > 0: return 'Positive' elif x < 0: return 'Negative' ``` 现在当输入为`-3`时,`sign`返回正确答案。 ```py sign(-3) 'Negative' ``` 那么如果输入是`0`呢?为了处理这个情况,我们可以添加`elif`子句: ```py def sign(x): if x > 0: return 'Positive' elif x < 0: return 'Negative' elif x == 0: return 'Neither positive nor negative' sign(0) 'Neither positive nor negative' ``` 与之等价,我们可以用`else`子句替换最后的`elif`子句,只有前面的所有比较都是`false`,才会执行它的正文。 也就是说,输入值等于`0`的时候。 ```py def sign(x): if x > 0: return 'Positive' elif x < 0: return 'Negative' else: return 'Neither positive nor negative' sign(0) 'Neither positive nor negative' ``` ### 一般形式 条件语句也可以有多个具有多个主体的子句,只有其中一个主体可以被执行。 多子句的条件语句的一般格式如下所示。 ```py if : elif : elif : ... else: ``` 总是只有一个`if`子句,但是可以有任意数量的`elif`子句。 Python 将依次求解头部的`if`和`elif`表达式,直到找到一个真值,然后执行相应的主体。 `else`子句是可选的。 当提供`else`头部时,只有在前面的子句的头部表达式都不为真时才执行`else`头部。 `else`子句必须总是在最后(或根本没有)。 ### 示例:"另一个" 现在我们将使用条件语句来定义一个看似相当虚假和对立的函数,但是在本章后面的章节中会变得方便。 它需要一个数组,包含两个元素(例如,`red`和`blue`),以及另一个用于比较的元素。 如果该元素为`red`,则该函数返回`blue`。 如果元素是(例如)`blue`,则函数返回`red`。 这就是为什么我们要将函数称为`other_one`。 ```py def other_one(x, a_b): """Compare x with the two elements of a_b; if it is equal to one of them, return the other one; if it is not equal to either of them, return an error message. """ if x == a_b.item(0): return a_b.item(1) elif x == a_b.item(1): return a_b.item(0) else: return 'The input is not valid.' colors = make_array('red', 'blue') other_one('red', colors) 'blue' other_one('blue', colors) 'red' other_one('potato', colors) 'The input is not valid.' ``` ## 迭代 编程中经常出现这样的情况,特别是在处理随机性时,我们希望多次重复一个过程。 例如,要检查`np.random.choice`是否实际上是随机选取的,我们可能需要多次运行下面的单元格,以查看`Heads`是否以大约 50% 的几率出现。 ```py np.random.choice(make_array('Heads', 'Tails')) 'Heads' ``` 我们可能希望重新运行代码,带有稍微不同的输入或其他稍微不同的行为。 我们可以多次复制粘贴代码,但是这很枯燥,容易出现拼写错误,如果我们想要这样做一千次或一百万次,忘记它吧。 更自动化的解决方案是使用`for`语句遍历序列的内容。 这被称为迭代。 `for`语句以单词`for`开头,后面跟着一个名字,我们要把这个序列中的每个项目赋给它,后面跟着单词`in`,最后以一个表达式结束,它求值为一个序列。 对于序列中的每个项目,`for`语句的缩进主体执行一次。 ```py for i in np.arange(3): print(i) 0 1 2 ``` 想象一下,没有`for`语句的情况下,完全实现`for`语句功能的代码,这样很有帮助。 (这被称为循环展开。)`for`语句简单地复制了内部的代码,但是在每次迭代之前,它从给定的序列中将我们选择的名称赋为一个新的值。 例如,以下是上面循环的展开版本: ```py i = np.arange(3).item(0) print(i) i = np.arange(3).item(1) print(i) i = np.arange(3).item(2) print(i) 0 1 2 ``` > 译者注:实际的实现方式不是这样,但是效果一样。这里不做深究。 请注意,我的名字是任意的,就像我们用`=`赋值的名字一样。 在这里我们用一个更为现实的方式使用`for`语句:我们从数组中打印`5`个随机选项。 ```py coin = make_array('Heads', 'Tails') for i in np.arange(5): print(np.random.choice(make_array('Heads', 'Tails'))) Heads Heads Tails Heads Heads ``` 在这种情况下,我们只执行了几次完全相同的(随机)操作,所以我们`for`语句中的代码实际上并不涉及到`i`。 ### 扩展数组 虽然上面的`for`语句确实模拟了五次硬币投掷的结果,但结果只是简单地打印出来,并不是我们可以用来计算的形式。 因此,`for`语句的典型用法是创建一个结果数组,每次都扩展它。 `numpy`中的`append`方法可以帮助我们实现它。 调用`np.append(array_name,value)`将求出一个新的数组,它是由`value`扩展的`array_name`。在使用`append`时请记住,数组的所有条目必须具有相同的类型。 ```py pets = make_array('Cat', 'Dog') np.append(pets, 'Another Pet') array(['Cat', 'Dog', 'Another Pet'], dtype='>> is_goat('Goat 1') True >>> is_goat('Goat 2') True >>> is_goat('Car') False """ if door_name == "Goat 1": return True elif door_name == "Goat 2": return True else: return False def monty_hall(): """ Play the Monty Hall game once and return an array of three strings: original choice, what Monty throws out, what remains """ original = np.random.choice(doors) if is_goat(original): return make_array(original, other_one(original, goats), 'Car') else: throw_out = np.random.choice(goats) return make_array(original, throw_out, other_one(throw_out, goats)) ``` 让我们玩几次这个游戏。这里是一个结果。你应该运行几次单元格来观察结果如何变化。 ```py monty_hall() array(['Car', 'Goat 2', 'Goat 1'], dtype='