I have multiple hands of a Blackjack game.
ex.
Player 1 = ['AD', 'AS']
Player 2 = ['6C', '3D']
Player 3 = ['TD', 'AH']
And I'm trying to get the value of each hand by referencing it to a deck value dictionary:
deckValue = {'AH':11, '2H':2,'3H':3, '4H':4, '5H':5, '6H': 6, '7H': 7, '8H':8,
'9H':9, 'TH':10, 'JH':10, 'QH':10, 'KH':10, 'AC':11, '2C':2,'3C':3,
'4C':4, '5C':5, '6C':6,'7C': 7, '8C':8, '9C':9, 'TC':10, 'JC':10,
'QC':10, 'KC':10, 'AS':11, '2S':2,'3S':3, '4S':4, '5S':5, '6S': 6,
'7S': 7, '8S':8, '9S':9, 'TS':10, 'JS':10, 'QS':10, 'KS':10,
'AD':11, '2D':2,'3D':3, '4D':4, '5D':5, '6D': 6, '7D': 7, '8D':8,
'9D':9, 'TD':10, 'JD':10, 'QD':10, 'KD':10}
Finding a value in a dictionary can be achieved with the
deckvalue.get(key)
or
deckvalue[key]
Where key would be the string in each hand ex. deckvalue.get('AH') = 11
To achieve what I'm trying to do, I'm using a for loop to go through each hand and find the total value.
def calculate_hand(pHands):
# What I have so far
total = 0
for i in pHands:
deckValue.get(pHands[i][0])
deckValue.get(pHands[i][1])
total = #value of 0 + value of 1
return
Where pHands is:
pHands = [['AD', 'AS'], ['6C', '3D'], ['TD', 'AH']]
But I'm getting a 'list indices must be integers, not list' error
I'm new to Python, so I have no clue what this is (but it's probably due to the pHands containing str elements not int elements).
How could I get individual totals for each hand?
eg.
pHands[0] = 22
pHands[1] = 9
pHands[2] = 21
Would I have to assign a new variable to save each hands total to?
Thanks for any input.
If you're iterating over a list, you don't get indices, but actually the list elements. Better:
def calculate_hand(pHands):
# What I have so far
values = []
for (card1, card2) in pHands:
total = deckValue.get(card1) + deckValue.get(card2)
values.append(total)
return values
Also, there's a more concise way to get the value of a card:
def getvalue(card):
val = card[0]
return 11 if val=="A" else 10 if val in "TQKJ" else int(val)
If you have a function like that, you can actually write the whole calculate_hand function in single list comprehension:
def calculate_hand(pHands):
return [getvalue(card1) + getvalue(card2) for (card1, card2) in pHands]
Related
I'm attempting to calculate the value based on a sequence of numbers, and find out how that value compares to if the sequence of numbers was randomly generated. To calculate the value of the sequence I have the random_lineup_value function, which works fine. To randomly generate 1000 sequences and calculate the value of each I have the random_lineup_values function.
However, the issue is that I can't compare the original value to these randomly generated values, because the output of the function is not subscriptable- or I cannot reference specific positions in this output. I'm trying to iterate through the ordered output of random_lineup_values until the initial stored value is equal to the number stored in the list of 1000 values. I've tried to return the output of random_lineup_value as a list, to use yield instead of return in the function, and to assign the output to a variable when I call the function and then try to iterate through positions of the list stored in the new variable, but none of these approaches work in Python. Any idea how to do this in Python?
import random
def lineup_value(order):
value = 0
for i in range(0,6):
if order[i] >= order[8]:
value = value + 4
if order[i] >= order[7]:
value = value + 4
for i in range(0,5):
if order[i] >= order[6]:
value = value + 4
for i in [0, 1, 3]:
if order[i] >= order[5]:
value = value + 4
if order[i] >= order[4]:
value = value + 4
if order[3] >= order[2]:
value = value + 4
if order[1] >= order[2]:
value = value + 4
return value
def random_lineup_values(order):
bootstrapped_values = []
for j in range(0, 1000):
random.shuffle(order)
bootstrapped_values.append(lineup_value(order))
return list(bootstrapped_values.sort)
random.seed(10)
batting_order = [115, 101, 76, 125, 120, 135, 88, 94, 97]
value = lineup_value(batting_order)
print(str(value) + ' is the value of the lineup')
sorted1 = random_lineup_values(batting_order)
print(sorted1)
p = 0
number = sorted1[p]
while int(number) < int(value):
p = p + 1 print(p)
print(str((1000 - p) / 10) + ' percent of random lineup values would be better than the lineup')
The error is here:
x = list(bootstrapped_values.sort)
by removing .sort it works.
x = list(bootstrapped_values)
here is the function
def random_lineup_values(order):
bootstrapped_values = []
for j in range(0, 1000):
random.shuffle(order)
bootstrapped_values.append(lineup_value(order))
x = list(bootstrapped_values) # <== Exception has occurred: TypeError 'builtin_function_or_method' object is not iterable
return x
The error was this:
Exception has occurred: TypeError 'builtin_function_or_method' object is not iterable
I suspect the issue you have is that you're only sorting and keeping track of the scores, not the order list that produced them. If you keep both things together in the same place, you can do what you want. (You also have a more glaring issue solved by #D.L's answer, but I suspect that issue was caused by an attempted solution to the larger issue which I'm addressing.)
Try this, which builds and sorts a list of (score, order) 2-tuples.
def random_lineup_values(order):
bootstrapped_values = []
for j in range(0, 1000):
random.shuffle(order)
bootstrapped_values.append(lineup_value(order), order.copy())
bootstrapped_values.sort()
return bootstrapped_values
Note that I'm adding a copy() of the order list in the tuple because the shuffling to build new orders happens in place. This may also mean you'll want to copy the original lineup too at the start of the function, so you don't destroy the order of the list you're passed as an argument (but I don't fully understand the calling code, so I'm not sure if you care about that or not).
I didn't initially understand your question, but based on your code, I guessed that you wanted to run a list that contained a batting order through a function that determined that order's "value", and compare that to 1000 randomly generated different order's values. I made a couple of slight adjustments, and now it does what I believe you were trying to accomplish.
import random
def lineup_value(order):
value = 0
for i in range(0,6):
if order[i] >= order[8]:
value = value + 4
if order[i] >= order[7]:
value = value + 4
for i in range(0,5):
if order[i] >= order[6]:
value = value + 4
for i in [0, 1, 3]:
if order[i] >= order[5]:
value = value + 4
if order[i] >= order[4]:
value = value + 4
if order[3] >= order[2]:
value = value + 4
if order[1] >= order[2]:
value = value + 4
return value
def random_lineup_values(order, value):
bootstrapped_values = []
newBootStrapped_values = []
for j in range(0, 1000):
random.shuffle(order)
bootstrapped_values.append(lineup_value(order))
for j in range(0, 1000):
if(bootstrapped_values[j] > value):
newBootStrapped_values.append(bootstrapped_values[j])
return list(sorted(newBootStrapped_values))
random.seed(10)
batting_order = [115, 101, 76, 125, 120, 135, 88, 94, 97]
value = lineup_value(batting_order)
print(str(value) + ' is the value of the original lineup')
sorted1 = random_lineup_values(batting_order, value)
print(str(len(sorted1)) + ' is the number of better value lineups out of 1000')
print(str(len(sorted1) / 10) + ' percent of random lineup values would be better than the lineup')
You'll notice that the biggest adjustment I made was adding an extra for loop in your random_lineup_values function. The first for loop gets the values of the 1000 random orders. The second for loop gets all of the values that are greater than the original order value (which should be 68 from the array that you provided) and appends them to a new list. The number of elements in that list out of 1000 times 100 (sorted1/10, to simplify) will get you the percentage of better batting orders. I did not change any logic for obtaining the batting order value, so if there is something wrong there, it will have to do with the logic of the lineup_value function, but as far as I know, it is functioning correctly. The output that I got after running this code was:
68 is the value of the original lineup
113 is the number of better value lineups out of 1000
11.3 percent of random lineup values would be better than the original lineup
can anyone explain why my code for a hacker rank example is timing out. I'm new to whole idea of efficiency of code based on processing time. The code seems to work on small sets, but once I start testing cases using large datasets it times out. I've provided a brief explanation of the method and its purpose for context. But if you could provide any tips if you notice functions I'm using that might consume a large amount of runtime that would be great.
Complete the migratoryBirds function below.
Params: arr: an array of tallies of species of birds sighted by index.
For example. arr = [Type1 = 1, Type2 = 4, Type3 = 4, Type4 = 4, Type5 = 5, Type6 = 3]
Return the lowest type of the the mode of sightings. In this case 4 sightings is the
mode. Type2 is the lowest type that has the mode. So return integer 2.
def migratoryBirds(arr):
# list of counts of occurrences of birds types with the same
# number of sightings
bird_count_mode = []
for i in range(1, len(arr) + 1):
occurr_count = arr.count(i)
bird_count_mode.append(occurr_count)
most_common_count = max(bird_count_mode)
common_count_index = bird_count_mode.index(most_common_count) + 1
# Find the first occurrence of that common_count_index in arr
# lowest_type_bird = arr.index(common_count_index) + 1
# Expect Input: [1,4,4,4,5,3]
# Expect Output: [1 0 1 3 1 0], 3, 4
return bird_count_mode, most_common_count, common_count_index
P.S. Thank you for the edit Chris Charley. I just tried to edit it at the same time
Use collections.Counter() to create a dictionary that maps species to their counts. Get the maximum count from this, then get all the species with that count. Then search the list for the first element of one of those species.
import collections
def migratoryBirds(arr):
species_counts = collections.Counter(arr)
most_common_count = max(species_counts.values())
most_common_species = {species for species, count in species_counts if count = most_common_count}
for i, species in arr:
if species in most_common_species:
return i
I am quite new to python so still getting to grips with the language.
I have the following function which takes a string and apply it to an algorithm which tells us if it aligns to models 1, 2, 3, 4, or 5.
Currently this piece of code:
def apply_text(text):
test_str = [text]
test_new = tfidf_m.transform(test_str)
prediction = 0
for m in range(0,5):
percentage = '{P:.1%}'.format(M=cat[m], P=lr_m[m].predict_proba(test_new)[0][1])
print(percentage)
And running the following function: apply_text('Terrible idea.')
Gives the following output:
71.4%
33.1%
2.9%
1.6%
4.9%
With Model 1 = 71.4%, Model 2 = 33.1%, ... Model 5 = 4.9%.
I want to only output the Model number where there is the highest percentage. So in the above example, the answer would be 1 as this has 71.4%.
As the output is a string type I am finding it difficult to find ways of converting this to an int and then comparing each value (probably in a loop of some sort) to obtain the maximum value
I think you want to save the percentages along with the model number, sort it and then return the highest.
This can be done by something like this:
def apply_text(text):
test_str = [text]
test_new = tfidf_m.transform(test_str)
prediction = 0
percentage_list = []
for m in range(0,5):
percentage = '{P:.1}'.format(M=cat[m], P=lr_m[m].predict_proba(test_new)[0][1])
percentage_list.append([m+1, float(percentage)])
percentage_list.sort(reverse=True, key=lambda a: a[1])
return percentage_list[0][0]
Things to note:
Sorting in reverse order as default is ascending. You could skip reversing and access the last element of precentage_list by accessing -1 element
The key function is used as we need to sort using the percentage
Try putting values in a list then you can utilize list methods:
percentage = []
for m in range(0, 5):
percentage.append('{P:.1%}'.format(M=cat[m], P=lr_m[m].predict_proba(test_new)[0][1]))
print(*percentage, sep='\n')
print('Max on model', percentage.index(max(percentage)))
Or using a dictionary:
percentage = {}
for m in range(0, 5):
percentage['Model ' + str(m)] = '{P:.1%}'.format(M=cat[m], P=lr_m[m].predict_proba(test_new)[0][1])
print(*percentage, sep='\n')
print('Max on', max(percentage.keys(), key=(lambda key: percentage[key])))
I want to create a function (without using libraries) which takes as input three integer numbers (>0) (a,b,c) , for example:
a = 6
b = 6
c = 3
and returns a list containing c elements (so in this case the returned list should contain 3 elements) taken from a list of a numbers (so in this case the initial list is [1,2,3,4,5,6]). The c elements of the returned list have to be the ones that managed to remain in the initial list after removing a number every b positions from the list of a elements until len(return_list) = c.
So for a = 6, b = 6 and c = 3 the function should do something like this:
1) initial_list = [1,2,3,4,5,6]
2) first_change = [2,3,4,5,6] #the number after the 6th (**b**) is 1 because after the last element you keep counting returning to the first one, so 1 is canceled
3) second_change = [2,4,5,6] #you don't start to count positions from the start but from the first number after the eliminated one, so new number after the 6th is 3 and so 3 is canceled
4) third_change = [2,4,5] #now the number of elements in the list is equal to **c**
Notice that if, when counting, you end up finishing the elements from the list, you keep the counting and return to the first element of the list.
I made this function:
def findNumbers(a,b,c):
count = 0
dictionary = {}
countdown = a
for x in range(1,a+1):
dictionary[x] = True
while countdown > c:
for key,value in dictionary.items():
if value == True:
count += 1
if count == b+1:
dictionary[key] = False
count = 0
countdown -= 1
return [key for key in dictionary.keys() if dictionary[key] == True]
It works in some cases, like the above example. But it doesn't work everytime.
For example:
findNumbers(1000,1,5)
returns:
[209, 465, 721, 977] #wrong result
instead of:
[209, 465, 721, 849, 977] #right result
and for bigger numbers, like:
findNumbers(100000, 200000, 5)
it takes too much time to even do its job, I don't know if the problem is the inefficiency of my algorithm or because there's something in the code that gives problems to Python. I would like to know a different approach to this situation which could be both more efficient and able to work in every situation. Can anyone give me some hints/ideas?
Thank you in advance for your time. And let me know if you need more explanations and/or examples.
You can keep track of the index of the last list item deleted like this:
def findNumbers(a, b, c):
l = list(range(1, a + 1))
i = 0
for n in range(a - c):
i = (i + b) % (a - n)
l.pop(i)
return l
so that findNumbers(6, 6, 3) returns:
[2, 4, 5]
and findNumbers(1000, 1, 5) returns:
[209, 465, 721, 849, 977]
and findNumbers(100000, 200000, 5) returns:
[10153, 38628, 65057, 66893, 89103]
I thought I could be recursive about the problem, so I wrote this:
def func(a,b,c):
d = [i+1 for i in range(a)]
def sub(d,b,c):
if c == 0: return d
else:
k = b % len(d)
d.pop(k)
d = d[k:] + d[:k]
return sub(d,b,c-1)
return sub(d,b,a-c)
so that func(6,6,3) returns: [2, 4, 5] successfully and func(1000,1,5) returns: [209, 465, 721, 849, 977] unfortunately with an error.
It turns out that for values of a > 995, the below flag is raised:
RecursionError: maximum recursion depth exceeded while calling a Python object
There was no need to try func(100000,200000,5) - lesson learnt.
Still, rather than dispose of the code, I decided to share it. It could serve as a recursive thinking precautionary.
I would like to sort several points from smallest to biggest however.
I will wish to get this result:
Drogba 2 pts
Owen 4 pts
Henry 6 pts
However, my ranking seems to be reversed for now :-(
Henry 6 pts
Owen 4 pts
Drogba 2 pts
I think my problem is with my function Bubblesort ?
def Bubblesort(name, goal1, point):
swap = True
while swap:
swap = False
for i in range(len(name)-1):
if goal1[i+1] > goal1[i]:
goal1[i], goal1[i+1] = goal1[i+1], goal1[i]
name[i], name[i+1] = name[i+1], name[i]
point[i], point[i + 1] = point[i + 1], point[i]
swap = True
return name, goal1, point
def ranking(name, point):
for i in range(len(name)):
print(name[i], "\t" , point[i], " \t ")
name = ["Henry", "Owen", "Drogba"]
point = [0]*3
goal1 = [68, 52, 46]
gain = [6,4,2]
name, goal1, point = Bubblesort( name, goal1, point )
for i in range(len(name)):
point[i] += gain[i]
ranking (name, point)
In your code:
if goal1[i+1] > goal1[i]:
that checks if it is greater. You need to swap it if the next one is less, not greater.
Change that to:
if goal1[i+1] < goal1[i]:
A bunch of issues:
def Bubblesort - PEP8 says function names should be lowercase, ie def bubblesort
You are storing your data as a bunch of parallel lists; this makes it harder to work on and think about (and sort!). You should transpose your data so that instead of having a list of names, a list of points, a list of goals you have a list of players, each of whom has a name, points, goals.
def bubblesort(name, goal1, point): - should look like def bubblesort(items) because bubblesort does not need to know that it is getting names and goals and points and sorting on goals (specializing it that way keeps you from reusing the function later to sort other things). All it needs to know is that it is getting a list of items and that it can compare pairs of items using >, ie Item.__gt__ is defined.
Instead of using the default "native" sort order, Python sort functions usually let you pass an optional key function which allows you to tell it what to sort on - that is, sort on key(items[i]) > key(items[j]) instead of items[i] > items[j]. This is often more efficient and/or convenient than reshuffling your data to get the sort order you want.
for i in range(len(name)-1): - you are iterating more than needed. After each pass, the highest value in the remaining list gets pushed to the top (hence "bubble" sort, values rise to the top of the list like bubbles). You don't need to look at those top values again because you already know they are higher than any of the remaining values; after the nth pass, you can ignore the last n values.
actually, the situation is a bit better than that; you will often find runs of values which are already in sorted order. If you keep track of the highest index that actually got swapped, you don't need to go beyond that on your next pass.
So your sort function becomes
def bubblesort(items, *, key=None):
"""
Return items in sorted order
"""
# work on a copy of the list (don't destroy the original)
items = list(items)
# process key values - cache the result of key(item)
# so it doesn't have to be called repeatedly
keys = items if key is None else [key(item) for item in items]
# initialize the "last item to sort on the next pass" index
last_swap = len(items) - 1
# sort!
while last_swap:
ls = 0
for i in range(last_swap):
j = i + 1
if keys[i] > keys[j]:
# have to swap keys and items at the same time,
# because keys may be an alias for items
items[i], items[j], keys[i], keys[j] = items[j], items[i], keys[j], keys[i]
# made a swap - update the last_swap index
ls = i
last_swap = ls
return items
You may not be sure that this is actually correct, so let's test it:
from random import sample
def test_bubblesort(tries = 1000):
# example key function
key_fn = lambda item: (item[2], item[0], item[1])
for i in range(tries):
# create some sample data to sort
data = [sample("abcdefghijk", 3) for j in range(10)]
# no-key sort
assert bubblesort(data) == sorted(data), "Error: bubblesort({}) gives {}".format(data, bubblesort(data))
# keyed sort
assert bubblesort(data, key=key_fn) == sorted(data, key=key_fn), "Error: bubblesort({}, key) gives {}".format(data, bubblesort(data, key_fn))
test_bubblesort()
Now the rest of your code becomes
class Player:
def __init__(self, name, points, goals, gains):
self.name = name
self.points = points
self.goals = goals
self.gains = gains
players = [
Player("Henry", 0, 68, 6),
Player("Owen", 0, 52, 4),
Player("Drogba", 0, 46, 2)
]
# sort by goals
players = bubblesort(players, key = lambda player: player.goals)
# update points
for player in players:
player.points += player.gains
# show the result
for player in players:
print("{player.name:<10s} {player.points:>2d} pts".format(player=player))
which produces
Drogba 2 pts
Owen 4 pts
Henry 6 pts