Related
I have a list of lists all of the same length. I would like to segment the first list into contiguous runs of a given value. I would then like to segment the remaining lists to match the segments generated from the first list.
For example:
Given value: 2
Given list of lists: [[0,0,2,2,2,1,1,1,2,3], [1,2,3,4,5,6,7,8,9,10], [1,1,1,1,1,1,1,1,1,1]
Return: [ [[2,2,2],[2]], [[3,4,5],[9]], [[1,1,1],[1]] ]
The closest I have gotten is to get the indices by:
>>> import itertools
>>> import operator
>>> x = 2
>>> L = [[0,0,2,2,2,1,1,1,2,3],[1,2,3,4,5,6,7,8,9,10],[1,1,1,1,1,1,1,1,1,1]]
>>> I = [[i for i,value in it] for key,it in itertools.groupby(enumerate(L[0]), key=operator.itemgetter(1)) if key == x]
>>> print I
[[2, 3, 4], [8]]
This code was modified from another question on this site.
I would like to find the most efficient way possible, since these lists may be very long.
EDIT:
Maybe if I place the lists one on top of each other it might be clearer:
[[0,0,[2,2,2],1,1,1,[2],3], -> [2,2,2],[2]
[1,2,[3,4,5],6,7,8,[9],10],-> [3,4,5],[9]
[1,1,[1,1,1],1,1,1,[1],1]] -> [1,1,1],[1]
You can use groupby to create a list of groups in the form of a tuple of starting index and length of the group, and use this list to extract the values from each sub-list:
from itertools import groupby
from operator import itemgetter
def match(L, x):
groups = [(next(g)[0], sum(1 for _ in g) + 1)
for k, g in groupby(enumerate(L[0]), key=itemgetter(1)) if k == x]
return [[lst[i: i + length] for i, length in groups] for lst in L]
so that:
match([[0,0,2,2,2,1,1,1,2,3], [1,2,3,4,5,6,7,8,9,10], [1,1,1,1,1,1,1,1,1,1]], 2)
returns:
[[[2, 2, 2], [2]], [[3, 4, 5], [9]], [[1, 1, 1], [1]]]
l=[[0,0,2,2,2,1,1,1,2,3], [1,2,3,4,5,6,7,8,9,10], [1,1,1,1,1,1,1,1,1,1]]
temp=l[0]
value=2
dict={}
k=-1
prev=-999
for i in range(0,len(temp)):
if(temp[i]==value):
if(prev!=-999 and prev==i-1):
if(k in dict):
dict[k].append(i)
else:
dict[k]=[i]
else:
k+=1
if(k in dict):
dict[k].append(i)
else:
dict[k]=[i]
prev=i
output=[]
for i in range(0,len(l)):
single=l[i]
final=[]
for keys in dict: #{0: [2, 3, 4], 1: [8]}
ans=[]
desired_indices=dict[keys]
for j in range(0,len(desired_indices)):
ans.append(single[desired_indices[j]])
final.append(ans)
output.append(final)
print(output) #[[[2, 2, 2], [2]], [[3, 4, 5], [9]], [[1, 1, 1], [1]]]
This seems to be one of the approach, this first creates the dictionary of contagious elements and then looks for that keys in every list and stores in output.
I have a nested list as an example:
lst_a = [[1,2,3,5], [1,2,3,7], [1,2,3,9], [1,2,6,8]]
I'm trying to check if the first 3 indices of a nested list element are the same as other.
I.e.
if [1,2,3] exists in other lists, remove all the other nested list elements that contain that. So that the nested list is unique.
I'm not sure the most pythonic way of doing this would be.
for i in range(0, len(lst_a)):
if lst[i][:3] == lst[i-1][:3]:
lst[i].pop()
Desired output:
lst_a = [[1,2,3,9], [1,2,6,8]]
If, as you said in comments, sublists that have the same first three elements are always next to each other (but the list is not necessarily sorted) you can use itertools.groupby to group those elements and then get the next from each of the groups.
>>> from itertools import groupby
>>> lst_a = [[1,2,3,5], [1,2,3,7], [1,2,3,9], [1,2,6,8]]
>>> [next(g) for k, g in groupby(lst_a, key=lambda x: x[:3])]
[[1, 2, 3, 5], [1, 2, 6, 8]]
Or use a list comprehension with enumerate and compare the current element with the last one:
>>> [x for i, x in enumerate(lst_a) if i == 0 or lst_a[i-1][:3] != x[:3]]
[[1, 2, 3, 5], [1, 2, 6, 8]]
This does not require any imports, but IMHO when using groupby it is much clearer what the code is supposed to do. Note, however, that unlike your method, both of those will create a new filtered list, instead of updating/deleting from the original list.
I think you are missing a loop For if you want to check all possibilities. I guess it should like :
for i in range(0, len(lst_a)):
for j in range(i, len(lst_a)):
if lst[i][:3] == lst[j][:3]:
lst[i].pop()
Deleting while going throught the list is maybe not the best idea you should delete unwanted elements at the end
Going with your approach, Find the below code:
lst=[lst_a[0]]
for li in lst_a[1:]:
if li[:3]!=lst[0][:3]:
lst.append(li)
print(lst)
Hope this helps!
You can use a dictionary to filter a list:
dct = {tuple(i[:3]): i for i in lst}
# {(1, 2, 3): [1, 2, 3, 9], (1, 2, 6): [1, 2, 6, 8]}
list(dct.values())
# [[1, 2, 3, 9], [1, 2, 6, 8]]
I am trying to split a nested list into multiple lists and assign their name dynamically. Untill now, I tried the code below, but it only works when we have equal length sublists and we give them names manually.
sub_list = [[1,2,3],[4,5,5], [2,63,6]]
l1, l2, l3 = map(list, zip(*sub_list))
print(l1)
print(l2)
print(l3)
# Output
[1, 4, 2]
[2, 5, 63]
[3, 5, 6]
The approach above will fail when we have unequal length sublists such as (sub_list = [[1,2,3],[4,5], [2]]) and it does not give lists dynamic names.
I know it can be done by for loop, but I am not able to make list_name using a loop.
Any help will help me to reach more closure to my work
you could use zip_longest from itertools as follows:
sub_list = [[1,2,3],[4,5], [2]]
from itertools import zip_longest
l1, l2, l3 = map(list, zip_longest(*sub_list))
print(l1)
print(l2)
print(l3)
Output:
# [1, 4, 2]
# [2, 5, None]
# [3, None, None]
Answering the first question: If you don't want to give a manual name assing the map() to just one variable:
sub_list = [[1,2,3],[4,5,5], [2,63,6]]
rotated = map(list, zip(*sub_list))
for r in rotated:
print(r)
# Output
# [1, 4, 2]
# [2, 5, 63]
# [3, 5, 6]
Not completely sure what you want to accomplish, but I suggest you take a look at:
How to use itertools.zip_longest(): Python: zip-like function that pads to longest length? (You can filter out the Nones afterwards)
How to create dynamically named vars (although this is generally not the best thing to do): How do I create a variable number of variables?
The following code performs in both of your special cases:
There are no errors if some input lists are shorter than others
Names are procedurally/dynamically generated
def rotate_list_matrix(rows):
nrows = len(rows)
col_counts = map(lambda lyst: len(lyst), rows)
ncols = max(col_counts)
for ci in range(0, ncols): # column index
lyst = list()
list_name = "l" + str(ci + 1)
globals()[list_name] = lyst
for ri in range(0, nrows):
try:
lyst.append(rows[ri][ci])
except:
break
return
list_mata = [[1, 2, 3],
[4, 5, 6],
[7, 8, 9]]
list_matb = [[1, 2, 3],
[4, 5 ],
[7 ]]
rotate_list_matrix(list_matb)
print(l1)
print(l2)
print(l3)
I would like to apply this idea to separate a list of lists into individually named list. Say L = [[0,1,2],[3,4,5],[6,7,8]] and I want the first list ([0,1,2]) to be separate to give L0 = l[0] (first list within the list is labeled as L0). My ideas is this:
for i in range(11):
'l{}'.format(i) = l[i]
but the error message is this: can't assign to function call. I am hoping to accomplish this ultimately:
for i in range(11):
list( 'l{}'.format(i)) = l[i]
in order to convert the string into a list. Anyone know of an way to make this work or is the idea a bust?
The simplest way to do this is using tuple packing/unpacking
L = [[0,1,2],[3,4,5],[6,7,8]]
L0, L1, L2 = L
print(L0)
print(L1)
print(L2)
result:
[0, 1, 2]
[3, 4, 5]
[6, 7, 8]
You can create variables dynamically (However I don't think its a good idea). Dynamically creating variables should not be used where a dict can be used instead. If you do not know how to create dynamic variables its probably means you shouldn't. there is more than enough info in the net to look up on a quick google search to know why.
That being said here is a way to do what you want using the dict method.
L = [[0,1,2],[3,4,5],[6,7,8]]
my_dict = {}
for x in range(len(L)):
my_dict[("L%s"%(x))] = L[x]
print (my_dict)
The result would be:
{'L0': [0, 1, 2], 'L1': [3, 4, 5], 'L2': [6, 7, 8]}
Then all you need to do is interact with the dict to get anything you need from those list.
You can use exec to execute a string as if it were Python code:
for i in range(len(L)):
exec('L{} = L[{}]'.format(i, i))
will give you variables L0, L1, L2 in your case.
L = [[0,1,2],[3,4,5],[6,7,8]]
myDict = {}
for index,inner_list in enumerate(L):
key = 'L'+str(index)
myDict[key]=inner_list
print(myDict)
RESULT
{'L0': [0, 1, 2], 'L1': [3, 4, 5], 'L2': [6, 7, 8]}
From this list:
N = [1,2,2,3,3,3,4,4,4,4,5,5,5,5,5]
I'm trying to create:
L = [[1],[2,2],[3,3,3],[4,4,4,4],[5,5,5,5,5]]
Any value which is found to be the same is grouped into it's own sublist.
Here is my attempt so far, I'm thinking I should use a while loop?
global n
n = [1,2,2,3,3,3,4,4,4,4,5,5,5,5,5] #Sorted list
l = [] #Empty list to append values to
def compare(val):
""" This function receives index values
from the n list (n[0] etc) """
global valin
valin = val
global count
count = 0
for i in xrange(len(n)):
if valin == n[count]: # If the input value i.e. n[x] == n[iteration]
temp = valin, n[count]
l.append(temp) #append the values to a new list
count +=1
else:
count +=1
for x in xrange (len(n)):
compare(n[x]) #pass the n[x] to compare function
Use itertools.groupby:
from itertools import groupby
N = [1,2,2,3,3,3,4,4,4,4,5,5,5,5,5]
print([list(j) for i, j in groupby(N)])
Output:
[[1], [2, 2], [3, 3, 3], [4, 4, 4, 4], [5, 5, 5, 5, 5]]
Side note: Prevent from using global variable when you don't need to.
Someone mentions for N=[1, 2, 2, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 5, 1] it will get [[1], [2, 2], [3, 3, 3], [4, 4, 4, 4], [5, 5, 5, 5, 5], [1]]
In other words, when numbers of the list isn't in order or it is a mess list, it's not available.
So I have better answer to solve this problem.
from collections import Counter
N = [1,2,2,3,3,3,4,4,4,4,5,5,5,5,5]
C = Counter(N)
print [ [k,]*v for k,v in C.items()]
You can use itertools.groupby along with a list comprehension
>>> l = [1,2,2,3,3,3,4,4,4,4,5,5,5,5,5]
>>> [list(v) for k,v in itertools.groupby(l)]
[[1], [2, 2], [3, 3, 3], [4, 4, 4, 4], [5, 5, 5, 5, 5]]
This can be assigned to the variable L as in
L = [list(v) for k,v in itertools.groupby(l)]
You're overcomplicating this.
What you want to do is: for each value, if it's the same as the last value, just append it to the list of last values; otherwise, create a new list. You can translate that English directly to Python:
new_list = []
for value in old_list:
if new_list and new_list[-1][0] == value:
new_list[-1].append(value)
else:
new_list.append([value])
There are even simpler ways to do this if you're willing to get a bit more abstract, e.g., by using the grouping functions in itertools. But this should be easy to understand.
If you really need to do this with a while loop, you can translate any for loop into a while loop like this:
for value in iterable:
do_stuff(value)
iterator = iter(iterable)
while True:
try:
value = next(iterator)
except StopIteration:
break
do_stuff(value)
Or, if you know the iterable is a sequence, you can use a slightly simpler while loop:
index = 0
while index < len(sequence):
value = sequence[index]
do_stuff(value)
index += 1
But both of these make your code less readable, less Pythonic, more complicated, less efficient, easier to get wrong, etc.
You can do that using numpy too:
import numpy as np
N = np.array([1,2,2,3,3,3,4,4,4,4,5,5,5,5,5])
counter = np.arange(1, np.alen(N))
L = np.split(N, counter[N[1:]!=N[:-1]])
The advantage of this method is when you have another list which is related to N and you want to split it in the same way.
Another slightly different solution that doesn't rely on itertools:
#!/usr/bin/env python
def group(items):
"""
groups a sorted list of integers into sublists based on the integer key
"""
if len(items) == 0:
return []
grouped_items = []
prev_item, rest_items = items[0], items[1:]
subgroup = [prev_item]
for item in rest_items:
if item != prev_item:
grouped_items.append(subgroup)
subgroup = []
subgroup.append(item)
prev_item = item
grouped_items.append(subgroup)
return grouped_items
print group([1,2,2,3,3,3,4,4,4,4,5,5,5,5,5])
# [[1], [2, 2], [3, 3, 3], [4, 4, 4, 4], [5, 5, 5, 5, 5]]