Flashield's Blog

Just For My Daily Diary

Flashield's Blog

Just For My Daily Diary

07.exercise-working-with-external-libraries【练习:使用外部库】

This notebook is an exercise in the Python course. You can reference the tutorial at this link.


Try It Yourself

There are only three problems in this last set of exercises, but they're all pretty tricky, so be on guard!

Run the setup code below before working on the questions.

自己尝试一下

最后一组练习只有三道题,但是都比较棘手,要小心!

在回答问题之前运行下面的设置代码。

from learntools.core import binder; binder.bind(globals())
from learntools.python.ex7 import *
print('Setup complete.')
Setup complete.

Exercises

练习

1.

After completing the exercises on lists and tuples, Jimmy noticed that, according to his estimate_average_slot_payout function, the slot machines at the Learn Python Casino are actually rigged against the house, and are profitable to play in the long run.

Starting with $200 in his pocket, Jimmy has played the slots 500 times, recording his new balance in a list after each spin. He used Python's matplotlib library to make a graph of his balance over time:

1.

完成列表和元组的练习后,吉米注意到,根据他的estimate_average_slot_payout函数,Learn Python 赌场的老虎机实际上是针对赌场的,从长远来看是有利可图的。

从口袋里的 200 美元开始,吉米已经玩了 500 次老虎机,并在每次旋转后将他的新余额记录在列表中。 他使用 Python 的matplotlib库绘制了一段时间内的余额图表:

# Import the jimmy_slots submodule
from learntools.python import jimmy_slots
# Call the get_graph() function to get Jimmy's graph
graph = jimmy_slots.get_graph()
graph

png

As you can see, he's hit a bit of bad luck recently. He wants to tweet this along with some choice emojis, but, as it looks right now, his followers will probably find it confusing. He's asked if you can help him make the following changes:

  1. Add the title "Results of 500 slot machine pulls"
  2. Make the y-axis start at 0.
  3. Add the label "Balance" to the y-axis

After calling type(graph) you see that Jimmy's graph is of type matplotlib.axes._subplots.AxesSubplot. Hm, that's a new one. By calling dir(graph), you find three methods that seem like they'll be useful: .set_title(), .set_ylim(), and .set_ylabel().

Use these methods to complete the function prettify_graph according to Jimmy's requests. We've already checked off the first request for you (setting a title).

