take the max of a list of string numbers - python

I have a list like this lst = ['1.8.6.8', '1.8.8.879', '1.8.8.880', '1.8.10.883', '1.8.10.884']. I would like to take the max such that it evaluates each block separated by . independently, and then return 1.8.10.884. max(lst) returns 1.8.8.880. Is there a neat way to do this without splitting it by . and then looping through each subset?

max(lst) returns 1.8.8.880 because strings are compared in lexicographical order.
To compare them as integers, you'll have to split by . and convert the elements to integers. However, you can use tuple / list comparisons to do the sorting instead of looping through each subset. For example:
[1, 2, 3] > [1, 1, 3] # True
[1, 2, 3] > [1, 2, 4] # False
However, this doesn't account for the length of the lists: the list that contains the smaller element first is picked as the smaller list.
[1, 2, 3] > [1, 1, 100, 1000] # Gives True
To get around this, we'll return a tuple containing the length of the list, and the list itself. Also, we'll use the key argument to the max function:
def convert_function(elem):
vals = [int(a) for a in elem.split('.')]
return (len(vals), vals)
lst = ['1.8.68', '1.8.8.879', '1.8.8.880', '1.8.10.883', '1.8.10.884']
max(lst, key=convert_function) # gives '1.8.10.884'

Start by splitting each string on . and converting the fields to integers.
>>> t1 = [[int(x) for x in y.split('.')] for y in lst]
>>> t1
[[1, 8, 68], [1, 8, 8, 879], [1, 8, 8, 880], [1, 8, 10, 883], [1, 8, 10, 884]]
Then zip the tuples and map max over the result.
>>> from itertools import zip_longest
>>> t2 = [max(x) for x in zip_longest(*t1, fillvalue=0)]
>>> t2
[1, 8, 68, 884]
Finally, join the elements of t2 into a single string.
>>> '.'.join(map(str, t2))
'1.8.68.884'
I leave it as an exercise to combine these steps into a single expression.

Related

How does this work in python : flat_array = sum( array_2d , [ ] )

To Convert a 2D array into 1D array in python , i found this method on leetcode. But can't find
How its working logically step by step ? Please explain.
Also someone said on leetcode that :
"This could be quadratic runtime complexity when array size gets really large"
Is this true? if yes, How?
a = [[4,2,5],[1,8,2],[7,5,6]]
flat = sum(a,[])
flat
output : [4, 2, 5, 1, 8, 2, 7, 5, 6]
How sum works?
In Python, lists can be concatenated with operator +:
>>> [1] + [2, 3]
[1, 2, 3]
The sum function in Python is similar to:
>>> def my_sum(iterable, start=0):
... for v in iterable:
... start = start + v
... return start
...
So for sum(a, []), you can understand the following operations:
>>> [] + [4, 2, 5] + [1, 8, 2] + [7, 5, 6]
[4, 2, 5, 1, 8, 2, 7, 5, 6]
But this will not be a good practice, because each concatenation will produce a new list, rather than concatenation in place on one of the lists:
>>> a = [1]
>>> b = [2, 3]
>>> c = a + b
>>> a, b, c
([1], [2, 3], [1, 2, 3])
This means that each concatenation requires O(n + m) time (n and m are the lengths of the two lists respectively), rather than O(m). For m lists with length n, the first time a list of length n will be concatenated with another list of length n, and the next time a list of length 2n will be concatenated with a list of length n... at the end, the total time spent will be:
(n + n) + (2n + n) + ... + (mn + n) = (m^2 + 3m) * n / 2 = O(m^2 * n)
Better practice
The simple method is to use in place concatenate each time:
def concatenate(lists):
result = []
for lst in lists:
result += lst
return result
A more concise way is to use functools.reduce and operator.iconcat:
>>> from functools import reduce
>>> from operator import iconcat
>>> reduce(iconcat, a, [])
[4, 2, 5, 1, 8, 2, 7, 5, 6]
You can also use itertools.chain.from_iterable to chain these lists, and then use the list to construct:
>>> from itertools import chain
>>> list(chain.from_iterable(a))
[4, 2, 5, 1, 8, 2, 7, 5, 6]
Or use nested list comprehension:
>>> [val for lst in a for val in lst]
[4, 2, 5, 1, 8, 2, 7, 5, 6]
For performance comparison, please refer to: How do I make a flat list out of a list of lists?
Here sum() works as follows:
By default, sum() takes one required parameter which is a sequence of integers, as for the second optional parameter, it is defaulted to 0 to indicate that the sequence is of type int.
However, in the above case, it passes list of lists to sum() with the optional start parameter set to [] to indicate the sequence type is list which concatenates them.
However, there are better solutions to this problem. One of which is to use a for loop.
For further information, please check the documentation.

