Trouble using max(list) - python

Im trying to use max() func to find max value in given a list.
Im creating a list for a given column from a txt file (representing a table, each line has a name and same amount of data columns).
for example - John,M,53,175,8000 (name,. gender, age, height, salary)
The problem is, I dont know if the column will contain numbers or strings. If the column contains integers, then it looks like this (for example):
['1','40','5','520','1025']
In that case, the max() func is comparing first digit and gives back a wrong value ('520').
Here is the relavant code - (Everything is within a class)
First func. is returning a list of a give column.
The second returns the name/names that has max value of the given column.
def get_column(self,colname):
if colname not in self.columns:
raise ValueError('Colname doesnt exists')
col_indx = self.columns.index(colname)+1
col_data = []
for i in range(len(self.names)):
col_data.append(self.data[i][col_indx])
return col_data
def get_row_name_with_max_value(self,colname):
if colname not in self.columns:
raise ValueError('Colname doesnt exists')
col_list = self.get_column(colname)
max_val = max(col_list)
counter = col_list.count(max_val)
max_name = []
k = -1
for i in range(counter):
index = col_list.index(max_val, k+1)
max_name.append(self.data[index][0])
k = index
return ', '.join(max_name)
thanks alot!

You can use the key argument of the max() function to specify comparison as ints:
In [1]: l = [1,'2',3,'100']
In [2]: max(l, key = int)
Out[2]: '100'
Probably you want to apply int() to the output as well.

Check if your list consist only of numeric strings before calling max, if this is a case, use key=int:
def kmax(col):
key = int if all(x.isdigit() for x in col) else str
return max(col, key=key)
print kmax(['1','40','5','520','1025']) # 1025
print kmax(['foo','bar','40','baz']) # foo

Related

Bad Function Python