(Remember: if you don't know what these methods do, use the help() function!)

正如你所看到的,他最近运气不太好。 他想在推特上加上一些精选的表情符号,但是,就目前看来,他的追随者可能会觉得很困惑。 他被问及您是否可以帮助他做出以下改变:

  1. 添加标题500老虎机抽奖结果
  2. 使y轴从0开始。
  3. 在y轴上添加标签余额

调用type(graph)后,您会看到 Jimmy 的图形的类型为matplotlib.axes._subplots.AxesSubplot。 嗯,这是新的。 通过调用dir(graph),您会发现三个看起来很有用的方法:.set_title().set_ylim().set_ylabel()

根据Jimmy的要求,使用这些方法完成pretify_graph功能。 我们已经为您核对了第一个请求(设置标题)。

(记住:如果您不知道这些方法的作用,请使用help()函数!)

def prettify_graph(graph):
    """Modify the given graph according to Jimmy's requests: add a title, make the y-axis
    start at 0, label the y-axis. (And, if you're feeling ambitious, format the tick marks
    as dollar amounts using the "$" symbol.)
    """
    graph.set_title("Results of 500 slot machine pulls")
    # Complete steps 2 and 3 here
    graph.set_ylim(0)
    graph.set_ylabel("Balance")
    ticks = graph.get_yticks()
    # Format those values into strings beginning with dollar sign
    new_labels = ['${}'.format(int(amt)) for amt in ticks]
    # Set the new labels
    graph.set_yticklabels(new_labels)

graph = jimmy_slots.get_graph()
prettify_graph(graph)
graph
/tmp/ipykernel_33/2628533306.py:14: UserWarning: FixedFormatter should only be used together with FixedLocator
  graph.set_yticklabels(new_labels)

file

Bonus: Can you format the numbers on the y-axis so they look like dollar amounts? e.g. $200 instead of just 200.

(We're not going to tell you what method(s) to use here. You'll need to go digging yourself with dir(graph) and/or help(graph).)

奖励: 您能否设置 y 轴上数字的格式,使它们看起来像美元金额? 例如 200 美元,而不是 200。

(我们不会告诉您在这里使用什么方法。您需要自己去挖掘 dir(graph) 和/或 help(graph)。)

# Check your answer (Run this code cell to receive credit!)
q1.solution()

Solution:

def prettify_graph(graph):
    graph.set_title("Results of 500 slot machine pulls")
    # Make the y-axis begin at 0
    graph.set_ylim(bottom=0)
    # Label the y-axis
    graph.set_ylabel("Balance")
    # Bonus: format the numbers on the y-axis as dollar amounts
    # An array of the values displayed on the y-axis (150, 175, 200, etc.)
    ticks = graph.get_yticks()
    # Format those values into strings beginning with dollar sign
    new_labels = ['${}'.format(int(amt)) for amt in ticks]
    # Set the new labels
    graph.set_yticklabels(new_labels)

2. 🌶️🌶️

This is a very hard problem. Feel free to skip it if you are short on time:

Luigi is trying to perform an analysis to determine the best items for winning races on the Mario Kart circuit. He has some data in the form of lists of dictionaries that look like...

这是一个非常难的问题。 如果您时间有限,请随意跳过:

Luigi正在尝试进行分析,以确定在马里奥卡丁车赛道上赢得比赛的最佳物品。 他有一些字典列表形式的数据,看起来像......

[
    {'name': 'Peach', 'items': ['green shell', 'banana', 'green shell',], 'finish': 3},
    {'name': 'Bowser', 'items': ['green shell',], 'finish': 1},
    # Sometimes the racer's name wasn't recorded
    {'name': None, 'items': ['mushroom',], 'finish': 2},
    {'name': 'Toad', 'items': ['green shell', 'mushroom'], 'finish': 1},
]

'items' is a list of all the power-up items the racer picked up in that race, and 'finish' was their placement in the race (1 for first place, 3 for third, etc.).

He wrote the function below to take a list like this and return a dictionary mapping each item to how many times it was picked up by first-place finishers.

items是赛车手在比赛中获得的所有强化物品的列表,finish是他们在比赛中的位置(1 表示第一名,3 表示第三名,等等)。

他编写了下面的函数来获取这样的列表,并返回一个字典,该字典将每个项目映射到第一名完成者拾取该项目的次数。

def best_items(racers):
    """Given a list of racer dictionaries, return a dictionary mapping items to the number
    of times those items were picked up by racers who finished in first place.
    """
    winner_item_counts = {}
    for i in range(len(racers)):
        # The i'th racer dictionary
        racer = racers[i]
        # We're only interested in racers who finished in first
        if racer['finish'] == 1:
            for i in racer['items']:
                # Add one to the count for this item (adding it to the dict if necessary)
                if i not in winner_item_counts:
                    winner_item_counts[i] = 0
                winner_item_counts[i] += 1

        # Data quality issues :/ Print a warning about racers with no name set. We'll take care of it later.
        if racer['name'] is None:
            print("WARNING: Encountered racer with unknown name on iteration {}/{} (racer = {})".format(
                i+1, len(racers), racer['name'])
                 )
    return winner_item_counts

He tried it on a small example list above and it seemed to work correctly:

他在上面的一个小示例列表上进行了尝试,它似乎工作正常:

sample = [
    {'name': 'Peach', 'items': ['green shell', 'banana', 'green shell',], 'finish': 3},
    {'name': 'Bowser', 'items': ['green shell',], 'finish': 1},
    {'name': None, 'items': ['mushroom',], 'finish': 2},
    {'name': 'Toad', 'items': ['green shell', 'mushroom'], 'finish': 1},
]
best_items(sample)
WARNING: Encountered racer with unknown name on iteration 3/4 (racer = None)

{'green shell': 2, 'mushroom': 1}

However, when he tried running it on his full dataset, the program crashed with a TypeError.

Can you guess why? Try running the code cell below to see the error message Luigi is getting. Once you've identified the bug, fix it in the cell below (so that it runs without any errors).

Hint: Luigi's bug is similar to one we encountered in the tutorial when we talked about star imports.

然而,当他尝试在完整数据集上运行它时,程序崩溃并出现TypeError

你能猜出为什么吗? 尝试运行下面的代码单元格以查看 Luigi 收到的错误消息。 一旦您发现了错误,请在下面的单元格中修复它(以便它运行时不会出现任何错误)。

提示:Luigi 的错误类似于我们在教程 中讨论*导入时遇到的错误。

# Import luigi's full dataset of race data
from learntools.python.luigi_analysis import full_dataset

# Fix me!
def best_items(racers):
    winner_item_counts = {}
    for idx in range(len(racers)):
        # The i'th racer dictionary
        racer = racers[idx]
        # We're only interested in racers who finished in first
        if racer['finish'] == 1:
            for i in racer['items']:
                # Add one to the count for this item (adding it to the dict if necessary)
                if i not in winner_item_counts:
                    winner_item_counts[i] = 0
                winner_item_counts[i] += 1

        # Data quality issues :/ Print a warning about racers with no name set. We'll take care of it later.
        if racer['name'] is None:
            print("WARNING: Encountered racer with unknown name on iteration {}/{} (racer = {})".format(
                idx+1, len(racers), racer['name'])
                 )
    return winner_item_counts

# Try analyzing the imported full dataset
best_items(full_dataset)
WARNING: Encountered racer with unknown name on iteration 4/8 (racer = None)
WARNING: Encountered racer with unknown name on iteration 6/8 (racer = None)

{'green shell': 4, 'banana': 1, 'red shell': 1, 'blue shell': 1, 'star': 1}
#q2.hint()
# Check your answer (Run this code cell to receive credit!)
q2.solution()

Solution: Luigi used the variable name i to represent each item in racer['items'].
However, he also used i as the loop variable for the outer loop (for i in range(len(racers))).
These i's are clobbering each other. This becomes a problem only if we encounter a racer
with a finish of 1 and a name of None. If that happens, when we try to print the "WARNING" message,
i refers to a string like "green shell", which python can't add to an integer, hence a TypeError.

This is similar to the issue we saw when we imported * from math and numpy. They both contained variables called log, and the one we got when we tried to call it was the wrong one.

We can fix this by using different loop variables for the inner and outer loops. i wasn't a very
good variable name for the inner loop anyways. for item in racer['items'] fixes the bug and is
easier to read.

Variable shadowing bugs like this don't come up super often, but when they do they can take an infuriating amount of time to diagnose!

3. 🌶️

Suppose we wanted to create a new type to represent hands in blackjack. One thing we might want to do with this type is overload the comparison operators like > and <= so that we could use them to check whether one hand beats another. e.g. it'd be cool if we could do this:

假设我们想创建一个新类型来表示二十一点中的手牌。 我们可能想要对这种类型做的一件事是重载><=等比较运算符,以便我们可以使用它们来检查一只手是否击败了另一只手。 例如 如果我们能这样做那就太酷了:

>>> hand1 = BlackjackHand(['K', 'A'])
>>> hand2 = BlackjackHand(['7', '10', 'A'])
>>> hand1 > hand2
True

Well, we're not going to do all that in this question (defining custom classes is a bit beyond the scope of these lessons), but the code we're asking you to write in the function below is very similar to what we'd have to write if we were defining our own BlackjackHand class. (We'd put it in the __gt__ magic method to define our custom behaviour for >.)