How do I find the sorted position/index of a list in python?

For example, I have a list:
[12, 1205, 102, 6]
I want to get
[1, 3, 2, 0]
Because this is the position they are supposed to be in if the list is sorted.
How can I achieve this in python?
Use a double numpy.argsort, or self-indexing:
l = [12, 1205, 102, 6]
out = np.argsort(np.argsort(l)).to_list()
# or
x = np.argsort(l)
out = x[x]
Output: [1, 3, 2, 0]
older (inefficient) answer
IIUC, you want the sorted rank:
sorted_list = sorted(your_list)
[sorted_list.index(x) for x in your_list]
You can get a list of indexes in the order of sorted values using the sorted() function, then use this to set the positions in the resulting list:
L = [12, 1205, 102, 6]
P = sorted(range(len(L)),key=L.__getitem__) # positions in sorted order
S = [None]*len(L) # resulting list
for p,i in enumerate(P): S[i]=p # assign position at original indexes
print(S) # [1, 3, 2, 0]
The equivalent solution using numpy could look like this:
S = np.zeros(len(L),dtype=np.int) # prepare resulting array
S[np.argsort(L)] = np.arange(len(L)) # assign positions at indexes
print(S) # array([1, 3, 2, 0])
Sorting your list can be done with numpy:
numpy.argsort([2, 3, 5, 1, 4])

Find positions of elements in sorted array

Suppose I have some numpy array (all elements are unique) that I want to sort in descending order. I need to find out which positions elements of initial array will take in sorted array.
Example.
In1: [1, 2, 3] # Input
Out1: [2, 1, 0] # Expected output
In2: [1, -2, 2] # Input
Out2: [1, 2, 0] # Expected output
I tried this one:
def find_positions(A):
A = np.array(A)
A_sorted = np.sort(A)[::-1]
return np.argwhere(A[:, None] == A_sorted[None, :])[:, 1]
But it doesn't work when the input array is very large (len > 100000). What I did wrong and how can I resolve it?
Approach #1
We could use double argsort -
np.argsort(a)[::-1].argsort() # a is input array/list
Approach #2
We could use one argsort and then array-assignment -
# https://stackoverflow.com/a/41242285/ #Andras Deak
def argsort_unique(idx):
n = idx.size
sidx = np.empty(n,dtype=int)
sidx[idx] = np.arange(n)
return sidx
out = argsort_unique(np.argsort(a)[::-1])
Take a look at numpy.argsort(...) function:
Returns the indices that would sort an array.
Perform an indirect sort along the given axis using the algorithm specified by the kind keyword. It returns an array of indices of the same shape as a that index data along the given axis in sorted order.
Here is the reference from the documentation, and the following is a simple example:
import numpy
arr = numpy.random.rand(100000)
indexes = numpy.argsort(arr)
the indexes array will contain all the indexes in the order in which the array arr would be sorted
I face the same problem for plain lists, and would like to avoid using numpy. So I propose a possible solution that should also work for an np.array, and which avoids reversal of the result:
def argsort(A, key=None, reverse=False):
"Indirect sort of list or array A: return indices of elements in order."
keyfunc = (lambda i: A[i]) if key is None else lambda i: key(A[i])
return sorted(range(len(A)), keyfunc, reverse=reverse)
Example of use:
>>> L = [3,1,4,1,5,9,2,6]
>>> argsort( L )
[1, 3, 6, 0, 2, 4, 7, 5]
>>> [L[i]for i in _]
[1, 1, 2, 3, 4, 5, 6, 9]
>>> argsort( L, key=lambda x:(x%2,x) ) # even elements first
[6, 2, 7, 1, 3, 0, 4, 5]
>>> [L[i]for i in _]
[2, 4, 6, 1, 1, 3, 5, 9]
>>> argsort( L, key=lambda x:(x%2,x), reverse = True)
[5, 4, 0, 1, 3, 7, 2, 6]
>>> [L[i]for i in _]
[9, 5, 3, 1, 1, 6, 4, 2]
Feedback would be welcome! (Efficiency compared to previously proposed solutions? Suggestions for improvements?)