I am new to Python and learning by myself.
This is my first post here, I appreciate any help that you can give me.
I have been trying to find the min value and the index on a list with a function.
This is the code that I wrote:
def findMin (L,startIndx):
m = L[startIndx]
index = startIndx
for i in range (startIndx,len(L)):
x = L[i]
if i < m:
i = index
m = x
else:
pass
return (m,index)
a,b = findMin([8,2,11,0,5])
print (a,b)
This is the error that I get:
**TypeError Traceback (most recent call last)
<ipython-input-33-9713029875a6> in <module>
----> 1 a,b = findMin([8,2,11,0,5])
2 print (a,b)
TypeError: findMin() missing 1 required positional argument: 'startIndx'**
I truly have no idea what is the problem with it, I appreciate any help,
The problem that you ran into is that you only passed one argument (L) and not the second one (startIndx) and you have some logic errors in your code as mentioned by other answers.
This is a much shorter way of writing your function using some built-in functions that isn't that error-prone.
Very compact form:
def findMin(L,startIndx=0):
m = min(L[startIndx:])
return (m, L.index(m))
Here is the more spaced out version of that function with some explenations.
#Notice that startIndx is a OPTIONAL ARGUMENT. This means if we don't pass anything to it, it will be defined to be 0
def findMin(L,startIndx=0):
#The searchspace is defined by SLICING the list using list[from:upto]
searchspace = L[startIndx:]
#Using the built-in min() function we find the smallest value in the list
m = min(searchspace)
#Then we use the built-in list.index(value) function to find the index of the smallest element
#Quick reminder: The first value in a list has the index 0
index = L.index(m)
#Finally we return the needed values
return (m, index)
This is how the function is called with startIndx:
test_list = [5,0,9,4,11]
test_index = 3
a,b = findMin(test_list, test_index)
print(a,b)
#--> 4, 3
When you call the function without the startIndx argument it searches through the entire list, because the optional argument startIndx is set to 0.
test_list = [5,0,9,4,11]
a,b = finMin(test_list)
print(a,b)
#--> 0, 2
So I can see a few errors in your algorithm.
First error:
findMin() missing 1 required positional argument: 'startIndx'
This error is due to the fact that findMin is a function that takes two arguments. First argument is the array you want to find the minimum of ([8,2,11,0,5] in your example). The second one is the startIndex. It is the index which you want to start at while searching for the min. You can add this index to your function call or add a default value in your funtion declaration.
Second error:
line 6: if i < m:
This is not what you want to do. Here you are comparing i (which is the current index of your for loop) and m which is the minimum value so far. You want to compare x and m like this:
if x < m
Third and last error:
i = index
This is wrong. You want to swap these two variables and assign i to index like this:
index = i
Here is the final correct code:
def findMin (L,startIndx):
m = L[startIndx]
index = startIndx
for i in range (startIndx+1,len(L)):
x = L[i]
if x < m:
index = i
m = x
else:
pass
return (m, index)
a,b = findMin([8,2,11,3,1], 0)
print (a,b)
Call with two parameters; findMin([8,2,11,0,5]) passes a single list. Perhaps findMin([8,2,11,0,5], 0).
Indentation appears to be incorrect, and this is critical in Python. All lines from "m =" to "return" must be indented one more level.
Variable naming could be improved; part of the confusion is likely from names like "m", "x", etc. What does m mean, vs. for example max_val?
Indexing seems confused; first you set index = startIndx then use for i. Delete the index = and use for index in range (startIndx + 1, len(L)) (you already have L[startIndx]).
What is the desired behavior and return value if startIndx is > len(L)?
Why comparing i to m?
Optimization: don't need the else: pass. It means "otherwise, do nothing" which does not need to be explicit.
findMin has two parameters, but you only pass it one argument. You have two choices:
Pass the argument that findMin requires.
a,b = findMin([8,2,11,0,5], 0)
Remove the startIndx parameter and use 0 instead:
def findMin (L):
index = 0
m = L[index]
for i in range (len(L)):
x = L[i]
if x < m:
index = i
m = x
else:
pass
return (m,index)
def findMin (L,startIndx): this function will expect 2 arguments L and startIndx. So you'll have to pass 2 arguments as well. But you are passing only 1 argument findMin([8,2,11,0,5]). [8,2,11,0,5] as a list is one argument passed to L, then missing one for startIndx which results in error.
startIndx seems unnecessary as well since all the elements needs to be traverse.
def findMin(L):
m = L[0]
index = 0
for i in range (1,len(L)):
if L[i]<m:
m=L[i]
index=i
return (m,index)
a,b = findMin([8,2,11,0,5])
print (a,b)

Is there a better way to traverse a multidimensional tuple / list?

I got data samples like this:
[1.212,3.54,[4.123],[5.5343],[[2,3.2],[9.345,4.102]]]
((1.41231,3.312),4.212,6.312)
["hello",1.232,"3.555"]
my final purpose is serialize those datas, but some data in those lists isn't python types,like python float--sympy.Core.Float , so I must read those multidimensional arrays,find out those number which is Sympy.core.Float type, then doing a type converts like this: "float(number)",so is there a easy to finish this ?
Here is part codes:
def RecursData(datas,final_list):
for index ,value in enumerate(datas):
if(isinstance(value,(tuple,list))):
tmp_data_list = list(value)
RecursData(tmp_data_list,final_list)
elif isinstance(value,(float,Float)):
final_list.append(float(value))
else:
final_list.append(value)
In your case i would do this:
your_data = [1.212,3.54,[4.123],[5.5343],[[2,3.2],[9.345,4.102]]]
def itter(lst):
end_goal = []
for x in lst:
if hasattr(x, '__iter__'):
end_goal.append(itter(x))
else:
# here do whatever you need to do
# so if you have to reconvert data
if isinstance(x,Float):
x = float(x)
end_goal.append(x)
return end_goal
# then just run the function/
print(itter(your_data))
this will replace all Float values to python floats

Can we return one dataframe and one variable from a function in python? [duplicate]

