Search min value within a list of tuples - python

I have a list which contain a tuple, within each tuple there's a list and a interger value
E.g.
Mylist = [([1,1,3], 3),([1,1,3], 30),([2,2,3], 15),([1,3,3], 2)]
I want this list to return this tuple ([1,3,3], 2)
since Mylist[i][1] = 2 that is the min in the list.
Now, the built-in function min() doesn't really do that.. it compares it on the basis of the actual list that is Mylist[i][0]
I can perform this only if the list contains two items:
But i have not figured how to do it in a list of.. say 10 items!
def min(a,x,b,y):
t = a
if x >= y:
t = b
return t

Mylist = [([1,1,3], 3),([1,1,3], 30),([2,2,3], 15),([1,3,3], 2)]
print min(Mylist,key=lambda x:x[1])
You can provide a key to min function using lambda.
Output:([1, 3, 3], 2)

If you store your list with the value first then you can just use min and sorted directly:
Mylist = [(3, [1,1,3]), (30, [1,1,3]), (15, [2,2,3]),(2, [1,3,3])]
min(Mylist)
Output: (2, [1, 3, 3])

my solution
myList = [([1, 1, 3], 3), ([1, 1, 3], 30), ([2, 2, 3], 15), ([1, 3, 3], 2)]
minValue = [i for i in myList if i[1] == min([x[1] for x in myList])]
return a list of items with the min value
[([1, 3, 3], 2)]
for example if you have a list like
myList = [([1, 1, 3], 3), ([1, 1, 3], 30), ([2, 2, 3], 15), ([1, 3, 3], 2), ([1, 1, 3], 2)]
Result will be
[([1, 3, 3], 2),([1, 1, 3], 2)]
I don't know if you need this but works :D

Just for interest's sake, here's a functional approach:
def get_min_tuple(l):
def get_index(lst, num, index=0):
if num in lst[index]:
return index
else:
return get_index(lst, num, index + 1)
def find_min(l, smallest=None, assigned=False):
if l == []:
return smallest
else:
if not assigned:
smallest = l[0][1]
assigned = True
else:
if l[0][1] < smallest:
smallest = l[0][1]
return find_min(l[1:], smallest, assigned)
return l[get_index(l, find_min(l))]
While the one-liner of supplying a key to the min function is of course more useful in a practical sense, I thought I'd share this for educational purposes.

Time Complexity = n
Mylist = [([1,1,3], 3),([1,1,3], 30),([2,2,3], 15),([1,3,3], 2)]
minv=MyList[0][1]
minv1=MyList[0][0]
for lst in MyList:
if(lst[1]<minv):
minv=lst[1]
minv1=lst[0]
print(tuple(minv1,minv))

Related

How to replace numbers with order in (python) list

I have a list containing integers and want to replace them so that the element which previously contained the highest number now contains a 1, the second highest number set to 2, etc etc.
Example:
[5, 6, 34, 1, 9, 3] should yield [4, 3, 1, 6, 2, 5].
I personally only care about the first 9 highest numbers by I thought there might be a simple algorithm or possibly even a python function to do take care of this task?
Edit: I don't care how duplicates are handled.
A fast way to do this is to first generate a list of tuples of the element and its position:
sort_data = [(x,i) for i,x in enumerate(data)]
next we sort these elements in reverse:
sort_data = sorted(sort_data,reverse=True)
which generates (for your sample input):
>>> sort_data
[(34, 2), (9, 4), (6, 1), (5, 0), (3, 5), (1, 3)]
and nest we need to fill in these elements like:
result = [0]*len(data)
for i,(_,idx) in enumerate(sort_data,1):
result[idx] = i
Or putting it together:
def obtain_rank(data):
sort_data = [(x,i) for i,x in enumerate(data)]
sort_data = sorted(sort_data,reverse=True)
result = [0]*len(data)
for i,(_,idx) in enumerate(sort_data,1):
result[idx] = i
return result
this approach works in O(n log n) with n the number of elements in data.
A more compact algorithm (in the sense that no tuples are constructed for the sorting) is:
def obtain_rank(data):
sort_data = sorted(range(len(data)),key=lambda i:data[i],reverse=True)
result = [0]*len(data)
for i,idx in enumerate(sort_data,1):
result[idx] = i
return result
Another option, you can use rankdata function from scipy, and it provides options to handle duplicates:
from scipy.stats import rankdata
lst = [5, 6, 34, 1, 9, 3]
rankdata(list(map(lambda x: -x, lst)), method='ordinal')
# array([4, 3, 1, 6, 2, 5])
Assuimg you do not have any duplicates, the following list comprehension will do:
lst = [5, 6, 34, 1, 9, 3]
tmp_sorted = sorted(lst, reverse=True) # kudos to #Wondercricket
res = [tmp_sorted.index(x) + 1 for x in lst] # [4, 3, 1, 6, 2, 5]
To understand how it works, you can break it up into pieces like so:
lst = [5, 6, 34, 1, 9, 3]
# let's see what the sorted returns
print(sorted(lst, reverse=True)) # [34, 9, 6, 5, 3, 1]
# biggest to smallest. that is handy.
# Since it returns a list, i can index it. Let's try with 6
print(sorted(lst, reverse=True).index(6)) # 2
# oh, python is 0-index, let's add 1
print(sorted(lst, reverse=True).index(6) + 1) # 3
# that's more like it. now the same for all elements of original list
for x in lst:
print(sorted(lst, reverse=True).index(x) + 1) # 4, 3, 1, 6, 2, 5
# too verbose and not a list yet..
res = [sorted(lst, reverse=True).index(x) + 1 for x in lst]
# but now we are sorting in every iteration... let's store the sorted one instead
tmp_sorted = sorted(lst, reverse=True)
res = [tmp_sorted.index(x) + 1 for x in lst]
Using numpy.argsort:
numpy.argsort returns the indices that would sort an array.
>>> xs = [5, 6, 34, 1, 9, 3]
>>> import numpy as np
>>> np.argsort(np.argsort(-np.array(xs))) + 1
array([4, 3, 1, 6, 2, 5])
A short, log-linear solution using pure Python, and no look-up tables.
The idea: store the positions in a list of pairs, then sort the list to reorder the positions.
enum1 = lambda seq: enumerate(seq, start=1) # We want 1-based positions
def replaceWithRank(xs):
# pos = position in the original list, rank = position in the top-down sorted list.
vp = sorted([(value, pos) for (pos, value) in enum1(xs)], reverse=True)
pr = sorted([(pos, rank) for (rank, (_, pos)) in enum1(vp)])
return [rank for (_, rank) in pr]
assert replaceWithRank([5, 6, 34, 1, 9, 3]) == [4, 3, 1, 6, 2, 5]

