Predicted Rank Values from Dict - python-3.x

Lets say I have a time dict
dict = {
'9': {'auth': '9', 'duration': 154.92},
'10': {'auth': '10', 'duration': 132.72},
'4': {'auth': '4', 'duration': 144.59}
}
and how can I get the "rank" value from the dict with a new duration number, lets say 133.92.
It should return dict index 1, since the top list is:
auth-10|duration:132.72
auth-4|duration:144.59
auth-9|duration:154.92
and 133.92 is bigger than 132.72, but less than 144.59 or 154.92
I'm sorry if Its unclear explained, but I tried my best.
EDIT:
I'm trying it again:
I need a function which returns the "predicted/rank" for a custom duration from the sorted list/sorted by "duration/DESC".
So a duration of 160 would return the last place, which is 4. (index+1). A 120 duration should return me the first position which is index 0 or 1st (index+1).

As #MartijnPieters mentioned, dictionaries lack indices, but they do have keys. The following function pred() (short for "predecessor") returns the key of the item whose duration is the largest duration <= the passed duration. It returns None (which can be tested for) if the passed duration is smaller than all of the durations in the dictionary:
from bisect import bisect_left
def pred(duration, entries):
entries = list(entries.items())
entries.sort(key = lambda x: x[1]['duration'])
durations = [x[1]['duration'] for x in entries]
j = bisect_left(durations,duration)
if j == 0:
return None
else:
return entries[j-1][0]
For example, if
d = {
'9': {'auth': '9', 'duration': 154.92},
'10': {'auth': '10', 'duration': 132.72},
'4': {'auth': '4', 'duration': 144.59}
}
(by the way -- don't use dict as an identifier since it has a predefined meaning in Python) then:
>>> pred(133.92,d)
'10'
>>> pred(149.92,d)
'4'
>>> pred(159.92,d)
'9'
>>> pred(129.92,d)
>>>
Note that
>>> pred(129.92,d) == None
True
On Edit: Here is another variation on the same idea, one that returns an integer rank:
def rank(duration, entries):
entries = list(entries.items())
durations = sorted(x[1]['duration'] for x in entries)
return bisect_left(durations,duration)
Then:
>>> rank(133.92,d)
1
>>> rank(129.92,d)
0
Final Edit: At the cost of some readability, here is a 1-liner:
def rank(duration, entries):
return bisect_left(sorted(v['duration'] for v in entries.values()),duration)

Related

Change a dataframe column value based on the current value?

I have a pandas dataframe with several columns and in one of them, there are string values. I need to change these strings to an acceptable value based on the current value. The dataframe is relatively large (40.000 x 32)
I've made a small function that takes the string to be changed as a parameter and then lookup what this should be changed to.
df = pd.DataFrame({
'A': ['Script','Scrpt','MyScript','Sunday','Monday','qwerty'],
'B': ['Song','Blues','Rock','Classic','Whatever','Something']})
def lut(txt):
my_lut = {'Script' : ['Script','Scrpt','MyScript'],
'Weekday' : ['Sunday','Monday','Tuesday']}
for key, value in my_lut.items():
if txt in value:
return(key)
break
return('Unknown')
The desired output should be:
A B
0 Script Song
1 Script Blues
2 Script Rock
3 Weekday Classic
4 Weekday Whatever
5 Unknown Something
I can't figure out how to apply this to the dataframe.
I've struggled over this for some time now so any input will be appreciated
Regards,
Check this out:
import pandas as pd
df = pd.DataFrame({
'A': ['Script','Scrpt','MyScript','Sunday','sdfsd','qwerty'],
'B': ['Song','Blues','Rock','Classic','Whatever','Something']})
dic = {'Weekday': ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday'], 'Script': ['Script','Scrpt','MyScript']}
for k, v in dic.items():
for item in v:
df.loc[df.A == item, 'A'] = k
df.loc[~df.A.isin(k for k, v in dic.items()), 'A'] = "Unknown"
Output:

Values in a list to a range()

I have a user input which comes in as follows:
j = ['1', '2', '3', '4', '5-6', '7-9']
I want to go through this list, identify any 'ranges' such as 7-8, or 5-99 etc.
With these remove the - and put the first and second values into range()
So far I have the following, I just can't figure out how to get the values into the right place, perhaps I need to select the number before the - and after the - ?
for item in j:
if "-" in item:
item = item.split("-")
# list(map(str, range(244, 247)))
for r in item:
print(r)
print('next')
# something like this?
list(map(str, range(int(item[0]), int(item[1]))))
EDIT
Taking into account jonrsharpe's comment:
for item in j:
if "-" in item:
start, stop = item.split("-")
print('start:' + start + ' end: ' + stop)
list(map(str, range(int(start), int(stop))))
This returns a type error TypeError: 'str' object is not callable
Assuming the output expected is j = [1, 2, 3, 4, 5, 6, 7, 8, 9]
You can create a blank list, process each element depending on whether or not it has a "-"
l = ['1', '2', '3', '4', '5-6', '7-9']
ll = []
for element in l:
if '-' in element:
sublist = element.strip().split('-')
sublist = list(range(int(sublist[0]), int(sublist[1]) + 1))
ll += sublist
else:
ll.append(int(element))
One approach is to create a list of items for both single numbers and ranges, and then use this as the argument to range:
j = ['1', '2', '3', '4', '5-6', '7-9']
for item in j:
if '-' in item:
rng = [int(x) for x in item.split('-')]
else:
# Start and stop are the same
rng = [int(item)] * 2
# Pad the stop number
rng[1] += 1
for i in range(*rng):
print(i)

Trouble with a python loop

I'm having issues with a loop that I want to:
a. see if a value in a DF row is greater than a value from a list
b. if it is, concatenate the variable name and the value from the list as a string
c. if it's not, pass until the loop conditions are met.
This is what I've tried.
import pandas as pd
import numpy as np
df = {'level': ['21', '22', '23', '24', '25', '26', '27', '28', '29', '30']
, 'variable':'age'}
df = pd.DataFrame.from_dict(df)
knots = [0, 25]
df.assign(key = np.nan)
for knot in knots:
if df['key'].items == np.nan:
if df['level'].astype('int') > knot:
df['key'] = df['variable']+"_"+knot.astype('str')
else:
pass
else:
pass
However, this only yields the key column to have NaN values. I'm not sure why it's not placing the concatenation.
You can do something like this inside the for loop. No need of any if conditions:
df.loc[df['level'].astype('int') > 25, 'key'] = df.loc[df['level'].astype('int') > 25, 'variable'] + '_' + df.loc[df['level'].astype('int') > 25, 'level']

Python 3.6 - How to search through a 2D list and return true or false if an item matches user input

I am using EDX to start learning python and I am stuck in a project that requires me to create a tic tac toe game.
I believe I have managed to complete most of the functions but when I tried to run the function that checks whether a position is available to be marked as X or O, I always get a false reading. It returns true only for the 7 and not for the rest of the items.
board = [['7', '8', '9'], ['4', '5', '6'], ['1', '2', '3']]
location = input(" Turn, select a number (1, 9): ")
def available(location, board):
for row in board:
for col in row:
if col==location:
return True
else:
return False
print(available(location,board))
I decided to separate the function from the rest of the code. The code above should be able to search the 2D list and if it finds the number that the user has entered to return true or false. When it does that another function is executed to change that number to X or O depending the player. I tried to run the function without the function and with print instead of return and works fine.
board = [['7', '8', '9'], ['4', '5', '6'], ['1', '2', '3']]
location = input(" Turn, select a number (1, 9): ")
for row in board:
for col in row:
if col==location:
print("True")
else:
print("False")
Any idea what I am doing wrong?
Let's look at your if else statement.
When the input number is not 7, we do not return true, instead we go to the else and immediately return false without checking the rest of the numbers.
The solution is to remove the else, and just return false only after iterating through every cell.
When you change the returns to prints this bug disappears because you are no longer returning, and therefore execution doesn't stop early.
def available(location, board):
for row in board:
for col in row:
if col==location:
return True
return False
The key insight here is that returning from a function, exits the function.
To overcome the problem you identified, you could for instance flatten your list by using a list comprehension and check for the existence of location in it:
board = [['7', '8', '9'], ['4', '5', '6'], ['1', '2', '3']]
location = input(" Turn, select a number (1, 9): ")
def available(location, board):
#collect all elements from all sublists in a list
allfields = [i for j in board for i in j]
#location element of this list?
if location in allfields:
return True
return False
print(available(location,board))
You can use any for a more Pythonic version.
def available(location, board):
return any(any(i == location for i in row) for row in board)

Using python need to get the substrings

Q)After executing the code Need to print the values [1, 12, 123, 2, 23, 3, 13], but iam getting [1, 12, 123, 2, 23, 3]. I have missing the letter 13. can any one tell me the reason to overcome that error?
def get_all_substrings(string):
length = len(string)
list = []
for i in range(length):
for j in range(i,length):
list.append(string[i:j+1])
return list
values = get_all_substrings('123')
results = list(map(int, values))
print(results)
count = 0
for i in results:
if i > 1 :
if (i % 2) != 0:
count += 1
print(count)
Pretty straight forward issue in your nested for loops within get_all_substrings(), lets walk it!
You are iterating over each element of your string 123:
for i in range(length) # we know length to be 3, so range is 0, 1, 2
You then iterate each subsequent element from the current i:
for j in range(i,length)
Finally you append a string from position i to j+1 using the slice operator:
list.append(string[i:j+1])
But what exactly is happening? Well we can step through further!
The first value of i is 0, so lets skip the first for, go to the second:
for j in range(0, 3): # i.e. the whole string!
# you would eventually execute all of the following
list.append(string[0:0 + 1]) # '1'
list.append(string[0:1 + 1]) # '12'
list.append(string[0:2 + 1]) # '123'
# but wait...were is '13'???? (this is your hint!)
The next value of i is 1:
for j in range(1, 3):
# you would eventually execute all of the following
list.append(string[1:1 + 1]) # '2'
list.append(string[1:2 + 1]) # '23'
# notice how we are only grabbing values of position i or more?
Finally you get to i is 2:
for j in range(2, 3): # i.e. the whole string!
# you would eventually execute all of the following
list.append(string[2:2 + 1]) # '3'
I've shown you what is happening (as you've asked in your question), I leave it to you to devise your own solution. A couple notes:
You need to look at all index combinations from position i
Dont name objects by their type (i.e. dont name a list object list)
I would try something like this using itertools and powerset() recipe
from itertools import chain, combinations
def powerset(iterable):
s = list(iterable)
return chain.from_iterable(combinations(s, r) for r in range(len(s) + 1))
output = list(map(''.join, powerset('123')))
output.pop(0)
Here is another option, using combinations
from itertools import combinations
def get_sub_ints(raw):
return [''.join(sub) for i in range(1, len(raw) + 1) for sub in combinations(raw, i)]
if __name__ == '__main__':
print(get_sub_ints('123'))
>>> ['1', '2', '3', '12', '13', '23', '123']

Resources