I would like to return two values from a function in two separate variables.
For example:
def select_choice():
loop = 1
row = 0
while loop == 1:
print('''Choose from the following options?:
1. Row 1
2. Row 2
3. Row 3''')
row = int(input("Which row would you like to move the card from?: "))
if row == 1:
i = 2
card = list_a[-1]
elif row == 2:
i = 1
card = list_b[-1]
elif row == 3:
i = 0
card = list_c[-1]
return i
return card
And I want to be able to use these values separately. When I tried to use return i, card, it returns a tuple and this is not what I want.
You cannot return two values, but you can return a tuple or a list and unpack it after the call:
def select_choice():
...
return i, card # or [i, card]
my_i, my_card = select_choice()
On line return i, card i, card means creating a tuple. You can also use parenthesis like return (i, card), but tuples are created by comma, so parens are not mandatory. But you can use parens to make your code more readable or to split the tuple over multiple lines. The same applies to line my_i, my_card = select_choice().
If you want to return more than two values, consider using a named tuple. It will allow the caller of the function to access fields of the returned value by name, which is more readable. You can still access items of the tuple by index. For example in Schema.loads method Marshmallow framework returns a UnmarshalResult which is a namedtuple. So you can do:
data, errors = MySchema.loads(request.json())
if errors:
...
or
result = MySchema.loads(request.json())
if result.errors:
...
else:
# use `result.data`
In other cases you may return a dict from your function:
def select_choice():
...
return {'i': i, 'card': card, 'other_field': other_field, ...}
But you might want consider to return an instance of a utility class (or a Pydantic/dataclass model instance), which wraps your data:
class ChoiceData():
def __init__(self, i, card, other_field, ...):
# you can put here some validation logic
self.i = i
self.card = card
self.other_field = other_field
...
def select_choice():
...
return ChoiceData(i, card, other_field, ...)
choice_data = select_choice()
print(choice_data.i, choice_data.card)
I would like to return two values from a function in two separate variables.
What would you expect it to look like on the calling end? You can't write a = select_choice(); b = select_choice() because that would call the function twice.
Values aren't returned "in variables"; that's not how Python works. A function returns values (objects). A variable is just a name for a value in a given context. When you call a function and assign the return value somewhere, what you're doing is giving the received value a name in the calling context. The function doesn't put the value "into a variable" for you, the assignment does (never mind that the variable isn't "storage" for the value, but again, just a name).
When i tried to to use return i, card, it returns a tuple and this is not what i want.
Actually, it's exactly what you want. All you have to do is take the tuple apart again.
And i want to be able to use these values separately.
So just grab the values out of the tuple.
The easiest way to do this is by unpacking:
a, b = select_choice()
I think you what you want is a tuple. If you use return (i, card), you can get these two results by:
i, card = select_choice()
def test():
....
return r1, r2, r3, ....
>> ret_val = test()
>> print ret_val
(r1, r2, r3, ....)
now you can do everything you like with your tuple.
def test():
r1 = 1
r2 = 2
r3 = 3
return r1, r2, r3
x,y,z = test()
print x
print y
print z
> test.py
1
2
3
And this is an alternative.If you are returning as list then it is simple to get the values.
def select_choice():
...
return [i, card]
values = select_choice()
print values[0]
print values[1]
you can try this
class select_choice():
return x, y
a, b = test()
You can return more than one value using list also. Check the code below
def newFn(): #your function
result = [] #defining blank list which is to be return
r1 = 'return1' #first value
r2 = 'return2' #second value
result.append(r1) #adding first value in list
result.append(r2) #adding second value in list
return result #returning your list
ret_val1 = newFn()[1] #you can get any desired result from it
print ret_val1 #print/manipulate your your result

How to find two items of a list with the same return value of a function on their attribute?

Given a basic class Item:
class Item(object):
def __init__(self, val):
self.val = val
a list of objects of this class (the number of items can be much larger):
items = [ Item(0), Item(11), Item(25), Item(16), Item(31) ]
and a function compute that process and return a value.
How to find two items of this list for which the function compute return the same value when using the attribute val? If nothing is found, an exception should be raised. If there are more than two items that match, simple return any two of them.
For example, let's define compute:
def compute( x ):
return x % 10
The excepted pair would be: (Item(11), Item(31)).
You can check the length of the set of resulting values:
class Item(object):
def __init__(self, val):
self.val = val
def __repr__(self):
return f'Item({self.val})'
def compute(x):
return x%10
items = [ Item(0), Item(11), Item(25), Item(16), Item(31)]
c = list(map(lambda x:compute(x.val), items))
if len(set(c)) == len(c): #no two or more equal values exist in the list
raise Exception("All elements have unique computational results")
To find values with similar computational results, a dictionary can be used:
from collections import Counter
new_d = {i:compute(i.val) for i in items}
d = Counter(new_d.values())
multiple = [a for a, b in new_d.items() if d[b] > 1]
Output:
[Item(11), Item(31)]
A slightly more efficient way to find if multiple objects of the same computational value exist is to use any, requiring a single pass over the Counter object, whereas using a set with len requires several iterations:
if all(b == 1 for b in d.values()):
raise Exception("All elements have unique computational results")
Assuming the values returned by compute are hashable (e.g., float values), you can use a dict to store results.
And you don't need to do anything fancy, like a multidict storing all items that produce a result. As soon as you see a duplicate, you're done. Besides being simpler, this also means we short-circuit the search as soon as we find a match, without even calling compute on the rest of the elements.
def find_pair(items, compute):
results = {}
for item in items:
result = compute(item.val)
if result in results:
return results[result], item
results[result] = item
raise ValueError('No pair of items')
A dictionary val_to_it that contains Items keyed by computed val can be used:
val_to_it = {}
for it in items:
computed_val = compute(it.val)
# Check if an Item in val_to_it has the same computed val
dict_it = val_to_it.get(computed_val)
if dict_it is None:
# If not, add it to val_to_it so it can be referred to
val_to_it[computed_val] = it
else:
# We found the two elements!
res = [dict_it, it]
break
else:
raise Exception( "Can't find two items" )
The for block can be rewrite to handle n number of elements:
for it in items:
computed_val = compute(it.val)
dict_lit = val_to_it.get(computed_val)
if dict_lit is None:
val_to_it[computed_val] = [it]
else:
dict_lit.append(it)
# Check if we have the expected number of elements
if len(dict_lit) == n:
# Found n elements!
res = dict_lit
break

How to count the number of letters in a string with a list of sample?

value = 'bcdjbcdscv'
value = 'bcdvfdvdfvvdfvv'
value = 'bcvfdvdfvcdjbcdscv'
def count_letters(word, char):
count = 0
for c in word:
if char == c:
count += 1
return count
How to count the number of letters in a string with a list of sample? I get nothing in my python shell when I wrote the above code in my python file.
There is a built-in method for this:
value.count('c')
functions need to be called, and the return values need to be printed to the stdout:
In [984]: value = 'bcvfdvdfvcdjbcdscv'
In [985]: count_letters(value, 'b')
Out[985]: 2
In [987]: ds=count_letters(value, 'd') #if you assign the return value to some variable, print it out:
In [988]: print ds
4
EDIT:
On calculating the length of the string, use python builtin function len:
In [1024]: s='abcdefghij'
In [1025]: len(s)
Out[1025]: 10
You'd better google it with some keywords like "python get length of a string" before you ask on SO, it's much time saving :)
EDIT2:
How to calculate the length of several strings with one function call?
use var-positional parameter *args, which accepts an arbitrary sequence of positional arguments:
In [1048]: def get_lengths(*args):
...: return [len(i) for i in args]
In [1049]: get_lengths('abcd', 'efg', '1234567')
Out[1049]: [4, 3, 7]
First you should probably look at correct indenting and only send in value. Also value is being overwritten so the last one will be the actual reference.
Second you need to call the function that you have defined.
#value = 'bcdjbcdscv'
#value = 'bcdvfdvdfvvdfvv'
value = 'bcvfdvdfvcdjbcdscv'
def count_letters(word, char):
count = 0
for c in word:
if char == c:
count += 1
return count
x = count_letters(value, 'b')
print x
# 2
This should produce the result you are looking for. You could also just call:
print value.count('b')
# 2
In python, there is a built-in method to do this. Simply type:
value = 'bcdjbcdscv'
value.count('c')

Categories