How to replace a string in a list with a numerical value?

let's say I have a list:
a = [['a','b'],['a','c'],['d','e'],['c','a']]
I need it to be
a = [[1,2],[1,3],[4,5],[3,1]]
I've tried to change the value using a counter but it does not work
This will work if you you have a list of lists and no other nesting. It will not only work for strings of any length, but for all hashable types. It also has the benefit of always using contiguous indexes starting at 0 (or 1) no matter which strings are used:
> from collections import defaultdict
> d = defaultdict(lambda: len(d)) # use 'len(d) + 1' if you want 1-based counting
> a = [[d[s] for s in x] for x in a]
> a
[[0, 1], [0, 2], [3, 4], [2, 0]]
# [[1, 2], [1, 3], [4, 5], [3, 1]]
It uses a defaultdict that always returns its current size for unknown items, thus always producing a unique integer.
You could also use Gensims Dictionary using the following:
from gensim.corpora import Dictionary
a = [['a','b'],['a','c'],['d','e'],['c','a']]
# create Dictionary object
dic = Dictionary(a)
# map letters to tokens
def mapper(l):
# take in list and return numeric representation using Dic above
return map(lambda x: dic.token2id[x], l)
a2 = map(lambda x: mapper(x), a) # run mapper on each sublist to get letter id's
a2 # [[0, 1], [0, 2], [4, 3], [2, 0]]
And if you wanted to convert to id's with counts (bag of words) you can use:
map(lambda x: dic.doc2bow(x), a)
# [[(0, 1), (1, 1)], [(0, 1), (2, 1)], [(3, 1), (4, 1)], [(0, 1), (2, 1)]]
If you're looking to expand this to multi-character strings, Python provides a builtin hash function that has a low chance of collisions:
a = [['a','b'],['a','c'],['d','e'],['c','a']]
b = [[hash(f), hash(s)] for f,s in a]
b
Out[11]:
[[-351532772472455791, 5901277274144560781],
[-351532772472455791, 791873246212810409],
[3322017449728496367, 3233520255652808905],
[791873246212810409, -351532772472455791]]
If the strings are single characters, I'd define a function that translates them, then do the same list comprehension:
def to_int(char):
return ord(char) - ord('a') + 1
b = [[to_int(f), to_int(s)] for f,s in a]
b
Out[14]: [[1, 2], [1, 3], [4, 5], [3, 1]]

How to find common elements inside a list