Fill in the body of the blackjack_hand_greater_than function according to the docstring.

好吧,我们不会在这个问题中完成所有这些(定义自定义类有点超出了这些课程的范围),但是我们要求您在下面的函数中编写的代码与我们的代码非常相似 如果我们定义自己的BlackjackHand类,则必须编写。 (我们将其放入__gt__魔术方法中来定义>的自定义行为。)

根据文档字符串填写blackjack_hand_greater_than函数的主体。

def BlackjackHand(hands):
    hands_sum = 0
    num_ace = 0
    for card in hands:
        if card in ('J','Q','K'):
            card_num = 10
            hands_sum += card_num
        elif card in ('A'):
            num_ace += 1
        else:
            hands_sum += int(card)
            #print(card, hands_sum)

    hands_sum += num_ace
    for i in range(num_ace):
        if hands_sum + 10 <= 21:
            hands_sum += 10
    return hands_sum
hands = ['A','A','9']
BlackjackHand(hands)
21
def blackjack_hand_greater_than(hand_1, hand_2):
    """
    Return True if hand_1 beats hand_2, and False otherwise.

    In order for hand_1 to beat hand_2 the following must be true:
    - The total of hand_1 must not exceed 21
    - The total of hand_1 must exceed the total of hand_2 OR hand_2's total must exceed 21

    Hands are represented as a list of cards. Each card is represented by a string.

    When adding up a hand's total, cards with numbers count for that many points. Face
    cards ('J', 'Q', and 'K') are worth 10 points. 'A' can count for 1 or 11.

    When determining a hand's total, you should try to count aces in the way that 
    maximizes the hand's total without going over 21. e.g. the total of ['A', 'A', '9'] is 21,
    the total of ['A', 'A', '9', '3'] is 14.

    如果 hand_1 击败 hand_2,则返回 True,否则返回 False。
    为了让 hand_1 击败 hand_2,必须满足以下条件:
     - hand_1 的总数不得超过 21
     - hand_1 的总数必须超过 hand_2 的总数或 hand_2 的总数必须超过 21

    手牌被表示为一个牌的列表。 每张卡片都由一个字符串表示。

     将一手牌的总点数相加时,带有数字的牌将计入相应的点数。 卡片(“J”、“Q”和“K”)价值 10 分。 “A”可以数为 1 或 11。

     在确定一手牌的总数时,您应该尝试按照以下方式计算 A:
     最大化手牌总数而不超过 21。 ['A', 'A', '9'] 的总数为 21,
     ['A', 'A', '9', '3'] 的总数为 14。

    Examples:
    >>> blackjack_hand_greater_than(['K'], ['3', '4'])
    True
    >>> blackjack_hand_greater_than(['K'], ['10'])
    False
    >>> blackjack_hand_greater_than(['K', 'K', '2'], ['3'])
    False
    """
    #pass
    total_1 = BlackjackHand(hand_1)
    total_2 = BlackjackHand(hand_2)

    return total_1 <= 21 and (total_1 > total_2 or total_2 > 21)

