-
Notifications
You must be signed in to change notification settings - Fork 34
/
Copy pathIII Gaining efficiencies.py
460 lines (360 loc) · 18.5 KB
/
III Gaining efficiencies.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
"""********************************************************************************************************************
This chapter covers more complex efficiency tips and tricks. You'll learn a few useful built-in modules for writing efficient code and practice
using set theory. You'll then learn about looping patterns in Python and how to make them more efficient.
Efficiently combining, counting, and iterating
==============================================
// combine pokemon with healthpoits list
>>>>>>>> zip ===combine lists easily
$$$$$ must be unpacked into a list and print $$$$$$
combined_zip = zip(names,hp) (zip object)
combined_zip_list = [*combined_zip]
Collections module
===================
>>>>>>>> Counter (collections module)
from collections import Counter
type_counts = Counter(poke_types)
print(type_counts)
Itertools module (combinactory generators)
=================
>>>>>>>> combinations() (itertools module)
poke_types = ['Bug','Fire','Ghost','Grass','Water']
from itertools import combinations
comb_obj = combinations(poke_types,2) # list, umber_of_objects_per_group
combos = [*comb_obj]
print(combos)
# [('Bug','Fire'),('Bug','Ghost'),('Bug','Grass')........
********************************************************************************************************************"""
## Combining Pokémon names and types 1
# Combine names and primary_types
names_type1 = [*zip(names, primary_types)]
print(*names_type1[:5], sep='\n')
"""('Abomasnow', 'Grass')
('Abra', 'Psychic')
('Absol', 'Dark')
('Accelgor', 'Bug')
('Aerodactyl', 'Rock')"""
## Combining Pokémon names and types 2
# Combine all three lists together
names_types = [*zip(names,primary_types,secondary_types)]
print(*names_types[:5], sep='\n')
"""('Abomasnow', 'Grass', 'Ice')
('Abra', 'Psychic', nan)
('Absol', 'Dark', nan)
('Accelgor', 'Bug', nan)
('Aerodactyl', 'Rock', 'Flying')"""
## Combining Pokémon names and types 3
# Combine five items from names and three items from primary_types
differing_lengths = [*zip(names[:5], primary_types[:3])]
print(*differing_lengths, sep='\n')
"""('Abomasnow', 'Grass')
('Abra', 'Psychic')
('Absol', 'Dark')"""
# $$$$$ zip() with objects of differing lengths, it will only combine until the smallest lengthed object is exhausted $$$$$
#````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````
## Counting Pokémon from a sample
# Collect the count of primary types
type_count = Counter(primary_types)
print(type_count, '\n')
# Collect the count of generations
gen_count = Counter(generations)
print(gen_count, '\n')
# Use list comprehension to get each Pokémon's starting letter
starting_letters = [name[0] for name in names]
# Collect the count of Pokémon for each starting_letter
starting_letters_count = Counter(starting_letters)
print(starting_letters_count)
"""Counter({'Water': 66, 'Normal': 64, 'Bug': 51, 'Grass': 47, 'Psychic': 31, 'Rock': 29, 'Fire': 27, 'Electric': 25, 'Ground': 23, 'Fighting': 23, 'Poison': 22, 'Steel': 18, 'Ice': 16, 'Fairy': 16, 'Dragon': 16, 'Ghost': 13, 'Dark': 13})
Counter({5: 122, 3: 103, 1: 99, 4: 78, 2: 51, 6: 47})
Counter({'S': 83, 'C': 46, 'D': 33, 'M': 32, 'L': 29, 'G': 29, 'B': 28, 'P': 23, 'A': 22, 'K': 20, 'E': 19, 'W': 19, 'T': 19, 'F': 18, 'H': 15, 'R': 14, 'N': 13, 'V': 10, 'Z': 8, 'J': 7, 'I': 4, 'O': 3, 'Y': 3, 'U': 2, 'X': 1})"""
"""!!!
The sample's most common Pokémon type was 'Water' and the sample's least common Pokémon
types were 'Ghost' and 'Dark'. Did you also notice that most of the Pokémon in the sample came from generation 5 and had a starting letter of 'S'?"""
#````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````
## Combinations of Pokémon
# Import combinations from itertools
from itertools import combinations
# Create a combination object with pairs of Pokémon
combos_obj = combinations(pokemon, 2)
print(type(combos_obj), '\n')
# Convert combos_obj to a list by unpacking
combos_2 = [*combos_obj]
print(combos_2, '\n')
# Collect all possible combinations of 4 Pokémon directly into a list
combos_4 = [*combinations(pokemon,4)]
print(combos_4)
"""<class 'itertools.combinations'>
[('Geodude', 'Cubone'), ('Geodude', 'Lickitung'), ('Geodude', 'Persian'), ('Geodude', 'Diglett'), ('Cubone', 'Lickitung'), ('Cubone', 'Persian'), ('Cubone', 'Diglett'), ('Lickitung', 'Persian'), ('Lickitung', 'Diglett'), ('Persian', 'Diglett')]
[('Geodude', 'Cubone', 'Lickitung', 'Persian'), ('Geodude', 'Cubone', 'Lickitung', 'Diglett'), ('Geodude', 'Cubone', 'Persian', 'Diglett'), ('Geodude', 'Lickitung', 'Persian', 'Diglett'), ('Cubone', 'Lickitung', 'Persian', 'Diglett')]"""
"""!!!
combinations() allows you to specify any size of combinations by passing an integer
as the second argument. Ash has 10 combination options when his Pokédex can store only two Pokémon.
He has 5 combination options when his Pokédex can store four Pokémon."""
"""*********************************************************************************************************************
Set theory
===========
- Applied to collections of objects
>>>>>>>>>> sets
0 >>>>>>>>>in
1 >>>>>>>>>>intersection() 2 >>>>>>>>>>difference() 3 >>>>>>>>>>symmetric_difference() 4 >>>>>>>>>>union()
0 -> if value exists in a sequence
1 -> all elements that are in both sets
2 -> all elements in one set but not in other
3 -> all elems in exactly one set---all differences of both together
4 -> all elems that are in either set--- deletes duplicates, merges uniques
$$$$$ fisrt convert the list into a set $$$$$$
list_a = [1,2,3,4]
list_b = [5,6,7,4]
list_a = set(lsit_a)
list_b = set(list_b)
set_a.intersection(list_b)
# {4}
set_a.difference(set_b)
# {1,2,3}
set_a.symmetric_difference(set_b)
# {1,2,3,5,6,7}
set_a.union(set_b)
# {1,2,3,4,5,6,7}
*********************************************************************************************************************"""
## Comparing Pokédexes
# Convert both lists to sets
ash_set = set(ash_pokedex)
misty_set = set(misty_pokedex)
# Find the Pokémon that exist in both sets
both = ash_set.intersection(misty_set)
print(both, '\n')
# Find the Pokémon that Ash has and Misty does not have
ash_only = ash_set.difference(misty_set)
print(ash_only, '\n')
# Find the Pokémon that are in only one set (not both)
unique_to_set = ash_set.symmetric_difference(misty_set)
print(unique_to_set, '\n')
"""{'Squirtle', 'Psyduck'}
{'Koffing', 'Zubat', 'Rattata', 'Spearow', 'Vulpix', 'Wigglytuff', 'Pikachu', 'Bulbasaur'}
{'Koffing', 'Zubat', 'Spearow', 'Vulpix', 'Rattata', 'Wigglytuff', 'Tentacool', 'Slowbro', 'Krabby', 'Bulbasaur', 'Pikachu', 'Poliwag', 'Magikarp', 'Vaporeon', 'Starmie', 'Horsea'} """
"""!!!
you were able to see that both Ash and Misty have 'Psyduck' and 'Squirtle' in their Pokédex.
You were also able to see that Ash has 8 Pokémon that Misty does not have."""
#````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````
## Searching for Pokémon 1
# Convert Brock's Pokédex to a set
brock_pokedex_set = set(brock_pokedex_set)
print(brock_pokedex_set)
"""{'Onix', 'Machop', 'Zubat', 'Vulpix', 'Kabutops', 'Geodude', 'Dugtrio', 'Golem', 'Omastar', 'Tauros'}"""
## Searching for Pokémon 2
# Convert Brock's Pokédex to a set
brock_pokedex_set = set(brock_pokedex)
print(brock_pokedex_set)
# Check if Psyduck is in Ash's list and Brock's set
print('Psyduck' in ash_pokedex)
print('Psyduck' in brock_pokedex_set)
"""{'Onix', 'Machop', 'Zubat', 'Vulpix', 'Kabutops', 'Geodude', 'Dugtrio', 'Golem', 'Omastar', 'Tauros'}
True
False"""
## Searching for Pokémon 3
# Convert Brock's Pokédex to a set
brock_pokedex_set = set(brock_pokedex)
print(brock_pokedex_set)
# Check if Psyduck is in Ash's list and Brock's set
print('Psyduck' in ash_pokedex)
print('Psyduck' in brock_pokedex_set)
# Check if Machop is in Ash's list and Brock's set
print('Machop' in ash_pokedex)
print('Machop' in brock_pokedex_set)
"""{'Onix', 'Machop', 'Zubat', 'Vulpix', 'Kabutops', 'Geodude', 'Dugtrio', 'Golem', 'Omastar', 'Tauros'}
True
False
False
True"""
## Searching for Pokémon 4
"""---
Question
Within your IPython console, use %timeit to compare membership testing for 'Psyduck' in ash_pokedex,
'Psyduck' in brock_pokedex_set, 'Machop' in ash_pokedex, and 'Machop' in brock_pokedex_set (a total of four different timings)."""
"""In [1]:
%timeit 'Psyduck' in ash_pokedex
%timeit 'Psyduck' in brock_pokedex_set
%timeit 'Machop' in ash_pokedex
%timeit 'Machop' in ash_pokedex
247 ns +- 13.7 ns per loop (mean +- std. dev. of 7 runs, 1000000 loops each)
53.3 ns +- 2.41 ns per loop (mean +- std. dev. of 7 runs, 10000000 loops each)
254 ns +- 21.1 ns per loop (mean +- std. dev. of 7 runs, 1000000 loops each)
285 ns +- 42.3 ns per loop (mean +- std. dev. of 7 runs, 1000000 loops each)"""
# Member testing using a set is faster than using a list in all four cases.
#````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````
## Gathering unique Pokémon 1
# Use the provided function to collect unique Pokémon names
uniq_names_func = find_unique_items(names)
print(len(uniq_names_func))
#368
## Gathering unique Pokémon 2
# Use find_unique_items() to collect unique Pokémon names
uniq_names_func = find_unique_items(names)
print(len(uniq_names_func))
# Convert the names list to a set to collect unique Pokémon names
uniq_names_set = set(names)
print(len(uniq_names_set))
# Check that both unique collections are equivalent
print(sorted(uniq_names_func) == sorted(uniq_names_set))
"""368
368
True"""
## Gathering unique Pokémon 3
"""---
Question
Within your IPython console, use %timeit to compare the find_unique_items() function with using a set data type to collect unique Pokémon character names in names.
Which membership testing was faster?"""
# Using a set to collect unique values is faster.
## Gathering unique Pokémon 4
# Use find_unique_items() to collect unique Pokémon names
uniq_names_func = find_unique_items(names)
print(len(uniq_names_func))
# Convert the names list to a set to collect unique Pokémon names
uniq_names_set = set(names)
print(len(uniq_names_set))
# Check that both unique collections are equivalent
print(sorted(uniq_names_func) == sorted(uniq_names_set))
# Use the best approach to collect unique primary types and generations
uniq_types = set(primary_types)
uniq_gens = set(generations)
print(uniq_types, uniq_gens, sep='\n')
"""{'Dragon', 'Rock', 'Fire', 'Electric', 'Fairy', 'Ghost', 'Psychic', 'Ground', 'Grass', 'Ice', 'Fighting', 'Dark', 'Poison', 'Bug', 'Water', 'Steel', 'Normal'}
{1, 2, 3, 4, 5, 6}"""
"""!!!
Since a set is defined as a collection of distinct elements, it is an efficient way to collect unique items
from an existing object. Here you took advantage of a set to find the distinct Pokémon from the sample
(eliminating duplicate Pokémon) and saw what unique Pokémon types and generations were included in the sample."""
"""*********************************************************************************************************************
Eliminating loops (w/ Built-in)
==============================
#-- List HP, attack, deffence, speed
poke_stats = [[90,92,75,70],[25,20,15,90],[65,130,,60,75]]
# List comprehension
totals_comp = [sum(row) for row in poke_stats]
# Built-in map() function for adding
faster totals_map = [*map(sum, poke_stats)]
#-------
# Built-in module approach for combination
from itertools import combinations
combos2 = [*combinations(poke_types, 2)]
$$$$ FASTER MAP THAN LIST COMPREHENSION $$$$
$$$$ FASTER combinations(itertools) THAN FOR LOOP $$$$
Eliminating loops (w/ NUMPY)
==============================
#-- List HP, attack, deffence, speed
poke_stats = [[90,92,75,70],[25,20,15,90],[65,130,,60,75]]
import numpy as np
poke_stats_np = np.array(poke_stats)
poke_mean_row1 = poke_stats_np.mean(axis= 1)
*********************************************************************************************************************"""
## Gathering Pokémon without a loop
# Collect Pokémon that belong to generation 1 or generation 2
gen1_gen2_pokemon = [name for name,gen in zip(
poke_names, poke_gens) if gen < 3]
# Create a map object that stores the name lengths
name_lengths_map = map(len, gen1_gen2_pokemon)
# Combine gen1_gen2_pokemon and name_lengths_map into a list
gen1_gen2_name_lengths = [*zip(gen1_gen2_pokemon, name_lengths_map)]
print(gen1_gen2_name_lengths_loop[:5])
print(gen1_gen2_name_lengths[:5])
"""[('Abra', 4), ('Aerodactyl', 10), ('Aipom', 5), ('Alakazam', 8), ('Ampharos', 8)]
[('Abra', 4), ('Aerodactyl', 10), ('Aipom', 5), ('Alakazam', 8), ('Ampharos', 8)]"""
#````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````
## Pokémon totals and averages without a loop
# Create a total stats array
total_stats_np = stats.sum(axis=1)
# Create an average stats array
avg_stats_np = stats.mean(axis=1)
# Combine names, total_stats_np, and avg_stats_np into a list
poke_list_np = [*zip(names, total_stats_np, avg_stats_np)]
print(poke_list_np == poke_list, '\n')
print(poke_list_np[:3])
print(poke_list[:3], '\n')
# $$$$ lambda effectively creates an inline function $$$$
# mylist = [[7, 8], [1, 2, 3], [2, 5, 6]]
# list(map(lambda x: x[1], mylist)) returns [8, 2 ,5]
top_3 = sorted(poke_list_np, key=lambda x: x[1], reverse=True)[:3]
print('3 strongest Pokémon:\n{}'.format(top_3))
"""
True
[('Abomasnow', 494, 82.33333333333333), ('Abra', 310, 51.666666666666664), ('Absol', 465, 77.5)]
[('Abomasnow', 494, 82.33333333333333), ('Abra', 310, 51.666666666666664), ('Absol', 465, 77.5)]
3 strongest Pokémon:
[('GroudonPrimal Groudon', 770, 128.33333333333334), ('KyogrePrimal Kyogre', 770, 128.33333333333334), ('Arceus', 720, 120.0)]"""
#````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````
## One-time calculation loop
# Import Counter
from collections import Counter
# Collect the count of each generation
gen_counts = Counter(generations)
# Improve for loop by moving one calculation above the loop
total_count = len(generations)
for gen,count in gen_counts.items():
gen_percent = round(count / total_count * 100, 2)
print('generation {}: count = {:3} percentage = {}'
.format(gen, count, gen_percent))
"""generation 4: count = 112 percentage = 15.56
generation 1: count = 151 percentage = 20.97
generation 3: count = 136 percentage = 18.89
generation 5: count = 154 percentage = 21.39
generation 2: count = 99 percentage = 13.75
generation 6: count = 68 percentage = 9.44"""
"""!!!
You spotted a calculation that could be moved outside a loop to make the loop more efficient.
When writing a loop is unavoidable, be sure to analyze the loop and move any one-time calculations outside."""
#````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````
## Holistic conversion loop
# Collect all possible pairs using combinations()
possible_pairs = [*combinations(pokemon_types, 2)]
# Create an empty list called enumerated_tuples
enumerated_tuples = []
# Add a line to append each enumerated_pair_tuple to the empty list above
for i, pair in enumerate(possible_pairs, 1):
enumerated_pair_tuple = (i,) + pair
enumerated_pair_list = list(enumerated_pair_tuple)
enumerated_tuples.append(enumerated_pair_list)
# Convert all tuples in enumerated_tuples to a list
enumerated_pairs = [*map(list, enumerated_tuples)]
print(enumerated_pairs)
"""[[1, 'Bug', 'Dark'], [2, 'Bug', 'Dragon'], [3, 'Bug', 'Electric'],....."""
#````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````
## Bringing it all together: Pokémon z-scores 1
# Calculate the total HP avg and total HP standard deviation
hp_avg = hps.mean()
hp_std = hps.std()
"""standad deviation : to see how many standard deviations each Pokémon's HP is from the mean of all HPs."""
# Use NumPy to eliminate the previous for loop
z_scores = (hps - hp_avg)/hp_std
# Combine names, hps, and z_scores
poke_zscores2 = [*zip(names, hps, z_scores)]
print(*poke_zscores2[:3], sep='\n')
"""('Abomasnow', 80.0, 0.46797638117739043)
('Abra', 60.0, -0.3271693284337512)
('Absol', 131.0, 2.4955979406858013)"""
#````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````
## Bringing it all together: Pokémon z-scores 2
# Calculate the total HP avg and total HP standard deviation
hp_avg = hps.mean()
hp_std = hps.std()
# Use NumPy to eliminate the previous for loop
z_scores = (hps - hp_avg)/hp_std
# Combine names, hps, and z_scores
poke_zscores2 = [*zip(names, hps, z_scores)]
print(*poke_zscores2[:3], sep='\n')
# Use list comprehension with the same logic as the highest_hp_pokemon code block
highest_hp_pokemon2 = [(name, hp, zscore) for name,hp,zscore in poke_zscores2 if zscore > 2]
print(*highest_hp_pokemon2, sep='\n')
#````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````
## Bringing it all together: Pokémon z-scores 3
"""-----Question
Use %%timeit (cell magic mode) within your IPython console to compare the runtimes between the original code
blocks and the new code you developed using NumPy and list comprehension.
Which approach was the faster?"""
%%timeit
poke_zscores = []
for name,hp in zip(names, hps):
hp_avg = hps.mean()
hp_std = hps.std()
z_score = (hp - hp_avg)/hp_std
poke_zscores.append((name, hp, z_score))
#126 ms +- 5.19 ms per loop
# The total time for executing the updated solution using NumPy and list comprehension was faster.