I have a list l1 that looks like [1,2,1,0,1,1,0,3..]. I want to find, for each element the indexes of elements which have same value as the element.
For eg, for the first value in the list, 1, it should list out all indexes where 1 is present in the list and it should repeat same for every element in the list. I can wrote a function to do that iterating through the list but wanted to check if there is any predefined function.
I am getting the list from Pandas dataframe columns, it would be good know if series/dataframe library offer any such functions
You can use numpy.unique, which can return the inverse too. This can be used to reconstruct the indices using numpy.where:
In [49]: a = [1,2,1,0,1,1,0,3,8,10,6,7]
In [50]: uniq, inv = numpy.unique(a, return_inverse=True)
In [51]: r = [(uniq[i], numpy.where(inv == i)[0]) for i in range(uniq.size)]
In [52]: print(r)
[(0, array([3, 6])), (1, array([0, 2, 4, 5])), (2, array([1])), (3, array([7])), (6, array([10])), (7, array([11])), (8, array([8])), (10, array([9]))]
i tried brute force..may be u can optimize
here is python3 code
L = [1,2,1,0,1,1,0,3]
D = dict()
for i in range(len(L)):
n =[]
if L[i] not in D.keys():
for j in range(len(L)):
if L[i] == L[j]:
n.append(j)
D[L[i]] = n
for j in D.keys():
print(j,"->",D.get(j))
You could achieve this using a defaultdict.
from collection import defaultdict
input = [1,2,1,0,1,1,0,3]
#Dictionary to store our indices for each value
index_dict = defaultdict(list)
#Store index for each item
for i, item in enumerate(input):
index_dict[item].append(i)
If you want a list which contains the indices of elements which are the same as the corresponding element in your input list, you can just create a reference to the dictionary:
same_element_indices = [index_dict[x] for x in input]
This has the advantage of only referencing the one object for each identical element.
Output would be:
[[0, 2, 4, 5],
[1],
[0, 2, 4, 5],
[3, 6],
[0, 2, 4, 5],
[0, 2, 4, 5],
[3, 6],
[7]]
You can also try something like this:
import pandas as pd
df = pd.DataFrame({'A': [1,2,1,0,1,1,0,3]})
uni = df['A'].unique()
for i in uni:
lists = df[df['A'] == i].index.tolist()
print(i, '-->', lists)
Output:
1 --> [0, 2, 4, 5]
2 --> [1]
0 --> [3, 6]
3 --> [7]

I want to check whether my rows and columns are same

I want to check whether row 1 is equal to column one and row two is equal to column 2 and so on. If a matrix is equal to its transpose.
I tried to solve the problem using the following code, but the function is returning none. Can some one help me with this
x = [[1, 2, 3],
[2, 3, 4],
[3, 4, 1]]
def rows(matrix):
list = [val for val in matrix]
list1 = [i for i in zip(*matrix)]
if list == list1:
return True
else:
return False
rows(x)
zip returns tuples, not lists:
>>> [val for val in x]
[[1, 2, 3], [2, 3, 4], [3, 4, 1]]
>>> [i for i in zip(*x)]
[(1, 2, 3), (2, 3, 4), (3, 4, 1)]
And they don't compare equal to each other:
>>> [1,2,3] == (1,2,3)
False
Instead, you can simply return the results of the comparison after converting to lists:
>>> x == [list(i) for i in zip(*x)]
True
Use map to map the sublists to tuples and compare, mapping to tuple shoud also be more efficient than changing tuples to lists:
def rows(matrix):
return zip(*matrix) == map(tuple, matrix)
The zip function returns a list of tuples:
>>> x = [[1, 2, 3],
[2, 3, 4],
[3, 4, 1]]
>>> zip(*x)
[(1, 2, 3), (2, 3, 4), (3, 4, 1)]
>>> x == zip(*x)
False
A list isn't equal to a tuple, even if it has the same elements. A list of lists isn't equal to a list of tuples, even if the inner lists/tuples contain the same elements. You can do what you want easily, and you were close!
>>> x == [list(i) for i in zip(*x)]
True
You can understand what is wrong with your program by running this:
x = [[1, 2, 3],[2, 3, 4],[3, 4, 1]]
def rows(matrix):
list = [val for val in matrix]
list1 = [i for i in zip(*matrix)]
print list
print list1
if list == list1:
return True
else:
return False
print rows(x)
Here print list prints list of lists, while print list1 prints list of touples.
Since lists and touples are different, the program returns False.

Get index from a list where the key changes, groupby

I have a list that looks like this:
myList = [1, 1, 1, 1, 2, 2, 2, 3, 3, 3]
What I want to do is record the index where the items in the list changes value. So for my list above it would be 3, 6.
I know that using groupby like this:
[len(list(group)) for key, group in groupby(myList)]
will result in:
[4, 3, 3]
but what I want is the index where a group starts/ends rather than just then number of items in the groups. I know I could start summing each sucessive group count-1 to get the index but thought there may be a cleaner way of doing so.
Thoughts appreciated.
Just use enumerate to generate indexes along with the list.
from operator import itemgetter
from itertools import groupby
myList = [1, 1, 1, 1, 2, 2, 2, 3, 3, 3]
[next(group) for key, group in groupby(enumerate(myList), key=itemgetter(1))]
# [(0, 1), (4, 2), (7, 3)]
This gives pairs of (start_index, value) for each group.
If you really just want [3, 6], you can use
[tuple(group)[-1][0] for key, group in
groupby(enumerate(myList), key=itemgetter(1))][:-1]
or
indexes = (next(group)[0] - 1 for key, group in
groupby(enumerate(myList), key=itemgetter(1)))
next(indexes)
indexes = list(indexes)
[i for i in range(len(myList)-1) if myList[i] != myList[i+1]]
In Python 2, replace range with xrange.
>>> x0 = myList[0]
... for i, x in enumerate(myList):
... if x != x0:
... print i - 1
... x0 = x
3
6

Categories