# Check your answer
q3.check()

Correct

#q3.hint()
q3.solution()

Solution:

def hand_total(hand):
    """Helper function to calculate the total points of a blackjack hand.
    """
    total = 0
    # Count the number of aces and deal with how to apply them at the end.
    aces = 0
    for card in hand:
        if card in ['J', 'Q', 'K']:
            total += 10
        elif card == 'A':
            aces += 1
        else:
            # Convert number cards (e.g. '7') to ints
            total += int(card)
    # At this point, total is the sum of this hand's cards *not counting aces*.

    # Add aces, counting them as 1 for now. This is the smallest total we can make from this hand
    total += aces
    # "Upgrade" aces from 1 to 11 as long as it helps us get closer to 21
    # without busting
    while total + 10 <= 21 and aces > 0:
        # Upgrade an ace from 1 to 11
        total += 10
        aces -= 1
    return total

def blackjack_hand_greater_than(hand_1, hand_2):
    total_1 = hand_total(hand_1)
    total_2 = hand_total(hand_2)
    return total_1 <= 21 and (total_1 > total_2 or total_2 > 21)

The end

You've finished the Python course. Congrats!

As always, if you have any questions about these exercises, or anything else you encountered in the course, come to the Learn Forum.

You probably didn't put in all these hours of learning Python just to play silly games of chance, right? If you're interested in applying your newfound Python skills to some data science tasks, check out some of our other Kaggle Courses. Some good next steps are:

  1. Machine learning with scikit-learn
  2. Pandas for data manipulation
  3. Deep learning with TensorFlow

Happy Pythoning!

最后

您已经完成了 Python 课程。 恭喜!

与往常一样,如果您对这些练习有任何疑问,或者在课程中遇到任何其他问题,请访问学习论坛

您投入这么多时间学习 Python 可能不会只是为了玩愚蠢的机会游戏,对吧? 如果您有兴趣将新发现的 Python 技能应用于某些数据科学任务,请查看我们的一些其他 Kaggle 课程。 一些好的后续步骤是:

  1. 使用 scikit-learn 进行机器学习
  2. 用于数据操作的 Pandas
  3. 使用TensorFlow进行深度学习

快乐Python!

07.exercise-working-with-external-libraries【练习:使用外部库】

Leave a Reply

Your email address will not be published. Required fields are marked *

Scroll to top