Python 应用计算思维(三)
原文:
zh.annas-archive.org/md5/D0EC374B077B4C90AC07697977B27176译者:飞龙
第九章:理解输入和输出以设计解决方案算法
在本章中,我们将深入研究问题,以确定设计问题算法所需的输入和输出。我们将使用我们在第八章中学到的概念,Python 简介,在那里我们讨论了面向对象的编程、字典、列表等。当您练习获取输入并在算法中使用它时,您将能够看到算法的输出取决于输入信息。
在本章中,我们将涵盖以下主题:
-
定义输入和输出
-
理解计算思维中的输入和输出
在本章中,我们将重点关注理解不同类型的输入以及如何使用Python编程语言中的输出。为了更好地理解这些主题,我们首先要看一下它们的定义。
技术要求
对于本章,您需要安装最新的 Python 版本。
您可以在此章节中找到使用的源代码:github.com/PacktPublishing/Applied-Computational-Thinking-with-Python/tree/master/Chapter09
定义输入和输出
我们将从研究输入及其定义开始,这反过来又用于提供结果或输出。
输入是放入系统或机器中的东西。在计算机中,我们有输入设备,计算机会解释这些设备以提供结果或输出。当我们看不同类型的输入以及如何在算法中使用输入时,您将看到我们从这些算法中获得的输出。让我们看一些输入设备的例子:
-
键盘:当我们使用键盘时,计算机会解释按键,并在各种程序中使用该解释,比如文档编写和搜索栏。
-
鼠标:鼠标用于在我们的物理屏幕上导航,帮助我们点击内容和滚动页面。
-
操纵杆:操纵杆可用于玩电脑游戏。
-
麦克风:在更现代的机器中,麦克风不仅用于通过电话和视频应用进行通信,还用于接受人工智能(AI)助手如Cortana和Siri的口头命令,以及语音转文字命令。
所有上述输入都被编程到我们的计算机中以便使用。在 Python 中,我们编写使用这些输入的算法。用户输入是指程序询问用户将用于生成输出的信息。请记住,输入和输出可以在算法中的任何时间发生。我们还可以提供需要用户额外输入的输出反馈,依此类推。让我们看看如何从 Python 获取用户输入。
Python 有一个主要的用户提示,即input()函数。此函数用于接受用户输入,然后自动将输入转换为用户期望的输出。
我们之前在多个程序中使用过这些主要提示,比如第五章中的商店示例,探索问题分析。让我们看看算法中一些输入命令的样子。看看以下代码片段:
ch9_input1.py
name = input("What is your name? ")
print("Nice to meet you " + name + ".")
片段要求用户输入他们的名字,然后使用输入的信息打印一个声明。这段代码的结果如下:
What is your name? Mikayla
Nice to meet you Mikayla.
现在,我们可以只要求输入而不使用任何提示问题,但用户将不知道正在被问及什么或者它将如何被使用。看看没有提示的片段,如下所示:
ch9_input2.py
name = input()
print("Nice to meet you " + name + ".")
当我们运行上述程序时,在我们的 shell 中没有打印任何内容。然而,我知道我需要输入一些东西,所以看看当我只输入一个字母时发生了什么:
d
Nice to meet you d.
正如你所看到的,用户不会知道该怎么做,因为窗口没有询问任何事情;它只是给了一个空白的空间,用户必须假设那是输入的地方。但这可能会导致很多混乱,因为没有告诉用户是否需要输入,或者他们需要输入什么样的东西。
我们可以通过使用 print 语句来减轻一些混乱,例如,通过向用户提供关于我们将要询问的输入的信息。我们还可以在 input() 函数中包含一条语句。例如,我们可以使用这样的一行代码 name = input('你叫什么名字?'),这样首先会显示带引号的语句,这样用户就知道我们在问什么。无论哪种方式,提供一条语句让用户知道需要什么是我们设计算法时非常重要的。
正如你在前面的例子中看到的,input() 函数接受一个字符串,然后在 print() 语句中使用该字符串。让我们看看使用 input 命令时列表会是什么样子:
ch9_input3.py
#Create the list
names = []
#Ask user how many names will be added
name = int(input("How many names will be in the list? "))
#Iterate to add each name to the list
for i in range(0, name):
people = str(input())
names.append(people)
print(names)
在上面的片段中,我们首先创建了列表,这不会显示给用户。然后询问用户将有多少个名字添加到列表中。之后,我们遍历列表,以便用户可以分别输入每个名字。最后,名字被添加到列表中并打印出来。看一下这个算法的输出:
How many names will be in the list? 4
Manny
Lisa
John
Katya
['Manny', 'Lisa', 'John', 'Katya']
注意,名字没有提示,所以算法假设用户在输入值 4 后会知道该怎么做。这可以通过在迭代中简单添加来减轻。看一下下面的代码片段:
ch9_input4.py
#Create the list
names = []
#Ask user how many names will be added
name = int(input("How many names will be in the list? "))
#Iterate to add each name to the list
for i in range(0, name):
people = input("Type the next name on the list. ")
names.append(people)
print(names)
正如你所看到的,每个名字现在都有一个提示,要求输入下一个名字,当算法运行时,输出看起来像下面的样子:
How many names will be in the list? 4
Type the next name on the list. Milo
Type the next name on the list. Sonya
Type the next name on the list. Gabriel
Type the next name on the list. Maxine
['Milo', 'Sonya', 'Gabriel', 'Maxine']
上面的输出显示了完成的列表以及添加到该列表中的每个名字的提示。在算法中对这些提示进行简单的添加可以减轻用户在输入时的混乱。
正如你从本节中前面的片段中看到的,输入被读取为整数、字符串和列表。Python 自动转换了信息,以便算法可以使用它。
在 Python 中,还有一种方法可以使用一行代码从多个输入中定义多个变量。看一下下面的代码片段:
ch9_input5.py
name1, name2 = input("Enter First Name: "), input("Enter Last Name: ")
print(name1 + " " + name2)
正如你在前面的代码中所看到的,print() 命令中的引号(" ") 用于分隔输入。看一下这个算法的输出:
Enter First Name: John
Enter Last Name: Doe
John Doe
正如你所看到的,程序要求输入名字,这些名字在算法的第一行中被调用。这绝不是我们从用户那里获取输入的唯一方式,也不是我们将使用的唯一输入。
在本节中,我们看了一些在算法中定义输入的方法。我们还学会了如何从用户那里获取输入以便稍后在算法中使用。现在让我们转到下一节,我们将在其中看一些计算思维问题中的输入和输出。
理解计算思维中的输入和输出
为了更好地理解输入和输出,我们将在本节中看一些计算思维问题。当我们设计算法的过程中,我们将专注于确定算法所需的输入以及我们从算法中需要的输出。让我们来看看我们的第一个问题。
问题 1 - 构建凯撒密码
凯撒密码是一种用于编码消息的密码系统。密码学用于保护信息,以便只有预期的用户可以阅读消息。凯撒密码使用字母的移位来编码消息。例如,字母 a 移动 3 个位置将变成 d。为了构建一个可以为我们做到这一点的算法,我们需要一些东西:
-
将要编码的消息
-
我们将每个字母移动多少
-
一个打印的编码消息
让我们思考一下前面提到的要点意味着什么。消息将需要用户输入。移位也将是一个输入,因为我们不希望程序总是使用相同的代码。否则,一旦知道了移位,原始消息就很容易被破解。打印的编码消息是我们的输出。以下是一些步骤,以帮助创建算法:
-
首先,我们需要输入消息。
-
然后我们需要定义输入移位。
-
之后,我们需要遍历每个字母。
-
迭代后,我们根据定义的移位调整每个字母。
-
最后,我们打印出新的编码消息。
我们将通过以下密码算法片段来举例说明前面的步骤:
ch9_problem1.py
#Print initial message for the user
print("This program will take your message and encode it.")
#Ask for the message
msg = input("What message would you like to code? ")
#Ask for shift
shift = int(input("How many places will you shift your message? "))
msgCipher = ""
#Iterate through the letters, adjusting for shift
for letter in msg:
k = ord(letter)
if 48 <= k <= 57:
newk = (k - 48 + shift)%10 + 48
elif 65 <= k <= 90:
newk = (k - 65 + shift)%26 + 65
elif 97 <= k <=122:
newk = (k - 97 + shift)%26 + 97
else:
newk = k
msgCipher += chr(newk)
print("Your coded message is below.")
print(msgCipher)
请注意,在迭代中,我们正在进行一些数学运算,以找出字母表中每个字母的值。我们使用一些条件语句来定义在使用用户定义的移位值后,每个字母的值是什么。
让我们看看当我们运行此算法时产生的输出:
This program will take your message and encode it.
What message would you like to code? Code this message
How many places will you shift your message? 2
Your coded message is below.
Eqfg vjku oguucig
正如您可以从前面的输出中看到的,注意消息和编码消息中的第一个单词 - Code:
-
C,向后移动两个字母,现在是E。 -
o现在是q,依此类推。
对于消息中的每个字母,字母都向后移动了两个位置。输出是我们的print()语句加上编码消息,由行print(msgCipher)给出。我们的输出包括两个语句以便清晰。第一个消息是必要的吗? 不。但是,在某些算法中,最好有一些行,让用户知道算法的运行情况。
让我们看看另一个问题。
问题 2 - 查找最大值
您被要求创建一个程序,该程序从数字列表中找到最大值。数字列表由用户提供。因此,为此问题创建一个算法。
首先,我们需要确定此特定问题的输入和输出。它们列如下:
-
列表中的项目数(输入)
-
列表中的数字(输入)
-
列表中的最大值(输出)
回想一下本章前面,在定义输入和输出部分,我们可以定义一个空列表,然后要求用户让程序知道将有多少项目输入列表。以下程序举例说明了相同的情况:
ch9_problem2.py
#Define the list name
maxList = []
#Ask user how many numbers will be entered
quant = int(input("How many data points are you entering? "))
#Iterate, append points, and find maximum
for i in range(0, quant):
dataPoint = int(input("Enter number: "))
maxList.append(dataPoint)
#Print maximum value
print("The maximum value is " + str(max(maxList)) + ".")
请注意,从前面的代码中,我们使用了max()函数来找到列表中的最大值。此外,我们还必须添加int()类型,以便算法正确识别值为数字。代码遍历从0到我们从用户输入中收到的数据点数,我们将其定义为quant变量。当算法遍历数字时,它会比较它们并找到最大值。
让我们看看输出是什么样子的:
How many data points are you entering? 6
Enter number: 1
Enter number: 2
Enter number: 8
Enter number: 4
Enter number: 5
Enter number: 7
The maximum value is 8.
正如您可以从前面的输出中看到的,用户声明将输入6个数字。然后算法提示用户输入每个值。最后,识别出最大值。
现在让我们看看如何构建一个猜数字游戏。
问题 3 - 构建一个猜数字游戏
您被要求构建一个游戏,用户可以根据需要获得尽可能多的机会来按正确顺序识别四位数的数字。当用户输入猜测时,算法将指出是否有任何数字是正确的。
算法将确定数字是否在正确的位置,但不会确定哪些数字是正确的,哪些在正确的位置。如果玩家猜对了数字,游戏就结束了。您可能会发现这个游戏类似于一个名为Mastermind的流行游戏,其中玩家将四个颜色代码插入游戏,第二个玩家猜测。
在这种情况下,我们使用数字而不是颜色,计算机程序将帮助我们确定是否有任何数字是正确的,以及它们是否在正确的位置。
为了构建这个游戏,让我们看看我们需要的输入和输出:
-
随机生成的 4 位数(输入)
-
用户的猜测(输入)
-
来自算法的数字和位置的反馈(输出)
-
在数字被猜中后,结束游戏的消息(输出)
当我们生成数字时,我们需要找到一种比较该数字的数字与用户输入的数字的方法。请注意,我们需要为这个特定的算法导入random库,以便我们可以生成随机的四位数。让我们看一下代码片段:
ch9_problem3.py
import random as rand
number = rand.randint(1000,10000)
guess = int(input("What's your first guess? "))
#Algorithm checks if number is correct.
if (guess == number):
print("That's right! You win!")
else:
i = 0
正如你所看到的,这是我们需要用户输入的一个例子。然而,这只是第一次猜测,所以我们需要从用户那里获取更多的输入,并提供一些提供反馈的输出。看一下来自同一算法的以下代码片段:
#Condition so that user keeps guessing until they win.
while (guess != number):
i = i + 1
#Remember you can also write as i += 1
j = 0
guess = str(guess)
number = str(number)
#Check which numbers are correct and mark incorrect with 'N'
guessY = ['N']*4
#Make sure you check each digit, so run loop 4 times
for q in range(0, 4):
if (guess[q] == number[q]):
j += 1
guessY[q] = guess[q]
else:
continue
#If only some digits are correct, run next condition
if (j < 4) and (j != 0):
print("You have " + str(j) + " digit(s) right.")
print("These numbers were correct.")
for m in guessY:
print(m, end = " ")
#Ask for next input
guess = int(input("What is your next guess? "))
注意,我们需要在每次猜测失败后询问每次猜测。算法需要知道下一次猜测是什么,以便在用户没有识别出正确的四位数时继续运行。这就是为什么猜测输入值在算法中出现在多个地方。
一旦用户猜对了,程序需要打印最终的声明,在这种情况下,我们会让用户知道猜测花了多少次:
#If only some digits are correct, run next condition
if (j < 4) and (j != 0):
print("You have " + str(j) + " digit(s) right.")
print("These numbers were correct.")
for m in guessY:
print(m, end = " ")
#Ask for next input
guess = int(input("What is your next guess? "))
#No digits correct
elif (j == 0):
print("None of these digits are correct.")
guess = int(input("What is your next guess? "))
if guess == number:
print("It took you " + str(i)+ " tries to win!")
正如你所看到的,这个算法在多个地方基于满足条件的输入,以及输出。输出包括print语句和对正确或错误数字的反馈。让我们看看程序执行时输出的样子:
What's your first guess? 1111
None of these digits are correct.
What is your next guess? 2222
None of these digits are correct.
What is your next guess? 3333
You have 1 digit(s) right.
These numbers were correct.
3 N N N What is your next guess? 3444
You have 3 digit(s) right.
These numbers were correct.
3 4 N 4 What is your next guess? 3454
You have 3 digit(s) right.
These numbers were correct.
3 4 N 4 What is your next guess? 3464
You won in 6 attempts.
请注意,在第三次尝试后,我们有一个正确的数字,表示为3 N N N。这意味着第一位数字是 3。然后我们需要猜测其余的数字。在程序中提供反馈,或输出,使用户能够继续这个游戏。
正如你所看到的,理解输入和输出使我们能够创建能够提供反馈、根据条件进行迭代等等的程序。
在这一部分,我们学习了如何在解决问题时使用输入和输出。我们提供了一些场景,让我们可以探索一些输入类型,比如用户定义的输入和算法定义的输入。我们还学习了如何编写算法,为用户提供清晰的输出。
总结
在本章中,我们讨论了 Python 算法中的输入和输出。输入是我们获取算法响应的信息的方式。例如,用户可以为需要提示信息的算法提供输入,就像我们在示例问题中看到的那样。Python 和其他编程语言也可以从鼠标、扫描仪、摄像头和程序交互的其他设备中获取输入。
为了编写我们的算法,重要的是要确定我们需要什么输入,我们在设计中何时需要它们,以及我们需要程序输出什么。在[第三章](B15413_03_Final_SK_ePub.xhtml#_idTextAnchor056),理解算法和算法思维中,我们使用用户输入来找到午餐的成本。我们创建了一个字典来帮助我们找到这些信息。同样,在阅读完本章之后,你现在有了构建算法来解决一些额外问题的技能,比如我们的数字猜测游戏和我们的最大查找器。
在设计算法之前,确定输入和输出是至关重要的,因为我们在算法中做出的条件和决策取决于输入和我们需要程序提供的输出。
在下一章中,我们将讨论控制流,这在理解算法的阅读方式和指令执行方式上至关重要。我们还将在探索控制流中更仔细地查看函数。
第十章:控制流
在本章中,我们将深入研究问题,并确定设计算法所需的输入和输出。在本章中,你将学习算法的阅读方式以及指令执行的顺序。你还将学习如何使用函数和循环来操纵算法中的控制流。
在本章中,我们将涵盖以下主题:
-
定义控制流及其工具
-
使用 if、for 和 range()以及其他控制流工具
-
使用循环和条件语句
-
重新审视函数
到本章结束时,我们将学习控制流是如何定义的,我们在计算思维中设计算法时如何使用if、for和range()功能,以及如何将这些功能纳入算法中的函数定义中。让我们先来看看什么是控制流。
技术要求
你需要最新版本的 Python 来运行本章中的代码。你可以在这里找到本章使用的完整源代码:github.com/PacktPublishing/Applied-Computational-Thinking-with-Python/tree/master/Chapter10
定义控制流及其工具
简单地说,控制流是算法读取和执行指令、函数和语句的顺序。控制流意味着计算机会做出决定。想想看:当我走出大楼时,我要么用伞,要么不用伞。这取决于天气是否下雨(我是否记得带伞,我猜)。根据这个条件,我们做出决定。这个过程就是算法设计中的控制流程。
让我们看看 Python 编程语言中可用的一些控制流语句。
-
当 -
如果 -
对于 -
范围() -
中断 -
继续 -
列表推导
-
通过语句 -
尝试语句
这些工具允许算法做一些事情,比如只要满足某个条件或一组条件,就可以运行,当条件发生时停止或中断,仅在一定范围内继续等等。让我们更仔细地看看其中一些工具。
使用 if、for 和 range()以及其他控制流语句
让我们从if语句开始,我们在第四章中首次讨论了这些内容,理解逻辑推理。这可能是算法设计中最常用和最知名的语句。你可能还记得在学习推理和证明时,在几何学中学习了条件语句。在那些课程中,你会以如果-那么的格式编写语句。接下来是一个例子:
下雨时,我穿雨衣。
这不是一个条件语句,至少现在不是。如果我们要将它写成条件句,那么我们就必须以如果-那么的格式来写,就像这个句子一样。看看接下来转换后的语句:
如果下雨,我就穿雨衣。
正如你所看到的,我们在日常生活中使用条件。我们只是没有指出它们。
在编写算法时,我们必须明确陈述算法需要做什么,所以我们必须明确陈述这些语句。在编程中,我们必须陈述每个条件。此外,如果我们有一系列需要满足的条件,有时我们需要嵌套这些语句。这最好通过一些例子来解释。让我们从如何使用嵌套语句开始。
使用嵌套 if 语句
即使在日常生活中,某些条件也取决于其他条件。例如,如果是星期一,我们就必须去上班。如果我们必须去上班而且下雨了,我们可能需要一把伞。但如果我们不去上班(假设我们呆在家里),我们就不需要检查是否需要伞。嵌套语句也是如此。我们使用它们来检查一个条件,然后是另一个条件,这是嵌套的。
假设我们正在玩一个掷骰子游戏。你掷骰子并得到如下分数:
-
2、4 或 6 = 10 分
-
1 或 3 = 5 分
-
5 = 0 分并清除所有之前的点数
每一轮,玩家都必须掷两次骰子。让我们看一个包含第一次掷骰子的流程图,如下所示:
图 10.1 - 第一次掷骰子得分的流程图
从流程图中可以看出,得分取决于掷出的数字。现在,假设你掷出了1。然后,让我们看看你的第二次掷骰子可能是什么样子,如下图所示:
图 10.2 - 在掷出 1 后进行第二次掷骰子的流程图
正如你所看到的,玩家有三种可能的总得分。为了将其转化为算法,我们需要做一些事情:
-
定义掷骰子。
-
根据掷出的数字确定得分。
-
再掷一次。
-
根据第二次掷骰子确定最终得分。
首先让我们定义那个点数。我们需要虚拟地掷骰子。这意味着我们需要程序在1和6之间选择一个数字。让我们看看我们如何编写代码。请记住,这只是包含在这个文件中的更大代码的一部分。我们将在随后的片段中使用第二个ready(ready2):
ch10_rollDice.py
import random as rand
print("Let's play a game. ")
print("You get 5 points for rolling 1 or 3\. You get 5 points for rolling 2, 4, or 6.")
print("You lose all points if you roll a 5 in either round.")
ready = input("Are you ready to begin? Type r to roll. ")
score = 0
if ready == 'r':
roll = rand.randint(1, 6)
print('You rolled a ' + str(roll) + '.')
if (roll == 1) or (roll == 3):
score = 5
elif (roll == 5):
score = 0
else:
score = 10
ready2 = input('Your round 1 score is ' + str(score) + '.
请注意,在上面的代码片段中,我们需要为这个特定的算法导入random库。将其导入为rand而不是random可以让我们缩短一些代码,从而减少输入。因此,我们使用rand.randint()而不是random.randint()。我们需要random库,因为我们希望能够访问随机整数函数,以便算法可以在1和6之间选择一个数字(包括两个端点)。让我们看看当前的输出:
Let's play a game.
You get 5 points for rolling 1 or 3\. You get 5 points for rolling 2, 4, or 6.
You lose all points if you roll a 5 in either round.
Are you ready to begin? Type r to roll. r
You rolled a 3.
Your round 1 score is 5\.
从上面的输出中可以看出,首先提供了说明,然后一旦玩家按下r键选择掷骰子,游戏就会选择一个随机整数并显示出来。它还显示了第一轮后的当前得分。现在,让我们看看算法的下一部分,即进行第二次掷骰子。请注意,缩进遵循前面的代码片段。这个片段也包含在之前的较大文件中:
ready2 = input('Your round 1 score is ' + str(score) + '. Type r to roll again. ')
roll2 = rand.randint(1, 6)
print('You rolled a ' + str(roll2) + '.')
if (roll2 == 1) or (roll2 == 3):
score += 5
elif (roll2 == 5):
score = 0
else:
score += 10
print('Your final score is ' + str(score) + '.')
当我们运行新代码时,它会显示第一轮得分,两次掷骰子和游戏的最终得分。请记住,如果我们在第二轮中掷出5,我们将失去所有的点数:
Let's play a game.
You get 5 points for rolling 1 or 3\. You get 5 points for rolling 2, 4, or 6.
You lose all points if you roll a 5 in either round.
Are you ready to begin? Type r to roll. r
You rolled a 2.
Your round 1 score is 10\. Type r to roll again. r
You rolled a 6.
Your final score is 20.
正如你所看到的,程序为我们打印了一些内容。首先,我们掷出了2和6,所以我们没有失去我们的点数。其次,2和6都有 10 分,最终得分为 20 分。但让我们回顾一下嵌套的if语句。
要开始游戏,我们必须验证玩家是否准备好掷骰子。第一个if语句是必要的,以便继续进行其他决定。我们可以在没有许可的情况下掷骰子吗? 可以。但想想所有的游戏,包括传统棋盘游戏的应用版本。在这些应用中,玩家总是按下按钮或骰子来玩。这是一个类似的情况。
在我们说好准备掷骰子之后,就必须对点数做出决定。我们再次需要使用if、elif和else语句来循环遍历各种选项。现在,让我们看看何时可以使用for循环和range。
使用 for 循环和 range
我们需要首先讨论的是for循环中有时会对变量和for循环条件产生一些混淆。为了理解我的意思,让我们看一个例子:
ch10_forLoop1.py
for letter in 'mountain':
print(letter)
在上面的代码片段中,letter不是一个变量。它只是告诉 Python 我们想要迭代单词mountain中的每个字符。不过,我可以随便起名字。如果我写成下面这样,程序会做完全相同的事情:
for pin in 'mountain':
print(pin)
在每种情况下,使用letter、pin或者任何让我当时感到高兴的单词,程序的输出看起来像这样:
m
o
u
n
t
a
i
n
正如您所看到的,Python 迭代了单词mountain中的每个字母,并将其打印到控制台上。同样的事情也可以用范围内的数字来做。例如,如果我想打印出1到10的数字,我可以使用for循环,如下所示:
ch10_forLoop2.py
for num in range(1, 11):
print(num)
*等等,我说我想要 1 到 10 的数字,为什么范围函数中有 11 呢?*那是因为range()函数总是包括范围内的最小值,但不包括上限。因此,我们需要在我们的顶部数字上加1。让我们看看这个程序的输出是什么样子的:
1
2
3
4
5
6
7
8
9
10
正如您所看到的,每个数字都打印在单独的一行上。因此,让我们看看如果我们只想将这些数字添加到一个列表中会怎么样。我们可以将它们附加到一个新列表中,然后打印出列表。这对某些类型的算法和游戏非常有帮助。但在我深入讨论这些之前,让我们首先看看如何使用几行代码附加这些数字:
ch10_forLoop3.py
myNumbers = []
for num in range(1, 11):
myNumbers.append(num)
print(myNumbers)
现在,当您打印列表时,请注意。如果您正确缩进了它,它将在每次附加新数字时都打印列表。但如果您的缩进是正确的,您只会打印最终列表,使输出看起来如下:
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
正如您所看到的,我们现在有了相同信息的表示。在这种情况下,我们打印出了数字列表。如果您试图将特定的东西附加到列表中,即使是用户输入,这是很有帮助的。在下一节中,当我们看while循环时,我们将看另一个列表问题,但在我们继续之前,让我们再看一个for循环问题和算法。
假设您想要一个算法,它将打印出一系列数字的立方。有一个条件:范围将根据用户输入而变化。让我们看看一个可以做到这一点的算法:
ch10_forLoop4.py
print('This program will provide a list of cubes for you. ')
minRange = int(input('What\'s the minimum for your range? '))
maxRange = int(input('What\'s the maximum for your range? '))
listOfCubes = []
for value in range(minRange, maxRange+1):
number = value**3
listOfCubes.append(number)
print('Your list of cubes in the range(' + str(minRange) + ', ' \
+str(maxRange) + ') is: ')
print(listOfCubes)
让我们从前面的程序中注意几点:
-
我们将输入转换为
int类型。我们这样做是为了可以在for循环内使用数字进行数学表达式。 -
我们有一个空列表,所以我们可以将立方附加到它上面。
-
我们将
maxRange加1,因为如果不这样做,该数字将不包括在我们的结果中。 -
我们有一个基于两个变量的范围,这两个变量是用户输入。
-
我们有一个打印出的值列表。
当我们输入range(3, 6)时,程序看起来像这样:
This program will provide a list of cubes for you.
What's the minimum for your range? 3
What's the maximum for your range? 6
Your list of cubes in the range(3, 6) is:
[27, 64, 125, 216]
正如您所看到的,程序接受了提供的输入,最小值为3,最大值为6,并创建了该范围内数字的立方的列表。
重要提示:
记住,要得到指数,我们在 Python 中使用**符号。所以,2**2是4,3**4是81,等等。
记住,控制流是事情完成的顺序。通过for循环和range,我们要求程序重复执行事情,而不必每次输入一行,有效地操纵控制流以循环而不是一行接着一行地做事情。
现在我们已经看过了for循环和range,让我们看看在 Python 中我们可以以其他方式进行迭代的方法。
使用其他循环和条件
Python 提供了各种迭代信息的方法,除了for循环之外,还有while循环。使用while循环时,我们不断检查条件。再次,通过看一个例子来理解这一点会更容易。
假设我们正在玩一个游戏,并要求用户提供尽可能多的动物。我们希望能够做一些事情:
-
检查玩家是否有要添加的动物。
-
如果是,将每个动物附加到列表中。
-
如果没有,结束程序并提供最终列表。
-
提供最终列表和玩家能够输入的动物数量。
让我们看一个为这个游戏做基本工作的算法:
ch10_whileLoop1.py
myAnimals = []
print('Let\'s see how many animals you can name. Get ready!')
readyPlayer = input('When you are ready to begin, type y. ')
while readyPlayer == 'y':
if readyPlayer == 'y':
animalAdd = input('Name an animal. ')
myAnimals.append(animalAdd)
readyPlayer = input('Ready for the next one? Type y for yes or n for no. ')
howMany = len(myAnimals)
print('You were able to name ' + str(howMany) + ' animals. Here\'s your list: ')
print(myAnimals)
让我们稍微解释一下这段代码:
-
我们创建了一个空列表
myAnimals。 -
然后我们问玩家是否准备好了。
-
如果答案是肯定的,我们就会要求输入一个动物。
-
然后,我们检查他们是否还有更多要添加的。
-
我们检查了列表中添加了多少项。
-
我们打印了列表。
请注意,我们必须使用while循环和if语句。这是因为我们希望继续询问玩家添加动物,直到他们完成或无法想到更多要添加的动物为止。因此,只要玩家仍然可以添加项目,我们希望程序继续询问问题。但是如果玩家完成了,那么他们可以拒绝添加另一个动物,从而结束程序。
现在,我想明确一点,这个程序可以做得更好。我们可以添加一个计时器,看看在 30 秒内可以输入多少项。当玩家试图输入动物而不是先说“是”时,我们也可以添加一个条件来中断程序并解释为什么这样做。你可以为玩家试图输入已经在列表中的动物添加一个条件。你的挑战是尝试将这些组件添加到现有的程序中!
但是让我们看看当我们运行它时,这个程序是什么样子的:
Let's see how many animals you can name. Get ready!
When you are ready to begin, type y. y
Name an animal. bird
Ready for the next one? Type y for yes or n for no. y
Name an animal. dog
Ready for the next one? Type y for yes or n for no. y
Name an animal. cat
Ready for the next one? Type y for yes or n for no. y
Name an animal. mouse
Ready for the next one? Type y for yes or n for no. y
Name an animal. elephant
Ready for the next one? Type y for yes or n for no. n
You were able to name 5 animals. Here's your list:
['bird', 'dog', 'cat', 'mouse', 'elephant']
请注意,这个程序的写法要求我们每次都要回答是否要添加一个动物。这是因为只要我们回答“是”,条件就会继续运行。但一旦我们回答“否”,程序就会结束,给我们动物列表和我们能够命名的数量。
现在,让我们再看一个while循环。这一次,我们将检查条件是否为True。看一下以下代码片段:
ch10_whileLoop2.py
while True:
num = int(input('Please enter an integer 0 through 9\. '))
if num in range(0, 10):
print(num)
else:
print('That\'s not in range. ')
break
在前面的算法中,控制流规定我们反复要求一个介于0和9之间的数字。只要我们给它一个介于0和9之间的数字,程序就会继续询问,直到我们犯错为止。这是因为只要我给它一个介于0和9之间的数字,它就会继续为True。让我们看一下这个的一个示例输出:
Please enter an integer 0 through 9\. 0
0
Please enter an integer 0 through 9\. 1
1
Please enter an integer 0 through 9\. 2
2
Please enter an integer 0 through 9\. 3
3
Please enter an integer 0 through 9\. 4
4
Please enter an integer 0 through 9\. 39
That's not in range.
请注意,程序一直重复询问同一个问题。有时如果用户不知道如何打破循环,这是没有帮助的。我们可以在我们的陈述中潜在地添加一行,这样它会问问题但提供一个提示。看看编辑后的代码:
ch10_whileLoop3.py
while True:
num = int(input('Please enter an integer 0 through 9\. Tired? Type a number out of range. '))
if num in range(0, 10):
print(num)
else:
print('That\'s not in range. ')
break
正如你所看到的,我们现在告诉用户,如果他们厌倦了提供范围,那么他们可以通过提供范围之外的输入来选择退出。虽然这个例子似乎不是很有用,但想想你可以有多少应用。例如,这种类型的算法可以用于纸牌游戏。你也可以使用类似这样的东西来检查输入是否与现有列表匹配。
现在,让我们再次看一下函数,但现在结合一些我们的循环并添加一些功能。
重新审视函数
如果你还记得第八章,Python 简介,我们看过内置函数,但我们也看过如何定义我们自己的函数。现在我们将讨论函数和循环中的参数,深入探讨 Python 中控制流的工作方式。
让我们考虑一下涉及范围的问题。范围需要两个参数:最小值和最大值。然而,在 Python 中,我应该注意到你可以只给一个参数,这样就假定你的最小值是0。例如,如果我写range(8),那就相当于range(0, 8)。看看如果你在 Python shell 中输入range(2)会发生什么:
图 10.3 - Python 范围解释与一个参数
在图 10.3中,你可以看到程序将代码解释为range(0, 2)。但假设你总是在改变你的范围。想想我们之前写的范围算法。我们现在将使用一个函数来重新编写它。这个函数现在也在里面有一个for循环:
ch10_functions1.py
minNum = int(input('What\'s your minimum number? '))
maxNum = int(input('What\'s your maximum number? '))
def myNumbers(minNum, maxNum):
myList = []
for num in range(minNum, maxNum + 1):
myList.append(num)
print(myList)
myNumbers(minNum, maxNum)
请注意,我们是根据用户输入调用函数的。当此程序运行时,它根据该输入调用函数。我们将在一秒钟内重新访问并通过在算法中调用多个范围来运行程序,但是看看前面的片段给出了什么输出:
What's your minimum number? 3
What's your maximum number? 9
[3, 4, 5, 6, 7, 8, 9]
请注意,我们还调整了for循环中的范围中的最大数字,以包括提供的顶部数字。这样我们就可以得到完整的数字列表。
现在,让我们完全取消输入。我们将使用不同的范围多次调用函数在算法中。看看更新后的片段:
ch10_functions2.py
def myNumbers(minNum, maxNum):
myList = []
for num in range(minNum, maxNum + 1):
myList.append(num)
print(myList)
myNumbers(4, 9)
myNumbers(1, 3)
myNumbers(9, 17)
最后三个语句是我们调用函数的地方。由于我们定义了函数以接受两个参数,它使用这两个参数来运行函数。因为我们调用了函数三次,所以应该看到三个列表作为输出。让我们看一下:
[4, 5, 6, 7, 8, 9]
[1, 2, 3]
[9, 10, 11, 12, 13, 14, 15, 16, 17]
正如您所看到的,我们在单独的行上打印了每个范围。这是函数可以为我们做的最有用的事情之一。例如,如果我们正在处理图像,我们可以使用库来创建形状,然后定义一个带有改变一些参数的循环的函数。通过一个函数和几个循环,我们可以根据调用函数和使用一些循环在不同位置创建多个具有不同半径的圆。
函数也不限于两个参数。我们可以有多个参数并在函数内定义它们。让我们看一个使用三个参数的函数:
ch10_functions3.py
def menu(appetizer, entree, dessert):
print('Your appetizer is %s.' %(appetizer))
print('You entree is %s.' %(entree))
print('Your dessert is %s.' %(dessert))
menu('street tacos', 'chilaquiles', 'sopapillas')
在这种情况下,我们正在使用已经给定的值调用函数。我喜欢,我的意思是我非常喜欢,墨西哥食物。因此,那个菜单会让我非常开心!这是输出的样子:
Your appetizer is street tacos.
You entree is chilaquiles.
Your dessert is sopapillas.
正如您所看到的,该函数在print语句中使用了每个参数。 “%s”语句用于让程序知道值将被替换的位置。 “%()”语句让程序知道从调用函数中获取哪个值。
现在,让我们看看如果我们想从用户那里获取输入的代码:
ch10_functions4.py
appetizer = input('What would you like as an appetizer? ')
entree = input('What would you like as an entree? ')
dessert = input('what would you like for dessert? ')
def menu(appetizer, entree, dessert):
print('Your appetizer is %s.' %(appetizer))
print('You entree is %s.' %(entree))
print('Your dessert is %s.' %(dessert))
menu(appetizer, entree, dessert)
正如您所看到的,我们的定义和参数是重复的。我们使用input语句从程序用户那里获取信息,然后打印出语句。现在在 Python shell 中看起来是这样的:
图 10.4 - 具有三个参数的函数中的用户输入
正如您所看到的,前三行接受用户输入,然后最后三行将输入合并到函数定义中。这取决于您要创建的内容。例如,如果您要为商店构建在线菜单,您需要用户输入,但您还希望能够确认购买。如果您想要确认借出电子书等在线图书馆,也是如此。我们在算法中经常使用确认语句。查看这些之后,去看看一些您喜欢的网站,看看基于用户输入的确认语句在哪里。您会发现使用非常广泛。函数使我们能够简化该过程。
在我们继续之前,让我们使用几个算法,使用不同的循环和函数提供相同的信息。请记住,我们正在研究迭代,因为控制流意味着顺序。迭代、函数、范围等是我们告诉程序如何响应算法中的指令以及何时重复或继续执行程序的方式。
让我们看一个打印给定用户最大输入的三倍的函数:
ch10_functions5.py
numItem = int(input('What is your maximum number for the list of triples? '))
def cost(numItem):
while numItem > 0:
print(numItem * 3)
numItem -= 1
cost(numItem)
请注意,while循环和函数定义取决于用户输入。然后,程序将打印用户提供的值的三倍,然后减少该数字一次并找到该数字的三倍。让我们看看这意味着什么:
What is your maximum number for the list of triples? 4
12
9
6
3
正如你所看到的,程序找到了4的三倍,即12,然后找到了3的三倍,即9,依此类推。它停止是因为我们告诉它在数字大于0时运行循环。但要记住,我们也可以将它们添加到一个列表中,并且我们可以使用for循环。
让我们看一个类似的程序,它使用了一个函数,但是使用了for循环而不是while循环,并使用了range:
ch10_functions6.py
numItem = int(input('What is your maximum number for the list of triples? '))
myList = []
def cost(numItem):
for x in range(1, numItem + 1):
newNum = 3 * x
myList.append(newNum)
print(myList)
cost(numItem)
注意我们定义了一些更多的东西,并在算法中添加了一个空列表。虽然代码行数多了一些,但它基本上与先前的代码做了相同的事情。还要注意,我们从1开始范围;否则,它也会在列表中包括0。看一下这个算法的输出:
What is your maximum number for the list of triples? 4
[3, 6, 9, 12]
我们有相同的信息,但是按照从小到大的顺序排列,并且放在一个列表中。从我们的算法中,发生事情的顺序很重要,当然。我们首先收集了用户输入。然后算法定义了一个空列表,然后定义了一个函数。函数然后使用了一个for循环,该循环使用输入创建一个范围,并在数字范围内进行迭代。然后每次迭代都被添加到列表中。最后,算法打印了列表。
正如你所看到的,有多种方法可以获得相同的信息。我们只需要看看哪种方法最适合我们的场景,如何组织信息以便程序可以读取它,并编写一个算法,该算法可以在运行程序时组织信息。
总结
在本章中,我们通过查看for循环、range、while循环和函数来讨论控制流和顺序。控制流指的是程序读取算法的顺序。通常在 Python 中,一行代码紧跟着一行代码被读取。在本章中,我们学会了如何控制这个顺序。具体来说,我们学会了可以通过迭代数据来实现这一点。以下是一些重要的要点需要记住:while循环在条件满足的情况下运行,for循环迭代一个序列(字符串、数字、列表、字典、集合或元组),range用于创建一系列数字。
我们还学会了在创建条件、定义函数和设计算法时可以将这些东西结合起来。继续牢记的最重要的事情是顺序很重要,所以我们需要小心地定义必要的变量以及如何编写算法,以便它们不会无限运行或在应该之前就中断。控制流很重要,这样我们的算法才能正常工作而没有错误。
在下一章中,我们将利用迄今为止所学到的知识,完成在多个学科中解决挑战时的计算思维过程。
第十一章:在简单挑战中使用计算思维和 Python
在本章中,我们将再次审视计算思维过程,并将其应用于设计算法,帮助我们解决各种场景和问题。随着本书第二部分,应用 Python 和计算思维的结束,我们将结合有关 Python 功能的一些知识与计算思维过程,解决这些问题。
在本章中,我们将涵盖以下主题:
-
定义问题和 Python
-
分解问题并使用 Python 功能
-
概括问题并规划 Python 算法
-
设计和测试算法
通过本章结束时,您将能够设计最适合所呈现场景的算法。您还将能够确定最符合所呈现问题的 Python 函数,并概括您的解决方案。
技术要求
您需要安装最新版本的 Python 才能运行本章中的代码。
本章中使用的源代码可以在 GitHub 存储库中找到:github.com/PacktPublishing/Applied-Computational-Thinking-with-Python/tree/master/Chapter11
定义问题和 Python
我们将深入探讨我们的第一个场景。您正在为您设计的产品发起一项活动。您正在寻找总额为 100,000 美元的承诺投资。您希望创建一个算法,保存有关为您制作的承诺的信息,同时提供有关每个承诺提供的产品的信息。在编写算法之前,您需要确定一些事项:
-
您的活动中将运行多少种类型的承诺?
-
是否会有仅捐款的承诺? 捐款是否会得到任何东西,比如贴纸、电子证书或其他产品?
-
每个承诺将给予承诺者什么?
所有这些问题都将帮助我们进行规划。但我们还必须考虑其他事项,比如我们从算法中需要什么。它仅仅是列出承诺的数量,还是也将用于与库存进行核对或创建库存? 它会从每个级别可用的一定数量的承诺中倒数吗?每次有人承诺时,它会自动更新吗?
正如您所知,当我们面对问题时,关键是要确定问题的实质。我们需要为问题设定参数,以便提供解决方案。在本节中,我们首先要回答很多这些问题。为了做到这一点,我们需要分解问题,并确定 Python 中哪些功能可以帮助我们解决这个问题。
分解问题并使用 Python 功能
我们可以创建一个用于设计这些承诺的算法。我们将设计一个算法,告诉我们根据一定数量的承诺需要每种类型的商品数量。
例如,假设我们有三个承诺层级和一个仅捐款的承诺。对于最高层,让我们称之为层级 1,您将获得以下内容:
-
将出售的商品以 20%的折扣翻倍
-
商品的配件捆绑包,包括四件商品
-
商品和配件的携带盒
-
网站的 1 年会员资格
中层,或层级 2,将为您提供以下商品:
-
以 20%的折扣出售的商品
-
商品的携带盒
-
网站的 6 个月会员资格
最低层,或层级 3,将为您提供以下商品:
-
以 20%的折扣出售的商品
-
商品的携带盒
假设我们想要一个算法,根据我们允许每个层级的承诺数量,为我们提供我们需要的物品数量。第四层,即仅捐赠的层级,不会影响我们的算法,因为这个算法将仅用于确定我们需要的每种物品的数量,具体取决于承诺的数量。
但事情并不像数字那么简单。假设您需要100,000 的目标。
您已经确定您的物品的零售价格为$800。以下是每个物品的成本:
-
$640 是物品本身的价格(给定 20%的折扣,仅适用于第 1、2 和 3 层)
-
$100 的配件(仅适用于第 1 层)
-
$40 的携带箱(适用于第 1、2 和 3 层)
-
会员费用将以每月35)
对于第 1 层,承诺需要至少$1,540:
-
640 × 2 = 1280
-
12 × 10 = 120
-
1280 + 120 + 100 + 40 = 1540
对于第 2 层,承诺需要至少$740:
-
640 + 40 = 680
-
6 × 10 = 60
-
680 + 60 = 740
对于第三层,承诺需要至少$680,即 640 + 40 = 680。
现在我们必须弄清楚每个层级将有多少可供购买。但首先,让我们将一些这些数字四舍五入:第 1 层将是800,第 3 层将是$700。
我们可以编写一个算法来让我们知道每个层级需要多少承诺。但在我们这样做之前,让我们谈谈 Python 和我们可以使用的功能。我们可以首先创建第三层的承诺,使其成为一个父组。然后我们可以创建两个子类,第 1 和第 2 层,它们具有略有不同的特征和成本。我们要问自己的问题是,我们是否需要在算法中详细说明,还是只需添加每个层级的成本/价值。
这是一个很好的问题。答案是取决于您需要这个算法的用途。如果您只是想进行早期简单的计算,并且需要一个可以根据当前销售/承诺定期运行的算法,那么您可以创建一个简单的字典或函数。但是,如果您希望将算法用于多种目的,或者需要能够稍后调整一些成本,那么您可能希望将所有细节编码到算法中。
*您为什么需要这个?*看一些成功的可用活动。通常,这些活动的原始承诺会售罄。然后新的承诺层会变得可用,您可能希望调整这些层的价格点。例如,承诺可能不再是物品的 20%折扣,而是 15%的折扣。其他变化可能发生,例如由于库存限制而出售装饰品而不是配件等。
您的算法越详细,从长远来看,您可以做的事情就越多。但是,如果您不需要所有这些信息,那么您可能不希望花费太多时间创建算法。
让我们开始处理这个算法。看一下下面显示的片段:
ch11_pledgesA.py
import math
tier1 = 1600
tier2 = 800
tier3 = 700
perc = int(input("What percentage of the 100,000 do you
expect will be Tier 1? Type a number between 1 and
100\. "))
percentage = perc/100
Tier1 = (100000*percentage)/1600
totalTier1 = math.ceil(Tier1)
print("You will need to sell %s Tier 1 pledges if you want
%s percent of your sales to be in Tier 1."
%(totalTier1,perc))
让我们看一下我们在前面片段中使用的一些东西。我们必须导入math模块,以便我们可以使用math.ceil()数学函数。我们使用这个函数来四舍五入所需的第 1 层承诺的数量。这是因为如果我们向下取整,我们将无法达到所需的百分比。通过这样做,我们找到了我们需要覆盖百分比的最小整数。
此外,我们在我们的print语句中使用了%s占位符来调用我们的字符串,然后说明要使用的字符串。
当我们运行该程序时,输出如下所示:
What percentage of the 100,000 do you expect will be Tier 1? Type a number between 1 and 100\. 45
You will need to sell 29 Tier 1 pledges if you want 45 percent of your sales to be in Tier 1.
请注意,我们的print语句使用了用户输入的百分比,部分是为了确保信息与预期相匹配。要想让 45%的资金来自 Tier-1 承诺,我们至少需要出售 29 个 Tier-1 承诺。如果我们运行数学来验证这一点,我们会发现这个信息是正确的:
正如您所看到的,如果我们出售 29 个 Tier-1 承诺,我们将获得略多于 100,000 的 45%。
但是,假设您还希望算法告诉您基于 Tier-1 承诺数量需要多少物品。让我们看一下调整后的算法:
ch11_pledgesB
import math
tier1 = 1600
tier2 = 800
tier3 = 700
perc = int(input("What percentage of the 100,000 do you
expect will be Tier 1? Type a number between 1 and
100\. "))
percentage = perc/100
Tier1 = (100000*percentage)/1600
totalTier1 = math.ceil(Tier1)
print("You will need to sell %s Tier 1 pledges if you want
%s percent of your sales to be in Tier 1."
%(totalTier1,perc))
print("You will need %s items, %s accessories kits, %s
carrying cases, and %s year-long memberships."
%(totalTier1*2, totalTier1, totalTier1, totalTier1))
请注意,我只添加了一个print语句。以这种方式使用这个算法有利有弊。在这种情况下,我只输出每个层级的数字。我没有在算法中保存需要的物品数量。如果我们想要为将来的参考保存这些信息,我们需要调整如何获取这些信息以及如何在算法中保存它。
对于这个算法,输出如下:
What percentage of the 100,000 do you expect will be Tier 1? Type a number between 1 and 100\. 45
You will need to sell 29 Tier 1 pledges if you want 45 percent of your sales to be in Tier 1.
You will need 58 items, 29 accessories kits, 29 carrying cases, and 29 year-long memberships.
请注意,我们得到了想要的信息。我们将需要58个物品,29个配件套件,29个携带箱,以及29个年度的会员资格。同样,如果我们只做一次性的事情,或者我们不希望有任何变化,这将是有帮助的。但让我们明确一点,这几乎从来不是这种情况。我们将希望进行更改。我们还需要根据层级 2 和层级 3 的选择来了解信息。那么我们能做什么呢?
首先,我们需要保存我们的数字。因此,我们需要为物品、配件套件、携带箱和两个会员资格添加一些变量,一个是年度的,一个是 6 个月的。我们还需要决定我们希望其余的承诺如何分配。我们希望其他百分比在层级 2 和 3 之间平均分配吗? 我们希望剩下的三分之一是层级 2,剩下的三分之二是层级 3 吗? 让我们选择这些数字。现在我们的情况是这样的:
-
Tier-1 的百分比是由用户在程序运行时选择的。
-
Tier-2 的百分比将是剩余百分比的三分之一。
-
Tier-3 将是剩余百分比的三分之二。
让我们教给算法。以下文件包含完整的、不间断的代码。我们添加了一些文本来解释某些部分,如下所示:
ch11_pledgesC.py
tier1 = 1600
tier2 = 800
tier3 = 700
perc = int(input("What percentage of the 100,000 do you
expect will be Tier 1? Type a number between
1 and 100."))
percTier1 = perc/100
percTier2 = (100-perc)/3/100
percTier3 = (100-perc-percTier2)/100
请注意,在下面的片段中,我们添加了一些变量,如totalTier1、itemsTier1、accTier1和cases1。这些变量将帮助我们保存每个层级订购的数量。我们将对层级 2 和 3 做同样的事情:
Tier1 = (100000*percTier1)/1600
totalTier1 = math.ceil(Tier1)
itemsTier1 = totalTier1*2
accTier1 = totalTier1
cases1 = totalTier1
yearMemb = totalTier1
Tier2 = (100000*percTier2)/800
totalTier2 = math.ceil(Tier2)
itemsTier2 = totalTier2
cases2 = totalTier2
sixMemb = totalTier2
Tier3 = (100000*percTier3)/700
totalTier3 = math.ceil(Tier3)
itemsTier3 = totalTier3
cases3 = totalTier3
totalItems = itemsTier1 + itemsTier2 + itemsTier3
totalAccessories = accTier1
totalCases = cases1 + cases2 + cases3
print("You will need to sell %s Tier 1 pledges if you want
%s percent of your sales to be in Tier 1."
%(totalTier1, perc))
print("You will need %s Tier 2 pledges and %s Tier 3
pledges to meet or exceed your $100,000 funding goal."
%(totalTier2, totalTier3))
虽然我们还没有打印出总物品或总案例的详细信息,但现在我们已经将它们保存到变量中。现在我们的输出看起来是这样的:
What percentage of the 100,000 do you expect will be Tier 1? Type a number between 1 and 100\. 50
You will need to sell 32 Tier 1 pledges if you want 50 percent of your sales to be in Tier 1.
You will need 21 Tier 2 pledges and 72 Tier 3 pledges to meet or exceed your $100,000 funding goal.
我们应该注意到,我们超过了我们的筹资目标,因为我们一直在四舍五入。也就是说,我们使用1,600。对于百分比,我们一直在四舍五入。所有这些加起来将使我们的总额超过 100,000。
让我们再扩展一下算法。以下只是我们已经看到的算法中的新片段,其中包含了我们需要的物品的总数:
print("These percentages are equivalent to %s total items,
%s total cases, %s accessories kits, %s year-long
memberships, and %s six-month memberships." \
%(totalItems, totalCases, totalAccessories,
yearMemb, sixMemb))
请注意,我们现在可以在我们的print函数中调用我们添加的那些变量,以获取我们库存所需的数量。如果我们没有在算法中定义这些项目,我们将无法获得这些详细信息。
在我们之前的片段中,还有一些项目具有完全相同的价值。然而,我们仍然用不同的变量定义了它们。例如,cases2 = totalTier2和sixMemb = totalTier2。尽管两者的值相同,但我们希望将它们分开识别。也许现在这并不重要,但以后也许我们会用完案例。那时我们只想改变案例的价值,而不是 6 个月的会员资格。
因为它们已经分开,我们可以改变一个而不影响另一个。让我们看看新的print语句的输出是什么样的:
These percentages are equivalent to 157 total items, 125 total cases, 32 accessories kits, 32 year-long memberships, and 21 six-month memberships.
看到这一点,你可能会意识到在第一层有一个携带箱,但有两个物品,这就是为什么这两个数字不同的原因。配件和一年的会员只发生在第一层,所以这两个数字相同是有道理的。6 个月的会员只适用于第二层,所以这个数字与第二层的承诺数量相匹配。
当我们进一步考虑这个问题时,我们可能会意识到我们可能希望以不同的方式保存信息。也许我们不是问用户他们想要多少百分比的第一层承诺,而是问他们有多少总物品,然后根据这个来分解层。所有这些都是可能的,所以我们如何定义问题是至关重要的。我们如何保存信息或者从用户那里请求输入也同样重要。分解问题只是创建和设计我们需要的算法过程的一部分。
在这一部分,我们学会了如何解决具有多种解决方案的问题,这取决于我们的目标。当我们定义问题时,我们通常也确定我们需要的变量,并确定根据算法的输出需要什么样的函数是最有用的。除非我们有一个非常简单和直接的问题,否则问题的分解和定义对于成功定义算法至关重要。
现在让我们在下一节中看一下概括问题。
概括问题和规划 Python 算法
在上一节中,我们正在使用一个专为资金筹集活动设计的算法。我们看到的算法是特定于问题的。
现在让我们尝试概括这个问题,并了解我们如何可能设计一个不同的算法。*为什么我们需要那个?*把它看作一个模板。如果你为初创公司运行多个筹资活动,你可能希望创建一个通用算法,然后根据每个活动的需求进行调整,而不是每次都要重新开始每个活动。
你需要设定一些明确的参数并做出一些决定。为了使这种情况对本书的目的来说更容易处理,我们将限制一下我们的选择:
-
每个活动将有 3 到 5 个承诺层,不包括仅捐赠。
-
每个层都会要求每个层所需的物品。
-
每个层选项都将有一个固定的百分比。
如果有三个层,第一层将占承诺的 50%,第二层将占 30%,第三层将占 20%。如果有四个层,第一层将占承诺的 40%,第二层将占 30%,第三层将占 20%,第四层将占 10%。如果有五个层,第一层将占承诺的 40%,第二层将占 30%,第三层将占 15%,第四层将占 10%,第五层将占 5%。
看一下下面的图表,显示了各层的分解情况:
图 11.1 - 层百分比分解
因为我们使用一个算法来处理许多可能的情况,我们将逐步分解该算法。完整的算法可以在 GitHub 上的ch11_pledgesTemplate.py文件中找到。
在这个第一个片段中,我们要求初始输入,然后保存我们的百分比:
import math
numberTiers = int(input("How many tiers of pledges will you
offer? Enter a number between 3 and 5 inclusive. "))
#Number of tiers percentages
if numberTiers == 3:
tier1 = .50
tier2 = .30
tier3 = .20
elif numberTiers == 4:
tier1 = .40
tier2 = .30
tier3 = .20
tier4 = .10
elif numberTiers == 5:
tier1 = .40
tier2 = .30
tier3 = .15
tier4 = .10
tier5 = .05
else:
print("Please try again and enter the numbers 3, 4,
or 5\. ")
正如你所看到的,在我们要求输入后有三个条件。请注意,我们将输入转换为整数。这很重要,否则条件将运行,但else条件也将运行。
前面的片段不会给我们任何输出,除了要求输入。因此,在我们的下一个片段之后,我们将展示更多的输出。
重要提示:
请注意此代码中的注释的使用。由于代码的长度,我们将使用注释来找到需要编辑的代码部分。在所有代码中使用注释总是一个好主意,尤其是在冗长的代码中。否则,找到我们想要更改的特定行可能会非常棘手。
一旦我们有了层级的数量,我们将需要知道每个层级中物品的数量。我们需要询问每个层级选择了多少物品。让我们看看前面代码的继续部分:
#Number of items per tier
if numberTiers == 3:
numTier1Items = int(input("How many items will be
provided for a Tier 1 pledge? "))
numTier2Items = int(input("How many items will be
provided for a Tier 2 pledge? "))
numTier3Items = int(input("How many items will be
provided for a Tier 3 pledge? "))
elif numberTiers == 4:
numTier1Items = int(input("How many items will be
provided for a Tier 1 pledge? "))
numTier2Items = int(input("How many items will be
provided for a Tier 2 pledge? "))
numTier3Items = int(input("How many items will be
provided for a Tier 3 pledge? "))
numTier4Items = int(input("How many items will be
provided for a Tier 4 pledge? "))
请注意,我们只显示了层级为 3 或 4 时的条件。代码文件还将包含 5 个层级的信息,但它遵循了前面代码中显示的内容。请注意,该算法要求输入每个层级。当我们需要处理数字和百分比时,这将是重要的。
此外,我们可以包括一个else语句,如果输入中有错误,它允许我们再次提出问题。如果您愿意,可以将这些类型的条件添加到现有模板中。现在,我们将继续获取用户需要的下一部分信息,例如每个层级的价格点。
现在让我们回想一下我们可能需要什么。我们将需要每个层级的价格点,这也将是模板类型算法的输入请求。由于每个活动的价格点都会有所不同,我们需要让用户输入。输入行将看起来与先前显示的片段非常相似。以下是 3 个层级的情况:
#Price points for each Tier
if numberTiers == 3:
priceTier1 = int(input("What is the price point of Tier
1? Enter dollar amount without dollar sign. "))
priceTier2 = int(input("What is the price point of Tier
2? Enter dollar amount without dollar sign. "))
priceTier3 = int(input("What is the price point of Tier
3? Enter dollar amount without dollar sign. "))
再次注意,我们使用注释来分隔代码的每个部分。您可以看到,我们正在添加有关每个承诺级别的收费信息。然后代码继续为 3、4 或 5 个层级执行此操作。
正如之前讨论的,您可能还想测试错误或提供在用户添加错误后继续运行代码的替代方法。我们在这段代码中没有处理这些错误,但可以添加以改善用户体验。您可以看到,我们已经开始研究如何将此算法推广到多种情况。
在这种情况下,我们正在为多种用途进行概括。但是在本书中,我们使用了许多算法并看到了许多情景,其中模式的概括要简单得多。概括可以是编写一个变量方程这样简单的事情。或者可以是为多种情况和条件创建算法。这就是为什么确定我们的问题是什么以及我们确切想要实现什么是很重要的。
在本节中,我们看了如何何时从用户那里获取输入。我们还通过定义变量来存储输入并在我们的方程式中使用它,以便算法输出必要的信息。
为了完成算法,我们将进入下一部分,重点是设计和测试算法。
设计和测试算法
在上一部分中,我们从用户那里得到了很多输入,因为我们正在创建一个用作许多活动模板的算法。现在我们需要以各种方式使用这些输入。到目前为止,我们已经有了这些:
-
承诺层级的数量
-
不同层级的百分比分布
-
每个层级的物品数量
-
每个层级的成本
现在我们可以利用所有这些信息做一些事情。首先,让我们谈谈我们可能想要的东西。我们可能想测试出售特定数量的层级可以赚多少钱。我们还可以根据筹款目标来分解我们需要多少个每个层级,就像本章的第一部分所做的那样。
什么会最有帮助? 嗯,这取决于你的需求。我要说的是,我想根据筹资目标来分解。我想知道我需要提供每种承诺类型的数量。所以现在我必须想办法从我已经定义的变量中获取这些信息。
我们也需要在这里有三个条件。因为每个等级类型的变量和数量都不同,我们需要确保我们考虑到这些信息。首先让我们考虑三个等级。根据筹资目标,以下是一些有用的输出:
-
要提供的每个等级的承诺数量
-
每个等级需要存货的物品数量
那我们该如何计算呢?
假设我们的筹资目标是 50,000 美元,假设一级的成本是 500 美元。那么,我们可以采取以下步骤来找到需要的一级承诺数量:
-
将筹资目标乘以百分比,即,50,000 × 0.50 = 25,000。
-
将得到的数字除以承诺的成本,即,25,000 ÷ 500 = 50。
这意味着我们需要发布 50 个一级承诺。现在假设用户输入了一级有3个物品。那么,这意味着 50 × 3 = 150 个物品。
现在让我们在我们的代码中看看。记住,这是与之前片段相同的文件(ch11_pledgesTemplate.py)。我们将继续讨论并使用代码的部分:
#Breakdown of number of Tiers based on funding goal
fundGoal = int(input("What is the funding goal for this
campaign? Enter dollar amount with no symbols. "))
if numberTiers == 3:
Tier1Total = math.ceil(fundGoal*tier1/priceTier1)
Tier2Total = math.ceil(fundGoal*tier2/priceTier2)
Tier3Total = math.ceil(fundGoal*tier3/priceTier3)
print("For a funding goal of %s with %s tiers, you'll
need %s Tier 1 pledges, %s Tier 2 pledges, and %s
Tier 3 pledges. " % (fundGoal, numberTiers,
Tier1Total, Tier2Total, Tier3Total))
在前面的片段中,我们有一个print函数,其中包含每个等级的承诺数量,但它们也保存为我们条件语句中的函数。请注意,我们现在将在这里得到一些输出。我们将从这段代码中得到我们需要的承诺数量,但不是每个等级的物品数量。我们很快会添加这部分内容。现在,当我们运行程序时,输出看起来是这样的:
How many tiers of pledges will you offer? Enter a number between 3 and 5 inclusive. 3
How many items will be provided for a Tier 1 pledge? Enter a number between 1 and 3 inclusive. 3
How many items will be provided for a Tier 2 pledge? Enter a number between 1 and 3 inclusive. 2
How many items will be provided for a Tier 3 pledge? Enter a number between 1 and 3 inclusive. 1
What is the price point of Tier 1? Enter dollar amount without dollar sign. 500
What is the price point of Tier 2? Enter dollar amount without dollar sign. 400
What is the price point of Tier 3? Enter dollar amount without dollar sign. 350
What is the funding goal for this campaign? Enter dollar amount with no symbols. 50000
For a funding goal of 50000 with 3 tiers, you'll need 50 Tier 1 pledges, 38 Tier 2 pledges, and 29 Tier 3 pledges.
正如你所看到的,我们现在知道我们需要列出 50 个价值 500 美元的一级承诺,38 个价值 400 美元的二级承诺,以及 29 个价值 350 美元的三级承诺,以达到我们的筹资目标。现在我们必须根据每个等级提供的物品数量来计算每个等级需要多少物品。代码如下:
if numberTiers == 3:
Tier1Total = math.ceil(fundGoal*tier1/priceTier1)
Tier2Total = math.ceil(fundGoal*tier2/priceTier2)
Tier3Total = math.ceil(fundGoal*tier3/priceTier3)
print("For a funding goal of %s with %s tiers, you'll
need %s Tier 1 pledges, %s Tier 2 pledges, and %s
Tier 3 pledges. " % (fundGoal, numberTiers,
Tier1Total, Tier2Total, Tier3Total))
Tier1Items = numTier1Items*Tier1Total
Tier2Items = numTier2Items*Tier2Total
Tier3Items = numTier3Items*Tier3Total
print("For %s Tier 1 pledges, you'll need %s items. For
%s Tier 2 pledges, you'll need %s items. For %s
Tier 3 pledges, you'll need %s items. "
%(Tier1Total, Tier1Items, Tier2Total, Tier2Items,
Tier3Total, Tier3Items))
正如你所看到的,现在我们有另外三个数学方程和一个print语句,为我们分解了信息。我们将得到每个等级的承诺数量,以及每个等级所需的物品数量。如果你想从这个模板中获得更多信息,你可以包括本章第一个示例中的一些内容,那里我们分解了每个承诺的物品类型。我们将把这个挑战留给你。
现在,对于三个等级和 50,000 美元的筹资目标,我们的最终输出如下:
How many tiers of pledges will you offer? Enter a number between 3 and 5 inclusive. 3
How many items will be provided for a Tier 1 pledge? 3
How many items will be provided for a Tier 2 pledge? 2
How many items will be provided for a Tier 3 pledge? 1
What is the price point of Tier 1? Enter dollar amount without dollar sign. 500
What is the price point of Tier 2? Enter dollar amount without dollar sign. 400
What is the price point of Tier 3? Enter dollar amount without dollar sign. 350
What is the funding goal for this campaign? Enter dollar amount with no symbols. 50000
For a funding goal of 50000 with 3 tiers, you'll need 50 Tier 1 pledges, 38 Tier 2 pledges, and 29 Tier 3 pledges.
For 50 Tier 1 pledges, you'll need 150 items. For 38 Tier 2 pledges, you'll need 76 items. For 29 Tier 3 pledges, you'll need 29 items.
正如你所看到的,我们不仅得到了我们需要的信息,而且还设置了变量,以便在需要时使用这些信息。回想一下我们之前讨论过的章节和笔记,让我们试着确定我们现在如何保存这些信息。
首先想到的是,我们可以创建一个字典来为我们存储信息。如果我们这样做,那么我们可以从那个字典中调用我们需要的信息,比如一个等级的物品数量。如果需要,我们还可以调整键值对,而不必重新输入整个内容。假设我们一开始的一级成本是 500 美元,但现在我们需要它是 600 美元,而其他等级不会改变。那么我们只需调整那一个值。
这种情况可以让你探索我们讨论过的 Python 编程语言的许多功能。花些时间研究代码,然后进行一些调整,尝试根据不同的条件利用你的知识来改进它。
记住,我们总是面临可以用不同方式解释的问题情况。我们需要编写满足我们自己和客户需求的算法。有时,我们会直接从利益相关者那里得到澄清。其他时候,我们需要要求澄清和/或自己做一些假设。关键是我们要设计算法并记录我们的进展,这样我们就可以在不得到所需内容时调整、适应和更改我们的工作部分,而不必重新开始。
总结
在本章中,我们通过处理更复杂的情景和对该情景的解释,再次学习了计算思维过程。我们学会了如何分解所提供的问题,然后识别模式、概括它们,并设计算法。我们利用了书中学到的一些知识,编写了一个提供我们所需信息的算法。
计算思维过程帮助我们培养技能,使我们的算法规划变得更容易。通过走过这个过程,我们更多地了解了 Python 的能力和函数在特定情况下如何帮助我们。我们还学会了如何概括模式,有时是为了解决问题而创建简单的方程,但有时是创建可以帮助我们在多种情况下的算法,而不必每次重新创建它们。随着我们对 Python 的了解越来越多,在第二部分,应用 Python 和计算思维的最后一章中,我们对计算思维过程更加熟悉。
在第三部分,使用计算思维和 Python 进行数据处理、分析和应用中,我们将继续探讨 Python 的其他能力,以处理数据处理、分析和应用计算思维元素。在下一章中,我们将开始研究数据以及如何使用 Python 分析数据、创建可视化表示,并编写与实验数据配合的算法。
第三部分:使用计算思维和 Python 进行数据处理、分析和应用
数据无处不在。我们使用数据来决策政策,比如为学区提供多少资源,县或州的医疗保险计划预算有多大,我们在某个地区的住房费用是多少,以及房地产市场的趋势。数据也嵌入在我们与广告互动的方式中。简而言之,了解更多关于数据以及如何使用 Python 分析这些数据是一项非常重要的技能。
在本节中,您将了解与数据分析和其他应用相关的 Python 编程语言的高级功能,如加密、实验数据、数据挖掘和机器学习。我们将使用计算思维为许多现实世界的应用问题设计解决方案和算法,从语言和历史分析到一些机器学习应用。
本节包括以下章节:
-
[第十二章],在实验和数据分析问题中使用 Python
-
[第十三章],使用分类和聚类
-
[第十四章],在统计分析中使用计算思维和 Python
-
[第十五章],应用计算思维问题
-
[第十六章],高级应用计算思维问题
第十二章:在实验和数据分析问题中使用 Python
在本章中,我们将看看 Python 如何帮助我们使用专门用于数据分析和数据科学的算法和库来理解和分析数据。我们将首先介绍实验数据,然后转向使用两个主要库NumPy和pandas的算法。
在本章中,我们将涵盖以下主题:
-
定义实验数据
-
在 Python 中使用数据库
-
使用 Python 进行数据分析
-
使用额外的库进行绘图和分析
在本章结束时,您将能够定义实验类型、数据收集以及计算思维在设计模型和解决方案时的帮助。您还将学习如何使用数据库,特别是NumPy、pandas和Matplotlib,来帮助分析和显示数据。最后,您将能够设计算法,以帮助数据分析,以便从现有数据中学习。
技术要求
您需要安装最新版本的 Python 来运行本章中的代码。
您还需要安装一些库,包括NumPy、pandas、Matplotlib和Seaborn。
您可以选择使用集成环境来运行 Python,例如Anaconda,它可以简化库依赖关系,并有助于在笔记本中组织您的算法。
本章中使用的源代码可以在 GitHub 存储库中找到:github.com/PacktPublishing/Applied-Computational-Thinking-with-Python/tree/master/Chapter12
定义实验数据
我们终于到了本书的数据章节。我们都有自己的偏见和喜欢生活的领域。这是我的其中之一。数据如此重要的原因有很多,但让我们从这样一个事实开始,即数据、我们如何收集它、如何分析它以及如何呈现它对我们的日常生活有巨大影响。
在编写算法以显示信息时,我们有责任以尽可能少的偏见方式分享数据,确保我们的数据包容和代表我们的社区和人民。在我们深入讨论这个话题之前,我想确保我说过这些话。对我来说,这就是我如何爱上代码和 Python 的方式。
在本节中,我们将讨论实验数据,定义它是什么,以及在处理实验数据时使用的关键术语。
现在,让我们开始吧。实验数据是一个从科学和工程中得到应用的术语。然而,我们也在其他领域使用实验数据,比如教育、公民事务等。实验数据包括方法论,我们如何测量数据,我们正在进行的实验的设计,以及我们如何呈现我们从这些设计中收集和分析的数据。在本章中,我们不是在设计实验。我们将专注于如何使用 Python 来分析现有数据。但是重要的是要注意,如果我们对实验有发言权,我们需要确保公平地设计它。
如果实验设计和数据收集方法是合理的,那么我们使用和分析的数据将比如果我们从一个有偏见的实验开始要更有帮助。虽然我们永远无法完全消除偏见,但作为研究人员和开发人员,我们有责任以尽可能少的偏见方式呈现数据。*为什么这很重要?*想想所有基于数据、图表和实验呈现的信息而发生的政策变化。如果实验从一开始就有偏见,结果将导致可能无法充分包容社区需求的政策,例如。
在处理数据时,我们总是在使用计算思维元素。在解决问题时,我们必须定义问题是什么,我们想要研究什么,我们想要如何衡量它,我们将如何能够创建和推广模式,以及我们需要使用什么算法来产生数据的最佳表示。我们在数据分析中所做的一切都受益于我们使用计算思维元素。
数据科学是STEM领域中不断增长的领域。2017 年,它被称为美国增长最快的领域。美国劳工统计局表示,预计到 2026 年,数据科学和统计领域将新增 1150 万个新工作岗位。目前,可用的工作岗位比合格的候选人更多。
现在,让我们看看 Python 如何让我们处理数据。
在实验数据中,我们希望使用自变量、因变量和控制变量收集信息:
-
自变量是由研究人员改变或控制的变量。
-
因变量是由研究人员测量或测试的变量;因变量依赖于自变量。
-
控制变量是实验中必须保持不变的变量或因素。
让我们看一个实验中这些变量的简单例子。一个常见的例子涉及植物生长的研究。自变量可以是一个或多个变量。对于我们的例子,我们的自变量将是我们添加到植物中的肥料量。植物生长将是一个因变量。对照组将不会得到任何肥料,只有水。因此,在我们的实验中,假设我们有五棵植物要测量。一棵植物只会得到水。那就是我们的对照组。其他四棵将添加不同水平的肥料。那些是我们的实验植物。生长是依赖于肥料的数量的。
设计实验时,我们希望有三个条件成立:可靠性、有效性和可推广性。以下是这些条件的含义:
-
可靠性与测量的一致性有关。这意味着如果我们模仿条件,我们的结果应该是可靠地相似的。
-
有效性与实验是否测量了其意图测量的内容有关。
-
可推广性与结果是否能够推广并应用到其他环境有关。
关于实验,我们还可以深入了解更多细节和深度,但在本章中,我们只关注在获得数据后会发生什么。了解这些基本术语对于我们参与实验设计是很重要的。作为开发人员,根据我们的角色,可能会出现这种情况。例如,在初创公司中,每个人可能都参与产品开发的各个方面。因此,在这些情况下,我们可能会参与实验以及随后的分析算法。
现在,让我们继续讨论数据分析可以做什么,以及 Python 库如何帮助我们实现分析实验结果所需的内容。
使用 Python 中的数据库
在这一部分,我们将看一些可以与 Python 编程语言一起使用的库和包。我们有时使用包和库这两个术语来交替使用,但为了清晰起见,一个包包含模块,而一个库是包的集合。
我们使用 Python 库就像使用内置的模块一样,比如我们在第四章中首次使用的math模块,理解逻辑推理。在我们的源代码中,我们在创建算法之前使用import math导入了math模块。在第四章的应用归纳推理部分的示例中,我们使用了该模块的math.floor()函数,这使我们能够将一个数字向下舍入,而不管其小数值是多少。当我们在 Python 中导入模块或库时,我们正在利用额外的功能和能力,使我们能够更深入地使用编程语言。
那么,什么是库?在 Python 中,库指的是可以重复使用的代码块。库包含一系列模块。Python 有许多可用的库,就像 Python 本身一样,许多库都是开源的,这意味着任何人都可以下载和使用它们。因为我们将在本章中处理数据,所以现在我们将继续使用 pandas、NumPy 和 Matplotlib。然而,还有许多其他库和许多类型的库。例如,有图形用户界面(GUI)框架,如Kivy、tkinter、PyQt、PySimpleGUI等。对于游戏,还有其他库,如Pygame和Pyglet。在机器学习中,TensorFlow库是由Google与Brain团队合作开发的一种流行工具。但这只是一些可用库和使用它们的领域的例子。
安装库
虽然math模块是内置在 Python 语言中的,但库需要安装。在Python 3.9中,我们用来安装库的程序是pip 安装程序。这是一个内置在 Python 中并从命令提示符窗口运行的命令。我在这里要提到的一个警告是,权限和我们安装 Python 的位置很重要,所以如果你的计算机属于你的雇主,请确保 Python 程序路径已根据需要进行调整,以便你可以访问所有模块并安装库。权限可能会有所不同。
在我的情况下,虽然我的主要计算机不属于我,但我有管理员权限,所以我可以以管理员身份运行我的命令提示符,并从那里运行pip。
下面的截图显示了安装sympy Python 库。如你所见,使用pip install sympy命令将库安装到我们的系统上。值得一提的是,sympy是 Python 可用的符号数学库。由于我已经安装了我们将要使用的其他库,我必须展示一个我还没有在我的计算机上安装的包的安装:
图 12.1 – 安装 Python 库
如果你试图安装一个你已经安装过的库,例如如果我再次尝试安装 pandas,你会收到一个已满足要求的消息,就像下面截图中显示的那样。请注意,用户信息将被填入你的用户,而不是像截图中显示的那样被涂黑,这也显示了库包在你的系统上的位置:
图 12.2 – 已满足要求的消息
你可能还希望使用 Anaconda,这是 Python 和 R 编程语言的开源发行版。安装了 Anaconda 后,你可以使用CMD.exe提示窗口使用conda install或pip install来安装你的库。Anaconda 导航器中包含的 Jupyter 笔记本可以运行和保存你的 Python 程序。
Anaconda 发行版程序中包含了更多的程序和包,它们可以简化我们与 Python 编程语言的交互方式。如果你进行大量编码,这是一个很好的资源。
如果你还没有这样做,现在是一个安装 NumPy、pandas 和 Matplotlib 库的好时机,在我们开始使用它们来分析和显示数据以及创建模型之前。
使用 NumPy 和 pandas
NumPy,就像 Python 中的许多库和 Python 编程语言本身一样,是一个开源库。NumPy 用于多维数组和矩阵数据结构。Python 本身没有数组;它有列表。因此,库可以用来为我们的算法提供这种能力。当我们有多个相同类型的元素时,我们可以使用数据结构来保存它们 - 也就是数组。
pandas 库用于分析数据,构建在 numpy 包的基础上。pandas 和 NumPy 库经常一起使用。当我们需要图形模型时,我们会添加第三个库,Matplotlib,或者其他类似的库。在本章中,我们将继续使用 Matplotlib。
当我们导入库时,我们可以将它们作为整个名称导入,比如在这种情况下是numpy,或者我们可以缩短它们以方便使用。如果我们想要导入库,我们可以使用import numpy。假设我们想要创建一个从0到11的数字数组。我们可以使用numpy通过组合arange和reshape函数来组织它。让我们看一下代码片段:
ch12_abbreviate.py
import numpy as np
myArray = np.arange(0, 12).reshape(2, 6)
print(myArray)
请注意,我们导入的是numpy as np,而不是只有numpy。这意味着我现在可以使用np来调用 NumPy 函数,而不必每次都输入numpy。
提示:
请注意,np是 NumPy 的标准缩写,所以你可能经常看到它。你可以将 NumPy 导入为任何名称,但np是标准约定。
在上面的代码片段中,我们要求算法将从0到11的数字列表分成2行,每行6个元素。然后,我们打印数组以查看我们的结果。看一下输出:
[[ 0 1 2 3 4 5]
[ 6 7 8 9 10 11]]
正如你所看到的,我们从0开始,这是我们在范围0到12中的下限。现在我们有两行,每行有六个数字。我们不包括 12。如果我们尝试做0到13,我们将无法重塑我们的数组,因为我们无法均匀地分割 13 个数字。我们将得到以下回溯错误(最近的调用最后):
File "C:/Users/… /ch12_abbreviate.py", line 3, in <module>
myArray = np.arange(0, 13).reshape(2, 6)
ValueError: cannot reshape array of size 13 into shape (2,6)
请注意,当你看到省略号(…)时,你的信息将会不同。它将根据你的 Python 设置有你的文件位置或路径。例如,你的文件位置可能类似于C:/Users/JohnSmith/Documents/ch12_abbreviate.py。这应该替换前面代码中的文件位置。
正如ValueError消息所示,我们无法将数组重塑为(2, 6)形状,因为我们会有一个多余的数字。
现在,让我们来看一个 pandas 的DataFrame。我们使用DataFrame来以行和列的方式操作我们的数据。让我们看一个例子:
ch12_pdDataFrame.py
import pandas as pd
myAddressBook = {'names': ['Susan', 'Malena', 'Isabel',
'Juan', 'Mike'],
'phoneNumbers':['333-333-3333',
'444-444-4444',
'555-555-5555',
'777-777-7777',
'888-888-8888']}
addressBook = pd.DataFrame(myAddressBook)
print(addressBook)
请注意,我们创建了一个带有联系人姓名和号码值对的字典。这样做之后,我们将我们的通讯录保存为DataFrame,它将以表格形式组织我们的信息。最后,我们打印了我们的通讯录。看一下输出:
names phoneNumbers
0 Susan 333-333-3333
1 Malena 444-444-4444
2 Isabel 555-555-5555
3 Juan 777-777-7777
4 Mike 888-888-8888
请注意,pandas 使用字典中的信息来为我们的列创建标签。我们没有为行标签提供信息,所以 Python 和 pandas 自动使用了0到4的数字。该算法生成了一个表格,提供了我们通讯录中的姓名和号码。
正如你所看到的,pandas 和 NumPy 只是为 Python 增加了更多的功能。在我们转向 Matplotlib 之前,请注意我们还没有真正看到实际的数据分析。我们很快就会看到。现在,我们知道如何使用我们的库,以及我们可以使用它们来组织和分析数据。让我们简要地谈一下 Matplotlib,然后转向一个例子,我们可以使用一个数据文件来进行一些分析。
使用 Matplotlib
正如我们在前一节中提到的,pandas 和 NumPy 并不提供我们数据的可视表示或可视模型。为了从我们的数据创建图表,我们可以使用 Python 中的 Matplotlib 库。就像我们将 pandas 和 NumPy 库分别导入为pd和np一样,我们也可以缩短 Matplotlib。完整的库包含多个模块。我们将经常使用pyplot模块。Matplotlib.pyplot的最常用的缩写是plt。如果我们在调用 Matplotlib,我们通常将其缩写为mlt。让我们快速看一个示例:
ch12_matplotlib.py
import matplotlib.pyplot as plt
plt.plot([0, 3, 6], [4, 5, 6])
plt.show()
请注意,这个特定的库的名字甚至比matplotlib还要长。要导入这个库,我们必须使用matplotlib.pyplot来导入它。想象一下,每次在算法中需要它时都要写这么长的名字。不,谢谢! 相反,我们将它导入为plt。
现在,让我们看一下代码片段中的第二行。我们正在创建一个图表,将第一个列表中的每个数字与第二个列表中的数字匹配。因此,我们有三个坐标对:(0, 4),(3, 5)和(6, 6)。使用这段代码时的输出不仅仅是一行输出;它是一个图表。现在,算法在前面代码片段的第二行创建了该图表。
但除非你告诉它你想要的,否则它不会向你显示。把show()函数想象成 Python 中的print()函数。除非我们告诉算法我们想要看到什么,否则算法会在后台执行任务但不会向我们显示。以下图表显示了这个算法的输出:
图 12.3 – Matplotlib 示例图
现在,正如你所看到的,图表本身已经很有帮助。我们可以看到这是由这些点定义的线性关系。但请注意屏幕底部给出了一些选项。左下角的主页图标可以在任何时候将您的图表重置为其原始窗口。缩放功能,如下面的截图所示,允许我们选择我们想要更仔细查看的图表部分:
图 12.4 – Matplotlib 的缩放功能
请注意,左侧的图表显示了我们选择的我们想要仔细观察的图表部分。然后,右侧的图表只显示了我们选择的值。Matplotlib 还允许我们配置子图,并在多个表示之间使用箭头来导航。例如,如果我在放大后点击图表上的左箭头,它会将我带回到先前的表示。点击Home按钮会将我带回到原始图表。
当然,这并不是 Matplotlib 允许的唯一类型的图表或表示。这只是对可视化表示的一个小小的窥视。
提示:
有关可用图表类型的其他资源可以在这里找到:matplotlib.org/3.3.2/tutorials/introductory/sample_plots.html。
在本节中,我们学习了 Matplotlib 库。我们学习了如何创建一个简单的图表,以及在创建图形时如何从输出中放大我们图表的部分。
现在我们知道如何访问我们的库以及每个库可以为我们做什么,让我们看看如何使用它们来分析数据。
了解 Python 数据分析
在前一节中,我们介绍了一些可以用来分析 Python 数据的库。在本节中,我们将看一个例子和多个代码片段,使用真实数据和 Matplotlib 构建条形图,但在此之前,让我们回顾一下为什么 Python 在数据分析方面如此重要。
由于 Python 是面向对象的,它允许我们简化非常复杂和/或大型的数据集。这使得数据的可读性很高,并且使用库可以产生数据表示,如表格和可视模型,使我们能够预测数据的走向,创建回归分析等。正如本章介绍中提到的,数据分析对于决策也是至关重要的。一个设计良好的实验产生的数据是可靠的,并且是可推广的。数据分析可以成为我们社会更加平等和公平的工具。
说了这么多,我们将更多地关注我们可以用 Python 做什么的机械方面,而不是我们如何解释它,以便我们可以了解 Python 如何进行数据分析,并使用我们的库呈现结果。话虽如此,我们将在第十五章中使用更多的例子,应用计算思维问题,以及第十六章中,高级应用计算思维问题,在各个领域使用一些这些工具,这不仅将提供我们使用这些工具的机会,还将提供我们探索数据分析在这些样本背景下的意义。
现在是时候看一个例子,这个例子可以帮助我们进一步了解这些工具的能力,以及我们如何编写算法来解决一些提出的问题。
在开始之前,我们将使用一个数据文件ch12_data.csv,它可以在 GitHub 存储库中找到。该文件包含了 1996 年至 2012 年按种族/族裔、完成时间、性别、机构控制和接受申请百分比对求学学生的毕业率。这些数据是从国家教育统计中心下载的:nces.ed.gov/programs/digest/d19/tables/dt19_326.10.asp。
我们存储库中的数据文件只包含所有四年制机构的数据,而不是整个文件。一些标题也被简化了。
当我们想要使用数据文件时,重要的是告诉 Python 它可以在哪里找到它,以便在运行算法时知道要使用什么。为此,我们使用os模块,它允许我们的算法与我们的操作系统进行交互。请注意,我们的代码片段已经包括了其他库(我们稍后会使用它们):
ch12_csvAnalysis.py
import pandas as pd
import matplotlib as mlp
import matplotlib.pyplot as plt
import os
#Let Python know correct directory for file.
os.chdir('C:\\Users\\...\\Program Files\\Chapter 12')
print('Current directory', os.getcwd())
与以前一样,请确保用您自己的位置替换用户信息中的省略号。在存储库中提供的算法中,您还需要调整该位置,以便运行此算法,因为该算法的位置将是我的路径。
现在我们已经告诉 Python 在哪里找到文件,我们可以在算法中使用它。如果我们运行这个算法,我们会看到输出应该与我们在以os.chdir开头的行中注意到的路径匹配:
Current directory C:\Users\...\Program Files\Chapter 12
再次注意,我们的路径不会匹配。这将取决于您保存文件的位置。
下图显示了与我们的.csv文件相同的数据和文件,以.xls 格式,因为这样更容易指出我们需要的内容。请注意,我们将使用.csv文件进行分析:
图 12.5 - 以.xls 文件格式化为 Python 使用的数据
如果我们只想提取所示的行,我们可以使用以下代码片段从我们的.csv文件中获取该信息:
ch12_csvAnalysis_2.py
import pandas as pd
graduates = pd.read_csv('ch12_data.csv', index_col = 0)
print(graduates[0: 16])
请注意,我们的算法导入了 pandas 库。read_csv()函数告诉 Python 我们将使用文件名和我们想要开始处理的列的索引。该索引给我们提供了我们将用作行标题的值。如果索引不在第一列,我们可以将其更改为不同的值。然后,我们打印我们想要看到的行。因为我们的数据很宽,我们的输出如下所示:
图 12.6 - ch12_csvAnalysis.py 算法的输出
请注意,我们只看到了前两列,然后有省略号。这向我们表明第二列和第三列之间还有更多的列。这并不意味着 Python 没有读取其余的信息,只是对我们不可见。最后的输出行[13 rows x 10 columns]实际上告诉我们我们提取的数据中有多少行和列。
假设我想要看到一个组的数据,比如Hispanic,并使用 1996 年至 2012 年之间的条形图进行比较。然后,我可以使用 Matplotlib 库中的条形图绘制。让我们看看对以下算法所做的调整:
ch12_csvAnalysis_3.py
import pandas as pd
import matplotlib as mlp
import matplotlib.pyplot as plt
import os
#Let Python know correct directory for file.
os.chdir('C:\\Users\\sofia.dejesus\\OneDrive - Hawken
School\\03_book\\Program Files\\Chapter 12')
graduates = pd.read_csv('ch12_data.csv', index_col = 0)
print(graduates[0:13])
fig, ax = plt.subplots()
ax.bar(graduates.index, graduates['Hispanic'])
ax.set_xticks(graduates.index)
ax.set_xticklabels(graduates.index, rotation = 60,
horizontalalignment = 'right')
ax.set_title('Number of Hispanic graduates from 1996-2012',
fontsize = 16)
ax.set_ylabel('Number of Graduates')
plt.show()
我们应该仔细查看我们前面的代码中的一些事情。我们做的第一件事是添加fig, ax = plt.subplots()行。这行允许我们创建图表,还允许我们在一个图中创建多个图表。如果我们要添加四个图表,我们会使用fig, ax = plt.subplots(2, 2),这会告诉算法我们要在两行两列中创建四个图表。如果我们像前面的代码中那样将括号留空,我们就只有一个子图。
接下来,我们要确定我们将创建的图形,这是一个条形图。我们只想比较西班牙裔人口的数字,所以我们在ax.bar(graduates.index, graduates['Hispanic'])行中确定了这一点。
我在这里应该指出,许多开发人员使用dataframe作为他们的变量。我更喜欢用描述性的方式命名我的DataFrame,所以我没有将我的DataFrame称为df,而是在算法中将其称为graduates。无论你的偏好是什么,这就是我们目前正在使用的DataFrame,用来创建可读的图表。
请注意代码中的刻度和刻度标签;我们首先确定我们将从哪里获取刻度的数据以及标签将是什么。然后,我们可以通过添加旋转(如果我们想要倾斜标签)、对齐等来为我们的图表添加更多格式。我们还可以在这里更改字体大小。最后,在显示图表之前,我们设置y轴标题和条形图标题。以下截图显示了这个算法的图表:
图 12.7 - ch12_csvAnalysis_3.py 的西班牙裔毕业生情况请注意,我们的图表有标签,清晰显示了数据,我们能够修改格式,使其可读。在分析信息时,我们还注意到了第一年和 2000 年之间,以及 2000 年和 2002 年之间的间隔。这些年份的数据没有包含在数据文件中。
这只是使用 Matplotlib 可能的图表之一。我们将有机会在第十五章中探索更多内容,应用计算思维问题,以及第十六章,高级应用计算思维问题,这两章专门讨论了在本书前几章中讨论的各种领域的样本。现在,让我们继续探讨数据和 Python 库的其他应用。
使用额外的库进行绘图和分析
在结束本章关于实验数据、库的使用以及绘图和分析数据之前,让我们看看另外三个在数据分析和绘图中有帮助的库。这些不是唯一用于分析和绘图的库,也不会是我们在本书的其余部分中探索的唯一库:
- Seaborn是用于数据可视化的库;建立在 Matplotlib 之上。
- SciPy是用于线性代数、优化、统计等的库;建立在 NumPy 之上。
- Scikit-Learn是用于机器学习的库;是 SciPy 堆栈的一部分。
在接下来的章节中,当我们解决一些需要使用这些库的应用问题时,我们将更深入地使用其中一些库。现在,让我们快速看一下这些库在查看数据集时可以帮助我们解决什么问题。
使用 Seaborn 库
Seaborn 库为我们提供了更多的功能,除了 Matplotlib 的可视化功能。我们可以用 Seaborn 库做很多事情,通常我们将其简化为sns进行导入。以下是该库的一些常见用途:
- 相关性
- 聚合统计(对分类值的观察)
- 依赖变量的线性回归图
- 创建具有多个图表的抽象和网格
Seaborn 最棒的一点是它也能很好地与 pandas 一起使用。当与 pandas 的数据框结合时,创建数据的统计表示和可视化变得很容易。
Seaborn 有一些可以访问的示例数据集 - 也就是说,它们是内置的 - 只要我们知道数据集的名称,就可以调用内置数据集。然后我们可以用一行简单的代码调用内置数据集。
让我们看看以下代码片段和生成的图形:ch12_seabornSample.py
# Import seaborn
import seaborn as sns
sns.set_style('darkgrid')
# Load an example dataset
exercise = sns.load_dataset("exercise")
#Create the plot
sns.relplot(
data=exercise,
x='id', y="pulse", col="time",
)
从上面的代码中,您可以看到我们为图表设置了样式。我们在导入库后为我们的图表添加了'darkgrid'样式。Seaborn 有一些内置样式:white、whitegrid、dark、darkgrid和ticks。
以下截图显示了 Seaborn 的结果图:
图 12.8 - 使用 darkgrid 样式的练习样本数据集的图表
从图表中可以看出,我们可以分析脉搏和时间或其他变量之间是否存在相关性。
我们还可以使用成对绘图来显示变量之间是否存在相关性。让我们使用另一个内置数据集 flights 来看看成对绘图的效果:
ch12_pairplotSNS.py
# Import seaborn
import seaborn as sns
sns.set_style('darkgrid')
# Load an example dataset
flights = sns.load_dataset("flights")
#Create the plot
sns.pairplot(
data=flights,
)
上述代码片段与之前的练习数据非常相似。不同之处在于,在这种情况下我们调用了pairplot()函数。以下截图显示了我们得到的图表网格:
图 12.9 - 使用 Seaborn 进行成对绘图
请注意,乘客数量和年份似乎呈正相关。也就是说,后来的年份比飞行初期的年份有更多的乘客。在分析这些数据集时,我们可能想要进行预测,比如说现代时代会有更多的乘客飞行。
我们可以使用图表来帮助我们进行预测。也就是说,这些数据相当古老,所以我们需要更多更新的数据来进行准确的预测。数据越多,越好。对于大量数据,我们还可以使用机器学习,我们将在[第十三章](B15413_13_Final_SK_ePub.xhtml#_idTextAnchor174)使用分类和聚类以及[第十四章](B15413_14_Final_SK_ePub.xhtml#_idTextAnchor184)计算思维和 Python 在统计分析中中简要探讨,以便我们学习如何操作和学习数据。
在我们转向另一个库之前,关于成对图的另一件事——如果有 10 列数据,成对图将比较每一列与自身,然后与其他可用的每一列,创建一个相当大的显示,显示所有比较的变量。我们将在第十四章,计算思维和 Python 在统计分析中中再看一些成对绘图。还有第十五章,应用计算思维问题,以及第十六章,高级应用计算思维问题中的问题,将让我们更多地练习这些图表以及我们可以从中得到的东西。
Seaborn 库有助于可视化统计数据,类似于 Matplotlib。Seaborn 内置函数的易用性使其成为可视化和分析的强大工具。
现在,让我们来看看 SciPy 库。
使用 SciPy 库
SciPy 库主要用于解决科学和数学问题。以下是一些有用的子包及其用途:
-
cluster用于聚类算法。 -
constants包含物理和数学常数和单位,如golden(黄金比例)和mu_0(磁常数)。 -
fftpack利用快速傅里叶变换例程。 -
integrate用于微分方程求解器。 -
interpolate包含插值和平滑样条。 -
io与输入和输出有关。 -
linalg与线性代数相关。 -
ndimage用于处理N维图像。 -
odr用于正交距离回归。 -
optimize用于优化和寻根例程。 -
signal用于信号处理。 -
sparse用于稀疏矩阵和相关例程。 -
spatial用于空间数据结构和算法。 -
special用于特殊函数(如椭圆函数和积分)。 -
stats用于统计分布和函数。
在每个子包中,SciPy 包含许多函数,以帮助可视化和优化科学数据。因为它是专门为此目的而创建的,所以它是科学领域常用的工具。也就是说,统计包也很强大,因此即使在非科学统计分析中,该库也很有帮助。
让我们现在来看看 Scikit-Learn 库,这将在本书的后续章节中使用。
使用 Scikit-Learn 库
Scikit-Learn 可能是 Python 中最重要的机器学习库。在接下来的章节中,我们将在样本中探索这个库,探讨一些适合机器学习的问题,因此我们不会在这里深入探讨功能。也就是说,以下是 Scikit-Learn 给我们的一些功能:
-
聚类有助于对未标记的数据进行分组。
-
回归衡量变量(变量的平均值)与其他变量的值之间的关系。
-
分类在 Scikit-Learn 中有多个分类器,类似于回归。一些分类器是线性判别分析、装袋分类器、K 最近邻分类器等。
-
模型选择具有用于在机器学习中创建训练和测试模型的工具。
-
预处理包含用于标准化数据集的工具(有关数据预处理的更多细节可以在第十四章,计算思维和 Python 在统计分析中中找到)。
Scikit-Learn 库是我们在接下来的例子中会比较熟悉的内容,所以我们将一些讨论留到[第十四章](B15413_14_Final_SK_ePub.xhtml#_idTextAnchor184),计算思维和 Python 在统计分析中,[第十五章](B15413_15_Final_SK_ePub.xhtml#_idTextAnchor199),应用计算思维问题,以及[第十六章](B15413_16_Final_SK_ePub.xhtml#_idTextAnchor219),高级应用计算思维问题,所以我们将一些讨论留到那些章节。
我们在 Python 中拥有的库和包使我们能够对数据集进行详细分析,并创建各种有用的图表,这有助于数据分析。
总结
在本章中,我们讨论了实验数据和有效性、可靠性以及在实验环境中的普适性的定义。我们还讨论了如何安装和使用 pandas、NumPy 和 Matplotlib 库,以便我们可以使用它们来组织和显示数据。您学到的一些技能包括定义实验、数据收集,以及计算思维如何帮助我们定义问题并设计用于显示结果的工具。
此外,我们还了解了数据分析和数据科学在当前世界中的增长和重要性。我们能够使用库来生成代表数据文件子集的 Matplotlib 条形图。
在下一章中,我们将学习更多关于数据以及数据科学和数据分析的应用。
第十三章:使用分类和聚类
在本章中,我们将使用 Python 编程语言的分类和聚类能力。我们将使用计算思维元素来定义处理聚类和分类问题时所需的组件。
在本章中,我们将涵盖以下主题:
-
定义训练和测试
-
实施数据聚类
通过本章的学习,您将能够设计最适合所提出场景的算法。您还将能够识别与所提出问题最符合的 Python 函数,并概括您的解决方案。
技术要求
我们将需要最新版本的 Python 和Scikit-Learn来执行本章中的代码。
您可以在这里找到本章中使用的代码:github.com/PacktPublishing/Applied-Computational-Thinking-with-Python/tree/master/Chapter13
您可以在这里找到Pima Indians Diabetes Database:www.kaggle.com/uciml/pima-indians-diabetes-database
数据训练和测试
在本节中,我们将学习如何使用 Python 工具和库为训练和测试数据创建模型。在处理数据和数据科学时,有时我们希望训练算法继续从数据中收集和学习。然后数据训练用于数据挖掘和机器学习。首先,让我们定义训练数据集:
-
这是用来拟合模型的数据样本。
-
这是一个实际数据集,用于训练模型(在神经网络的情况下是权重和偏差)。训练模型从这些数据中“看到”和“学习”。
在计算机领域,神经网络是使用人类和动物大脑中的生物神经网络作为灵感创造的计算系统。当使用训练数据集时,例如在创建机器学习模型时,模型严重依赖数据。但是什么是机器学习? 机器学习,或ML,是人工智能(AI)的一种应用,允许机器在没有明确编程的情况下自动学习使用程序。
没有高质量的数据训练基础,算法就是无用的。ML 中的数据训练是指用于开发模型的初始数据。它们发现关系,发展理解,发现模式和趋势。输入数据被馈送到 ML 算法和所有相关的技术中,以产生输出。该输出也被反馈到模型中作为更新的反馈,进而提供用作再次输入的反馈数据。这个过程是循环的,不断地适应和学习。下图显示了一个简单的 ML 图形,展示了发生的过程:
图 13.1 - 机器学习及其与输入和输出的关系
在 ML 中,数据与统计工具结合起来预测输出。机器接收输入数据并使用算法构建答案。这类似于数据挖掘,其中使用大型数据集来发现异常、相关性、模式等,以预测结果。
在数据挖掘中,我们提取信息,但使用方法提取必要的、相关的、无误的数据点。也就是说,通过数据挖掘,我们从数据集中提取我们需要的内容,而不提取那些异常值,同时还要查看数据中的相关性和模式。数据挖掘和 ML 之间的区别在于 ML 分析输入数据和输出数据。因此,一旦输出被处理,它就会回到算法中,被馈送回输入数据,并重新处理。这个循环是持续的,正如您可以从前面的图表中看到的那样。
重要提示:
有四组 ML 算法,但在本书中,我们只会介绍其中两种。虽然这不是一本 ML 书,但 Python 在 ML 领域的应用仍在不断增长,因此了解编程语言的应用以及如何使用计算思维来解决问题与我们的目标相关。
在 ML 中,我们使用两种重要的技术:
-
监督学习映射数据对,使用输入数据和期望输出(训练数据),以便模型可以找到潜在的模式。
-
无监督学习使用无标签的训练数据进行结论。
此外,还有两种我们不会在本书中介绍的技术:半监督学习和强化学习。
在监督学习中,学习算法被提供一组输入以及它们的期望输出(也称为标签)。目标是发现一条规则,使计算机能够重新创建输出,或者换句话说,映射输入和输出。另一方面,无监督学习允许我们在几乎不知道我们的结果应该是什么的情况下解决问题。输出变量是无标签的。使用无监督学习,算法被提供一组输入,但没有期望的输出,这意味着算法必须自行找到结构和模式。
以下图表显示了监督学习和无监督学习的路线图:
图 13.2 - 机器学习类型
从前面的图表中可以看出,我们有两种监督学习。当我们获得训练数据和期望的输出时,我们使用回归或分类。使用回归,我们预测连续值输出。使用分类,我们得到离散值输出(0 或 1)。回归的一个例子是预测某一天会有多少降雨,而分类则是想知道是否会下雨。
对于无监督学习,前面的图包含了聚类的示例。在聚类中,我们获得训练数据,但只有少量期望的输出。聚类的一个例子是分组,它会从大量数据中获取项目并对其进行分组。
我们可以将这些类型的学习风格应用于人工神经网络。神经网络的训练通常是通过确定网络的处理输出(通常是预测)与目标输出之间的差异来进行的。这就是错误,因此网络根据学习规则调整其加权关联,并使用此错误值进行调整。神经网络通常组织成层。层由包含激活函数的多个相互连接的节点组成。
激活函数用于决定神经元是否被激活。为此,计算加权和,然后添加偏差。我们使用激活函数为神经元提供非线性的输出。最常见的三种激活函数如下:
-
Sigmoid可以表示为
,其中实数的输入为x。它返回一个在-1 和 1 之间的值。
-
Tanh由tanh(x)给出。它是一个具有实数输入x的双曲正切函数;它返回一个在-1 和 1 之间的值。
-
修正线性单元(ReLU)是一个分段线性函数。如果输入是正数,则输出与输入相同,否则为 0。
以下图表显示了与上述每个激活函数相关的图表:
图 13.3 - 激活函数
模式通过输入层呈现给网络,该层与一个或多个隐藏层通信,实际处理是通过一组加权连接完成的。隐藏层然后链接到一个输出层,答案作为下图所示的输出输出:
图 13.4 – 人工神经网络(ANN)模型
正如您所见,求和运算符()接受输入值并通过网络创建输出。它们必须被求和,并在进入新节点时返回单个值。激活函数本质上是压缩输入并将其转换为表示节点应该贡献多少的输出值(即节点何时应该触发)。当节点被激活时,节点被认为是激活的。它获取输出值并将其转换,以便下一个节点可以将其作为输入。这称为激活。
现在让我们看看如何使用 pandas 对数据进行分类。
分类数据示例
现在让我们看一个分类数据的例子。以下屏幕截图显示了使用监督学习的示例。为了生成屏幕截图中可以看到的输出,我们使用了来自www.kaggle.com的现有数据集。该数据集称为皮马印第安人糖尿病数据库。它描述了皮马印第安患者是否被诊断出患有糖尿病:
图 13.5 – 无监督学习示例
正如您所见,表格中的属性,也称为输入变量(x),如下所示:
-
怀孕次数
-
口服葡萄糖耐量试验 2 小时后的血浆葡萄糖浓度
-
舒张压(mm Hg)
-
三头肌皮褶厚度(mm)
-
2 小时血清胰岛素(mu U/ml)
-
体重指数
-
糖尿病谱系功能
-
年龄(年)
对于输出变量(y),我们有类变量(0 或 1)。从数据集中,每一行代表一个患者,以及该人在过去 5 年内是否被诊断出患有糖尿病。正如您所见,有八个输入变量和一个输出变量(如前图所示的最后一列)。
我们将使用二元分类模型(1 或 0),将输入变量(x)的行映射到输出变量(y)。这将总结为y = f(x)。以下代码片段使用此信息来获取我们的输出。请注意,我们将在整个示例中逐步讨论完整的文件:
ch13_diabetesA.py
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
dataset = pd.read_csv('diabetes.csv')
正如您从前面的片段中所看到的,我们正在上传名为diabetes.csv的数据集(来自 Kaggle)。如果您需要提醒如何保存文件并找到所需的路径,请参阅第十二章,在使用 Python 进行实验和数据分析问题*中,了解使用 Python 进行数据分析部分。有许多上传数据集的方法。
就像我们在第十二章中所做的那样,我们使用非常流行的pandas并将其导入为pd。Pandas 用于数据操作和分析。它提供了用于操作数值表和时间序列的数据结构和操作。来自 pandas 的read_csv()函数处理从逗号分隔值(CSV)中导入数据的值。
重要提示:
我们需要找到正确的目录。当您调用.csv文件时,请确保您在正确的目录中(即.csv文件所在的位置),以避免错误代码。在使用import os后使用os.chdir(),然后使用print('Current directory', os.getcwd())。有关更多信息,请参见第十二章,在实验和数据分析问题中使用 Python。
运行上述代码片段后,您可以查看变量资源管理器,以查看以下截图中显示的项目。请注意,变量资源管理器是一个工具,允许您浏览和管理与您的代码相关的对象。该工具是Spyder环境的一部分,它运行 Python 并具有附加功能和编辑工具,如变量资源管理器。变量资源管理器位于我们环境的右上方。以下截图显示了变量资源管理器中我们数据库的视图:
图 13.6 - 变量资源管理器示例
正如您所看到的,Size描述了数据集。它显示了患者数量786和变量总数9。现在我们对数据集有了更好的理解。
但是假设你不知道你将需要什么类型的学习。您可以在控制台中键入此函数,以获得数据和输出的完整图片:
dataset.describe(include='all')
以下截图显示了我们在算法中使用上述代码行后收到的信息:
图 13.7 - 运行描述算法后的信息显示
从上图可以看出,我们能够获得所有数值特征,并知道没有分类数据。我们希望获得这些信息,因此可以使用以下代码行来查看变量之间的相关性:
dataset.corr()
这一行简单的代码帮助我们获得以下截图中显示的信息。请注意,以下截图可能会因您使用的环境而有所不同。在运行此代码时,使用Spyder或Jupyter等环境时,根据您的主题设置和选择,表格可能会有所不同,具有不同的颜色方案(或没有颜色方案):
图 13.8 - 数据集相关图
我们可以看到所有变量与结果(输出(y))之间的相关性。前面的截图显示了血浆葡萄糖与结果的最强相关性,胰岛素的相关性最低。
现在我们对数据集有了更好的理解,让我们将输入变量和输出变量分开放入模型。让我们看一下我们的ch13_diabetesA.py文件中的以下代码片段,这为我们提供了示例:
#Split dataset into input(x) and output(y) variables
x_variables = dataset.iloc[:,0:8]
y_variable = dataset.iloc[:,8]
我们使用print函数来检查我们的值:
print(x_variables)
print(y_variable)
运行上述代码片段后,输出数据将如下截图所示。请注意,结果显示了我们定义为变量x_variables和y_variable,这些变量又被定义为数据集的一部分,如前面的代码中所述:
图 13.9 - 用于算法打印输入和输出值的训练数据集输出
现在我们需要将数据分割成训练数据集和测试数据集。分割技术的目的是评估 ML 算法的性能。它仅适用于任何类型的监督学习算法。第一组(训练数据集)用于拟合模型。
主要目标是将其拟合到具有已知输入和输出的可用数据上,然后对将来的新示例进行预测,在那里我们没有预期的输出或目标值。
使用 Scikit-Learn 库
在处理数据和机器学习时,另一个重要的库是scikit-learn(sklearn)库。该库特别适用于分类、回归、聚类、模型选择、降维等。您可能还记得第十二章,在实验和数据分析问题中使用 Python,在在 Python 中使用数据库部分中,您可以使用命令提示符窗口中的pip install来安装所需的库。一旦您有了库,就可以将其导入到代码中,如下面的代码片段所示,该代码片段使用sklearn来拆分数据。需要注意的是,此代码片段是较大的ch13_diabetesA.py文件的一部分:
from sklearn.model_selection import train_test_split
X_train,X_test, y_train,y_test = train_test_split(
x_variables, y_variable, test_size = 0.20,
random_state = 10)
以下是已知的参数:
-
x_variable和y_variable如前所定义。 -
test_size:测试大小将占数据集的 20%。 -
random_state:它设置了随机生成器的种子,因此您的训练和测试拆分始终是确定性的。如果设置为 none,则返回一个随机初始化的RandomState对象。
以下图表显示了该过程以及每个元素如何在循环中相互作用:
图 13.10 - 机器学习中的数据循环
请注意,我们将在算法中使用顺序模型。此外,我们使用keras库,它与 Python 一起使用,因此我们可以运行我们的算法与深度学习模型。确保您有keras库可用于此算法。如果您已安装了 TensorFlow,则应该已经可以访问 Keras 库。
在处理机器学习问题和算法时,您可以选择使用不同的库。我们选择了 Keras 来解决这个特定的问题。Keras 是开源的,用于创建人工神经网络。TensorFlow 是一个包含许多机器学习组件和任务的平台。
Keras 建立在 TensorFlow 之上,并使其更容易与 Python 编程语言交互。Keras 是一个更高级的 API,我们将进一步讨论。由于其容量,它有时可能比平常慢。PyTorch是另一个用于人工神经网络的库。它是一个较低级别的 API,因此运行速度更快。Keras 由Google支持,而 PyTorch 由Facebook支持。两者都很有帮助,因此决定使用哪一个通常是开发人员的偏好。就我个人而言,我更喜欢 Keras 库。
顺序 API 允许您逐步创建模型层。还有另外两个可用的模型,功能 API和模型子类化。我们将使用顺序 API,因为它是最简单的架构,而功能 API 用于深度学习(复杂模型)和模型子类化。
有几件事情我们应该注意使用 Keras:
-
模型类是根类,用于定义模型的架构。
-
像 Python 本身一样,Keras 使用面向对象的编程,这意味着我们可以添加子类。
-
模型中的子类是可定制的。
尽管如此,还应该注意到,与使用顺序或功能 API 相比,子类化更具挑战性。现在让我们看一下使用我们的 Keras 库更新的算法。记得包括你的文件目录或将.csv文件保存到必要的目录以正确运行算法:
ch13_diabetesB.py
from sklearn.model_selection import train_test_split
from keras import Sequential from keras.layers import Dense
#Defining the Model
model = Sequential()
model.add(Dense(12, input_dim=8, activation='relu'))
model.add(Dense(15, activation='relu'))
model.add(Dense(8, activation='relu'))
model.add(Dense(1, activation='sigmoid'))
从前面的代码片段中,我们可以看到我们添加了四个密集连接的层。
第一层建立如下:
-
12 个神经元
-
input_dim = 8(即,输入进入网络的输入值) -
激活'relu'
如您所见,我们已经添加了多个模型并对其进行了定义。为了编译模型,我们使用了同一代码文件中包含的以下代码片段。我们还可以设置model.fit来使用我们的库和以下代码,这是我们的ch13_diabetesB.py文件的一部分:
#Compile the model
model.compile(loss='binary_crossentropy', optimizer='adam',
metrics=['accuracy'])
#Fit the model on the dataset
model.fit(x_variables, y_variable, epochs=95,
batch_size=25)
#Evaluate the model
_, accuracy = model.evaluate(x_variables, y_variable)
print('Accuracy: %.2f' % (accuracy*100))
model.summary()
前面的代码编译了 Adam 优化器。Adam 优化器用于随机梯度下降,并使用训练数据迭代更新网络权重。一旦我们运行我们的代码,输出会提供以下信息:
图 13.11 - 使用 Keras 库运行模型的输出
请注意,当您运行算法时,准确性可能会有所不同。多次测试算法以查看窗口中的变化。在运行准确性模型后,我们打印模型摘要,使用model.summary(),如下所示:
图 13.12 - 算法的模型摘要
现在我们已经看到如何使用糖尿病数据文件运行我们的算法,让我们简要地看一下一些优化模型,这将帮助我们评估算法。我们不会深入研究这些算法,但我们想提到一些可用于我们的各种工具。在建模时,我们使用优化模型,如二元交叉熵、Adam 优化算法和梯度下降。
定义优化模型
让我们看看模型的类型。请注意,我们并没有深入研究这些模型的使用,但建议进一步探索它们在我们算法中的应用。
二元交叉熵模型
在二元分类中,我们使用交叉熵作为默认的损失函数。损失函数是一种帮助我们评估算法如何对数据建模的方法。有了损失函数,我们可以使用优化来产生更准确的结果,即它有助于减少预测误差。当目标值在二进制集中时,我们使用损失函数。
交叉熵是一种损失函数,用于计算两个概率分布之间的差异。在使用逻辑回归和人工神经网络优化分类模型时,我们可以使用交叉熵。
Adam 优化算法
Adam 算法是一种随机优化方法。当函数中存在随机性以最大化或最小化函数的值时,使用随机优化。Adam 优化算法适用于一些非凸的简单优化问题。它高效且占用内存少,但可以应用于大型数据集。
梯度下降模型
梯度下降算法是一种一阶优化算法。一阶指的是线性局部误差。我们在可微分的函数上使用梯度下降来找到局部最小值。
混淆矩阵模型
混淆矩阵也被称为错误矩阵。混淆矩阵在视觉上很有帮助,因为它以表格形式呈现算法的性能,这样可以更好地可视化性能。它通常用于监督学习。
正如所述,这只是一些基本信息,当您开始优化算法时。关于 ML 的更多信息可以在其他 Packt 图书中找到,例如Python 机器学习和Python 探索性数据分析。
在我们进入对聚类的介绍之前,让我们快速回顾一下在本节中使用 Keras 包和模型学到的内容:
-
加载数据
-
在 Keras 中定义神经网络
-
使用高效的数值后端编译 Keras 模型
-
在数据上训练模型
-
在数据上评估模型
-
使用模型进行预测
现在让我们转向数据聚类。
实施数据聚类
在本节中,我们将看一下如何处理数据聚类。首先,让我们定义一下我们所说的数据聚类。数据聚类是指我们如何将数据分成组或簇。如果簇能够提供对领域知识的扩展理解,那么簇就是有意义的。我们在许多应用中使用聚类,比如医学领域,聚类可以帮助识别一组患者对治疗的反应,或者市场研究,聚类用于根据特定群体的特征来对消费者进行分组以吸引该群体。
在本讨论中,我们将看一下合成簇而不是应用簇。在第十六章,高级应用计算思维问题中,你将看到一些上下文中的簇的例子。合成簇是由合成数据集生成的。也就是说,我们使用算法生成数据集。让我们看一下下面的代码片段:
ch13_syntheticDataset.py
from numpy import where
from sklearn.datasets import make_classification
from matplotlib import pyplot
#Create a synthetic dataset
X, y = make_classification(n_samples = 1800,
n_features = 2, n_informative = 2, n_redundant = 0,
n_clusters_per_class = 1, random_state=4)
#Scatterplot
for class_value in range(2):
row_ix = where(y == class_value)
pyplot.scatter(X[row_ix, 0], X[row_ix, 1])
#Display plot
pyplot.xlabel('variable 1')
pyplot.ylabel('variable 2')
pyplot.title('Synthetic data graph')
pyplot.show()
从前面的代码片段中可以看到,我们确定了样本数量、特征数量和簇的数量等等。此外,我们还创建了合成数据的散点图并绘制了结果。下面的图展示了我们的合成数据集作为散点图的结果:
图 13.13 – 合成数据集散点图
请注意,这个合成数据集的样本数量为1800。尝试改变样本数量来看看散点图的变化。现在我们有了数据集,我们可以开始应用聚类算法。以下是一些常见的聚类算法:
-
BIRCH 算法
-
K 均值聚类算法
我们将在接下来的章节中看到上述的算法。
使用 BIRCH 算法
平衡迭代减少和层次聚类(BIRCH)是一种聚类算法,只要有足够的可用内存和时间,就可以使用聚类中心。对于 BIRCH 和 K 均值聚类算法,我们将分享一个算法和相应的图表,以便更好地理解它们。
以下代码片段展示了 BIRCH 算法:
ch13_BIRCH.py
from numpy import unique
from numpy import where
from sklearn.datasets import make_classification
from sklearn.cluster import Birch
from matplotlib import pyplot
#Synthetic dataset definition
X, _ = make_classification(n_samples = 1800,
n_features = 2, n_informative = 2, n_redundant = 0,
n_clusters_per_class = 1, random_state = 4)
#Define the BIRCH model
model = Birch(threshold = 0.01, n_clusters = 2)
model.fit(X)
yhat = model.predict(X)
#Clusters
clusters = unique(yhat)
#Display
for cluster in clusters:
row_ix = where(yhat == cluster)
pyplot.scatter(X[row_ix, 0], X[row_ix, 1])
pyplot.show()
请注意,在这个示例中我们任意选择了两个簇,就像你在代码行model = Birch(threshold = 0.01, n_clusters = 2)中看到的一样。我们坚持使用我们的 1800 个样本,这样我们可以比较我们的输出图。下面的截图展示了两个样本的 BIRCH 模型。第一个(左侧)展示了前面代码片段中提供的算法运行的情况。第二个(右侧)展示了相同的算法运行,但是为了三个簇。
为了运行第二个图,我们将模型行代码更改为以下内容:
model = Birch(threshold = 0.01, n_clusters = 3)
看一下下面的图,显示了n_clusters = 2和n_clusters = 3的情况:
图 13.14 – 分别使用 2 和 3 个簇的 BIRCH 模型
请注意,在前面截图的左侧,清楚地显示了两个簇。与图 13.13相比,你可以看到一些数据点已经被转换以适应每个被识别的簇。前面截图的右侧的散点图将数据分成了三个不同的簇。为了更加熟悉聚类算法,改变参数来看看当你改变簇的数量、样本大小和其他参数时会发生什么。
现在让我们来看一下 K 均值聚类算法。
使用 K 均值聚类算法
K 均值聚类算法是最常用的聚类算法之一。该算法将示例分配到每个识别的聚类中以最小化方差。与 BIRCH 算法类似,我们在算法中设置了聚类的数量。让我们看一下 K 均值代码片段:
ch13_KMeans.py
from numpy import unique
from numpy import where
from sklearn.datasets import make_classification
from sklearn.cluster import KMeans
from matplotlib import pyplot
#Dataset definition
X, _ = make_classification(n_samples = 1800,
n_features = 2, n_informative = 2, n_redundant = 0,
n_clusters_per_class = 1, random_state = 4)
#Model identification and fit
model = KMeans(n_clusters = 2)
model.fit(X)
#Clusters
yhat = model.predict(X)
clusters = unique(yhat)
#Display
for cluster in clusters:
row_ix = where(yhat == cluster)
pyplot.scatter(X[row_ix, 0], X[row_ix, 1])
pyplot.show()
再次注意,我们使用相同数量的聚类(2)和样本数量(1800),以便进行比较。以下屏幕截图显示了前述算法产生的 K 均值散点图输出:
图 13.15 – K 均值算法输出
请注意,数据仍然完全相同,然而,当我们比较从 BIRCH 算法和 K 均值算法得到的显示时,您会发现我们的算法为我们的聚类产生了非常不同的结果。
我们可以使用和测试许多其他的聚类算法。了解它们并比较结果对于确定基于真实数据集使用哪些算法是至关重要的。在这种情况下,K 均值算法的结果并不真正适合模型。当使用两个聚类时,BIRCH 模型似乎更合适,因为 K 均值算法中的方差是不均等的。
随着我们从聚类示例中继续前进,请注意,对于大部分数据科学和机器学习而言,我们使用模型和算法的越多,我们就越了解它们的用途,以及何时使用这些模型是合适的,我们可以学会通过视觉识别算法是否适合我们的数据。
总结
在本章中,我们学习了如何使用 Python 编程语言中可用的一些包来创建大型数据集的模型。我们使用了 Keras 等包来上传数据和定义神经网络。我们训练了模型并评估了模型。我们使用模型进行预测。我们还学习了数据的分类和测试以及如何处理数据聚类。阅读完本章后,您现在可以定义数据训练以及 Python 在数据训练中的用途。您还可以定义和使用聚类算法。
我们将在下一章继续探讨这里讨论的一些主题。我们还将在第十六章中提供的示例中看到一些这些应用,高级 应用计算思维问题。