I have an array with these datas:
[['1', '7', '14'], ['1', '1', '3'], ['1', '12', '3'], ['2', '3', '1'], ['1', '4', '9']]
I like to sort it (multiple):
>>> sorted(datas,key=lambda x:(x[0], x[1]))
[['1', '1', '3'], ['1', '12', '3'], ['1', '4', '9'], ['1', '7', '14'], ['2', '3', '1']]
but after sorted as it seems the 12 < 4. It should be:
[['1', '1', '3'], ['1', '4', '9'], ['1', '7', '14'], ['1', '12', '3'], ['2', '3', '1']]
Any idea? I need not natural sorting.
There is not wrong with sorted behaviour. Your data are lists of string, so it's doable.
>>> data = ['1', '12', '3', '2']
>>> sorted(data)
['1', '12', '2', '3']
If you want to sort as integer, it must be converted.
>>> sorted(data)
['1', '12', '2', '3']
>>> data = [['1', '7', '14'], ['1', '1', '3'], ['1', '12', '3'], ['2', '3', '1'], ['1', '4', '9']]
>>> sorted(data, key=lambda x: map(int, x))
[['1', '1', '3'], ['1', '4', '9'], ['1', '7', '14'], ['1', '12', '3'], ['2', '3', '1']]
Convert x[1] to int(x[1]):
sorted(d,key=lambda x:(int(x[0]), int(x[1])))
Output:
[['1', '1', '3'], ['1', '4', '9'], ['1', '7', '14'], ['1', '12', '3'], ['2', '3', '1']]
You are comparing strings, not ints. Therefor the order you get is the lexicographical order.
If you convert to int first
sorted(data, key=lambda x:(int(x[0]), int(x[1])))
you will get the desired result
[['1', '1', '3'], ['1', '4', '9'], ['1', '7', '14'], ['1', '12', '3'], ['2', '3', '1']]
Currently your sort is working on tuples string values. String values are determined similarly to any other iterable. When it compares two strings, it goes character by character from left-to-right or index 0 to index n-1 where n is the length of the iterable, until it finds one character that is larger than another. so when comparing '12' and '4', it notices that '4' is greater than '1' so it finishes right there. This system of ordering is known as lexicographical order.
To see the "value" of a character (In Python, a character is just a string of length 1), just use the ord function:
>>> ord('1')
49
>>> ord('4')
52
And to validate that the string '12' is indeed less than '4' because ord('1') < ord('4'):
>>> '12' < '4'
True
If you want to sort by the integer values of the strings, you have to convert the strings to ints by using the built-in int constructor.
sorted(datas,key=lambda x: (int(x[0]), int(x[1]))
Or if you want to cleanly handle iterables of all sizes, simply use a tuple-generator for the key:
sorted(datas,key=lambda x: tuple(int(e) for e in x))
Related
A = [['0', '6', '4', '3'], ['0', '2', '8', '3'], ['0', '4', '1', '5'], ['0', '3', '2', '5']]
B = ['0', '4', '1', '5']
Say I want to find out at which line does B equal to the list. How do I write a solution for this?
The answer would be the third line.
I tried doing a for loop.
You can try list.index(element) to get the index of the element in the original list (A). In your terminology, to get the line just add one to the index.
line = A.index(B) + 1
you dont need use loops.you can get the element index by indexing.
A = [['0', '6', '4', '3'], ['0', '2', '8', '3'], ['0', '4', '1', '5'], ['0', '3', '2', '5']]
B = ['0', '4', '1', '5']
print(A.index(B))
>>> 2
I wish to check if a certain number is in a list, but I'm struggling with the if sentence. I have
possibilities = []
node = 1
edges = [['1', '1', '10'], ['1', '6', '7'], ['1', '16', '5'], ['1', '18', '6'], ['1', '19', '6'], ['2', '2', '10'], ['2', '5', '3']]
for i in edges:
if node in i[:-1]:
possibilities.append(i)
print(possibilities)
But I'm not getting any entries in possibilities. Is there something I'm missing, because I thought this should work.
You are confusing the data type int with data type string. Change the node to "1" instead of 1 and you are good to go.
You are trying to compare int data type with str data type. Try this:
possibilities = []
node = 1
edges = [['1', '1', '10'], ['1', '6', '7'], ['1', '16', '5'], ['1', '18', '6'], ['1', '19', '6'], ['2', '2', '10'], ['2', '5', '3']]
for i in edges:
if str(node) in i:
possibilities.append(i)
print(possibilities)
I am trying to get all possible pattern from list like:
input_x = ['1', ['2', '2x'], '3', '4', ['5', '5x']]
As we see, it has 2 nested list ['2', '2x'] and ['5', '5x'] here.
That means all possible pattern is 4 (2 case x 2 case), the expect output is:
output1 = ['1','2' , '3', '4', '5']
output2 = ['1','2x', '3', '4', '5']
output3 = ['1','2' , '3', '4', '5x']
output4 = ['1','2x', '3', '4', '5x']
I tried to search how to, but I can not find any examples (because of I have no idea about "keyword" to search)
I think python has inner libraries/methods to handle it.
One way to achieve this is via using itertools.product. But for using that, you need to firstly wrap the single elements within your list to another list.
For example, firstly we need to convert your list:
['1', ['2', '2x'], '3', '4', ['5', '5x']]
to:
[['1'], ['2', '2x'], ['3'], ['4'], ['5', '5x']]
This can be done via below list comprehension as:
formatted_list = [(l if isinstance(l, list) else [l]) for l in my_list]
# Here `formatted_list` is containing the elements in your desired format, i.e.:
# [['1'], ['2', '2x'], ['3'], ['4'], ['5', '5x']]
Now call itertools.product on the unpacked version of the above list:
>>> from itertools import product
# v `*` is used to unpack the `formatted_list` list
>>> list(product(*formatted_list))
[('1', '2', '3', '4', '5'), ('1', '2', '3', '4', '5x'), ('1', '2x', '3', '4', '5'), ('1', '2x', '3', '4', '5x')]
If you don't want to convert your list to all sub list then
You can try something like this :
input_x = ['1', ['2', '2x'], '3', '4', ['5', '5x'],['6','6x']]
import itertools
non_li=[]
li=[]
for i in input_x:
if isinstance(i,list):
li.append(i)
else:
non_li.append(i)
for i in itertools.product(*li):
sub=non_li[:]
sub.extend(i)
print(sorted(sub))
output:
['1', '2', '3', '4', '5', '6']
['1', '2', '3', '4', '5', '6x']
['1', '2', '3', '4', '5x', '6']
['1', '2', '3', '4', '5x', '6x']
['1', '2x', '3', '4', '5', '6']
['1', '2x', '3', '4', '5', '6x']
['1', '2x', '3', '4', '5x', '6']
['1', '2x', '3', '4', '5x', '6x']
Example, I have list, I can sort it by first 2 fields, that's ok:
import operator
list = [
['1', '2', '3'],
['1', '5', '6'],
['2', '8', '9', '8', '17'],
['2', '3', '5', '3'],
['1', '14', '89', '34', '15'],
]
sorted_list = sorted(list, key=operator.itemgetter(0, 1))
getting:
['1', '14', '89', '34', '15']
['1', '2', '3']
['1', '5', '6']
['2', '3', '5', '3']
['2', '8', '9', '8', '17']
So, what I need - is to combine those lists by 1st field, in first step it would be '1' from [0][0], [0][1], [0][2], then I join second field with something like comma: "14:2:5" and I don't care, which part of those 3 list I append then, so, after '|' it can be any of:
['1', '14:2:5',| '89', '34', '15']
['1', '14:2:5',| '3']
['1', '14:2:5',| '6']
(in most cases data after '|' will match for first field)
In the end, I want something like:
['1', '14:2:5', '89', '34', '15']
['2', '3:8', '5', '3']
I'm currently into some sort of for loop and getting IndexError all the way :-(
I feel it should be much easier pythonic way.
Don't get yet how to find this algorithm and how it called. Something like list of lists reduce-map-shrink-normalization elements, appending another by element values?
Thanks a lot for helping, things in python still surprise me, how cool it could be done, in the end from all answers, for python 3:
# -*- coding: utf-8 -*-
import operator
import itertools
from natsort import humansorted
list_to_sort = [
['1', 'A', '3'],
['1', '5', '6'],
['1', '1', '10', '11', '12'],
['t', 'S', '7', '0asdf'],
['2', '8', '9', '8', '17'],
['2', '705', '5', '3'],
['2', 'checks', 'df', '1'],
['1', '14', '89', '34', '15'],
]
sorted_list = humansorted(list_to_sort, key=operator.itemgetter(0, 1))
grouped = [list(g) for k, g in itertools.groupby(sorted_list, key=lambda x: x[0])]
out = [[gg[0][0], ':'.join([g[1] for g in gg])] + gg[0][2:] for gg in grouped]
for elem in out:
print(elem)
Once you've sorted your list so that your first-field groups are contiguous, you can use itertools.groupby to do the heavy lifting:
>>> grouped = [list(g) for k,g in groupby(sorted_list, key=lambda x: x[0])]
>>> grouped
[[['1', '14', '89', '34', '15'], ['1', '2', '3'], ['1', '5', '6']], [['2', '3', '5', '3'], ['2', '8', '9', '8', '17']]]
>>> out = [[gg[0][0], ':'.join([g[1] for g in gg])] + gg[0][2:] for gg in grouped]
>>> out
[['1', '14:2:5', '89', '34', '15'], ['2', '3:8', '5', '3']]
You can use a dictionary to preserve the sublist with common first item then use a list comprehension with zip and join to create the desire result:
>>> d={}
>>>
>>> for i,*j in sorted_list:
... d.setdefault(i,[]).append(iter(j))
...
>>> [[i,':'.join(next(zip(*j)))]+next(j)[1:] for i,j in d.items()]
[['1', '14:2:5', '89', '34', '15'], ['2', '3:8', '5', '3']]
But note that this code have been written in python 3. if you are in python 2 you can use itertools.izip instead of zip and for creating the dictionary you can do :
>>> for i in sorted_list:
... d.setdefault(i[0],[]).append(iter(i[1:]))
You could do it with a pair of functions as shown below. The first one, named grouper, is a generator function which are often useful when there's a need to produce intermediate results from a process requires non-trivial amount of initialization and/or housekeeping to be done before multiple intermediate results are returned.
As #Ashwini Chaudhary pointed out in a comment, you were sorting the fields lexicographically, not numerically, in your code, so that issue was also corrected.
import operator
def grouper(a_list):
if a_list:
sorted_list = sorted(a_list,
key=lambda e, get_items=operator.itemgetter(0, 1):
map(int, get_items(e)))
g = [sorted_list[0]]
for x in sorted_list[1:]:
if x[0] == g[-1][0]:
g.append(x)
else:
yield g
g = [x]
yield g
def combiner(a_list):
return [[g[0][0], ':'.join(e[1] for e in g)] + g[0][2:]
for g in grouper(a_list)]
a_list = [
['1', '2', '3'],
['1', '1', '10', '11', '12'], # element added to test sorting
['1', '5', '6'],
['2', '8', '9', '8', '17'],
['2', '3', '5', '3'],
['1', '14', '89', '34', '15'],
]
print(combiner(a_list))
Output:
[['1', '1:2:5:14', '10', '11', '12'], ['2', '3:8', '5', '3']]
Suppose you have an array p:
p=[['0', '9', '10', '13'], ['1', '8', '10', '14'],
['2', '7', '9', '15'], ['3', '2', '8', '18'],
['4', '1', '7', '19'], ['5', '1', '2', '20'],
['6', '2', '4', '21'], ['7', '1', '3', '22'],
['8', '4', '6', '25'], ['9', '3', '5', '26'],
['10', '5', '6', '27'], ['11', '8', '10', '28'],
['12', '7', '9', '31'], ['13', '1', '2', '32'],
['14', '4', '6', '33'], ['15', '3', '5', '34']]
I am interested in knowing the indexes of elements based on a partial string that should always match with elements in p at positions [1] and [2] (In p that would be 9,10, 8,10, 7,9, 2,8 etc). For example, I would like to know the indexes of elements containing the partial string '1','2'. In such case, the indexes should be: [5,13] that corresponds to elements:
['5', '1', '2', '20']
['13', '1', '2', '32']
I found this related question in stackoverflow: python - find index postion in list based of partial string. However, when I have tried with only 2 as the partial string I have got the following result (which is perfect):
>>> indices = [i for i, s in enumerate(p) if '2' in s]
>>> indices
[2, 3, 5, 6, 13]
>>>
But then, if I try with ('1','2') I get nothing:
>>> indices = [i for i, s in enumerate(p) if ('1','2') in s]
>>> indices
[]
How should I write the partial string in the if so that this code work?. I really don't understand why it is not working. I'd be happy if somebody could help me to understand this. I have tried: putting the partial string between [], define it as an array before the conditional and that doesn't work either. Is there another way to accomplish the same task?, because I am aware that the mere use of the code presented in the link mentioned above does not search matches in positions [1] and [2].
You need something like this:
indices = [i for i, s in enumerate(p) if ('1','2') == (s[1],s[2])]
In your list comprehension, each s is an array of strings, and the if statement is checking whether a tuple of two strings is contained in that list, which always evaluates to false.
>>> '2' in ['5', '1', '2', '20']
True
>>> ('1', '2') in ['5', '1', '2', '20']
False
>>> ['1', '2'] in ['5', '1', '2', '20']
False
This is why the resulting indices is empty.
you need to use itertool.combinations like in this example:
import itertools
indices = [i for i, s in enumerate(p) if ('1', '2') in list(itertools.combinations(s, 2))]
print indices
and the output will be :
[5, 13]
knowing that using listover itertools uses lots of memory if you have big list.
hope this helps.