Replace every element by sum of all other elements

I have to replace every element in the list by sum of all other elements in the list.
[1, 2, 3] => [5, 4, 3]
[1] => [0]
[2, 7, 9] => [16, 11, 9]
I have done so far:
for i in range(len(numbers)):
numbers[i] = sum[numbers[:i]] + sum[numbers[i+1:]]
return numbers
But I'm keep getting TypeError
Elegant way to achieve this will be:
>>> my_list = [1, 2, 3]
>>> [sum(my_list)-x for x in my_list]
[5, 4, 3]
OR, even better to calculate sum outside the list comprehension so that you won't have to calculate it each time (as pointed by #Jean):
>>> my_sum = sum(my_list)
>>> [my_sum-x for x in my_list]
[5, 4, 3]
Issue with your code: You are not making call () to sum, instead trying to access it's index using [..] resulting in TypeError. Also, you are modifying the original list while iterating which will result in different result (which is unexpected result for you). You should be placing these values in separate list.

Why does my code fail on this SIMPLE exercise?

I am doing a coding exercise:
Given a sequence of integers as an array, determine whether it is possible to obtain a strictly increasing sequence by removing no more than one element from the array.
Example
For sequence = [1, 3, 2, 1], the output should be
false;
There is no one element in this array that can be removed in order to get a strictly increasing sequence.
For sequence = [1, 3, 2], the output should be
true;
You can remove 3 from the array to get the strictly increasing sequence [1, 2]. Alternately, you can remove 2 to get the strictly increasing sequence [1, 3].
.
So I wrote this code:
def almostIncreasingSequence(sequence):
first_list, second_list = [x for x in sequence], [x for x in sequence]
for i in range(len(sequence)-1):
if sequence[i] >= sequence[i+1]:
first_list.remove(sequence[i])
second_list.remove(sequence[i+1])
break
if first_list == sorted(set(first_list)) or second_list == sorted(set(second_list)):
return True
else:
return False
The code passes 13/15 tests.
Below are the 2 inputs that my code fails on:
[1, 2, 3, 4, 3, 6]
[3, 5, 67, 98, 3]
Both inputs should return True but my code returns False. Any thoughts?
Read about list.remove:
Remove the first item from the list whose value is x. It is an error if there is no such item.
>>> l = [1, 2, 3, 4, 3, 6]
>>> l.remove(3)
>>> print(l)
[1, 2, 4, 3, 6]
Instead of remove(value), use pop(index):
first_list.pop(i)
second_list.pop(i+1)
Take your first list, [1, 2, 3, 4, 3, 6]. Once you run the for loop over first_list and second_list, the values of those variables are:
In [9]: first_list
Out[9]: [1, 2, 3, 3, 6]
In [10]: second_list
Out[10]: [1, 2, 4, 3, 6]
The first part of the if is False because:
In [12]: sorted(set(first_list))
Out[12]: [1, 2, 3, 6]
By putting the list into a set, you've lost one of the 3's.
The second part of the if is False because second_list is not sorted.
So, both parts of the if statement are False, and your function returns False.

Categories