
Let us return to the problem of recording the number of runs scored in every ball of an IPL match. A typical innings of a T20 match has 20 overs, each over having 6 balls. Let us assume that all balls bowled are fair deliveries that do not concede any extras, a rather liberal assumption. This leaves us with exactly 120 numbers that we need to record, all lying between 0 and 6. How can this information be stored in a Python program that makes it suitable for further processing? A list is a good candidate.
Let us now simulate an innings. For this, we take the help of the random library:
xxxxxxxxxx41import random2runs = random.choices([0, 1, 2, 3, 4, 5, 6], k = 120)3print(type(runs))4print(len(runs))choices is a function in the random library. It uniformly samples from the seven numbers (0 to 6) given in the input list with replacement. If that sounded too cryptic, this is what it does:
[0, 1, 2, 3, 4, 5, 6] at random. Each of the seven numbers is equally likely to be picked.Let us verify if the counts are approximately the same:
xxxxxxxxxx21for run in [0, 1, 2, 3, 4, 5, 6]:2 print('{} appears {} times'.format(run, runs.count(run)))runs.count(run) returns the number of times the element run appears in the list runs. count is a method defined for the list type. This gives the following output:
xxxxxxxxxx710 appears 19 times21 appears 20 times32 appears 19 times43 appears 16 times54 appears 18 times65 appears 11 times76 appears 17 times
The counts are quite close. But this is not very practical:
5 runs are seldom observed in cricket matches.0, 1 and 2 are much more common than 3, 4 and 6.We can give our preferences using a weights keyword-argument:
xxxxxxxxxx91import random2# choices is distributed over multiple lines3# this is done to improve readability4runs = random.choices([0, 1, 2, 3, 4, 5, 6], 5 weights = [30, 30, 20, 5, 10, 0, 5], 6 k = 120)7for run in [0, 1, 2, 3, 4, 5, 6]:8 print('{} appears {} times'.format(run, runs.count(run)))9print(f'Total number of runs scored = {sum(runs)}')This gives the following output:
xxxxxxxxxx810 appears 32 times21 appears 34 times32 appears 32 times43 appears 7 times54 appears 12 times65 appears 0 times76 appears 3 times8Total number of runs scored = 185
We have used sum(runs) to get the sum of the elements in the list. sum is a built-in function. The way to understand the weights keyword-argument is using the following table:
| Run | Weight |
|---|---|
| 0 | 30 |
| 1 | 30 |
| 2 | 20 |
| 3 | 5 |
| 4 | 10 |
| 5 | 0 |
| 6 | 5 |
| Total | 100 |
The weight is the importance given to a run. From the table given above, we see that 0 and 1 occur 30% of the times, 6 occurs 5% of the times and so on. choices function will keep this distribution in mind while picking up items from the input-list.
Let us now start analyzing this innings. We have already seen how to count the number of occurrences of singles, doubles, fours and sixes. What about the first occurrence of a six? In which ball was the first six scored?
xxxxxxxxxx21first_six_ball = runs.index(6) + 12print(first_six_ball)index is a method that accepts an element as input and returns the first occurrence of this element in the list. For example, runs.index(6) returns the first index where a six occurs in the list runs. Since the number of balls is one more than the index, 1 has been added. What happens if we pass an input that is not present in the list:
xxxxxxxxxx21first_five_ball = runs.index(5)2print(first_five_ball)In this case, 5 never occurs in the list. So this throws a ValueError with the following message: 5 is not in list. One must be careful while using the index method. We could have done this using another method:
xxxxxxxxxx41for ball, run in enumerate(runs):2 if run == 6:3 print(f'The first six was hit at ball number {ball + 1}')4 breakThe enumerate object can be very handy when we want to access both the element and its index while iterating through a list. The enumerate object yields pairs: (index, list[index]). In some sense, we have two loop variables: the first is the index of the element in the list while the second is the element itself. Coming back to cricket, what if we want to find the number of balls it took to score the last 50 runs in the innings? It would be easier to reverse the list and then iterate through it:
xxxxxxxxxx81balls = 02last_runs = 03for run in reversed(runs):4 last_runs += run5 balls += 16 if last_runs >= 50:7 print(f'It took {balls} balls to score the last 50 runs.')8 breakThe reversed object helps us iterate through the list in the reversed order. Note that it doesn't make any changes to the original list. One final question: we wish to find if the batsmen have run three runs at any point in the match. We don't want to know at which point in the innings this has happened.
xxxxxxxxxx21three_existence = 3 in runs2print(three_existence)Recall that we used the in keyword to check for the presence of one string in another. Something similar is happening here. The code given above prints True if 3 is an element in runs and False otherwise.