python split array into runs/sequences - preferably numpy - python

How can i split it into sequences/runs/subsequent-numbers, if the run is no longer than 3?
Having an array as follows
[1, 2, 3, 5, 9, 10, 16, 17, 18, 19]
My expected output would be the following arrays:
[1, 2, 3]
[5]
[9, 10]
[16, 17, 18]
[19]
e.g. [[1, 2, 3], [5], [9, 10], [16, 17, 18], [19]]
If it is a run of length 8 e.g. [1, 2, 3, 4, 5, 6, 7, 8] i would like to get 8 / 3 + 1 = 2 lists:
[1, 2, 3]
[4, 5, 6]
[7, 8]

If you name your current list, x and the new list of output, new_list, you can try (untested and assumes no repeat values in original list)
k = 0
new_list = [[]]
for i in range(len(x) - 1):
if x[i] not in new_list[max(k - 1, 0)]:
new_list[k].append(x[i])
for j in range(i + 1, len(x)):
if x[j] - x[i] == j - i and x[j] not in new_list[k]:
new_list[k].append(x[j])
k += 1
new_list.append([])
new_list = [x for x in new_list if x != []] # gets rid of empty list at the end

Here's my version that uses numpy:
import numpy as np
from itertools import chain
def split_list(mylist):
"""Function to do the initial split"""
# calculate differences
d = np.diff(mylist)
# when the differences are not 1 save that location
# we need a +1 to get make up for the lost element
breaks = list(np.arange(len(mylist) - 1)[d != 1] + 1)
slices = zip([0] + breaks, breaks + [len(mylist)])
# slice up the list
int_list = [mylist[a:b] for a, b in slices]
# chop up long sequences
chopped = [chop(l) for l in int_list]
# flatten the list once
return list(chain.from_iterable(chopped))
def chop(sublist, max_len=3):
"""Chops a list into chunks of length max_len"""
breaks = list(range(0, len(sublist), max_len))
slices = zip(breaks, breaks[1:] + [len(sublist)])
return [sublist[a:b] for a, b in slices]
Running this on the list's given:
>>> split_list([1, 2, 3, 5, 9, 10, 16, 17, 18, 19])
[[1, 2, 3], [5], [9, 10], [16, 17, 18], [19]]

Something like this?
def split_list(list, sub_list_size = 3):
sublists = []
for i in range(0, len(list), sub_list_size):
start = i
end = i + sub_list_size
sublists.append(list[start:end])
return sublists
# your list
mylist = [1,2,3,4,5,6,7,8, 9, 10, 11]
# your output
sublists = split_list(mylist)
print(sublists)
produces the following output
[[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11]]
The syntax list[start:end] will work even if end it is larger than the actual list size.
Hope this helps

Related

python get all sequences of numbers from list of numbers [duplicate]

This question already has answers here:
group together consecutive numbers in a list
(2 answers)
Closed 6 months ago.
I have a list of numbers:
lst = [0, 1, 3, 4, 6, 9, 10, 11, 12, 13, 15]
and I would like to get a list of lists like so:
[[0, 1], [3, 4], [6], [9, 10, 11, 12, 13], [15]]
this means I would like to get the available continuous unique sequences from the first list.
What have I tried?
I have clunky function which iterates over each element and checks if the element is equal to the previous element +1 and appends it if that was the case.
You could try something like this:
def sequences(lst):
ret = [] # Initialise return list
j = 0 # Index at which the last break was made
for i, n in enumerate(lst[1:]): # Iterate through indexes and numbers
if n != (lst[i] + 1): # Check if there is a step which is not 1
ret.append(lst[j:i+1]) # If there is, append this portion to ret
j = i+1 # Update j
ret.append(lst[j:]) # Add the last portion
return ret # Return
print(sequences([0, 1, 3, 4, 6, 9, 10, 11, 12, 13, 15]))
Output: [[0, 1], [3, 4], [6], [9, 10, 11, 12, 13], [15]]
self-explanatory code:
def notSoClunkyFunc(lst):
if len(lst)==0: return [] #returns if empty list
ans=[] #The final result you want
temp=[lst[0]] #the temporary list that will be added to the ans as per the question demands.
for i in range(1,len(lst)): #Loop that works on the same logic as suggested by you along with the question.
if lst[i]==temp[-1]+1:
temp.append(lst[i])
else:
ans.append(temp)
temp=[]
temp.append(lst[i])
if len(temp): ans.append(temp)
return ans
lst = [0, 1, 3, 4, 6, 9, 10, 11, 12, 13, 15]
print(notSoClunkyFunc(lst))
Output:
[[0, 1], [3, 4], [6], [9, 10, 11, 12, 13], [15]]

Fastest way to split a list into a list of lists based on another list of lists

Say I have a list that contains 5 unique integers in the range of 0 to 9.
import random
lst = random.sample(range(10), 5)
I also have a list of lists, which is obtained by splitting integers from 0 to 19 into 6 groups:
partitions = [[8, 12], [2, 4, 16, 19], [1, 6, 7, 13, 14, 17], [3, 15, 18], [5, 9, 10, 11], [0]]
Now I want to split lst based on the reference partitions.
For example, if I have
lst = [0, 1, 6, 8, 9]
I expect the output to be a list of lists like this:
res = [[0], [1, 6], [8], [9]]
I want the algorithm to be as fast as possible. Any suggestions?
res=[]
for sublist in partitions: # go through all sublists in partitions
match = [i for i in lst if i in sublist] # find matching numbers in sublist and lst
if match: # if it is empty don't append it to res
res.append(match)
# at this point res is [[8], [1, 6], [9], [0]]
print(sorted(res)) # use sorted to get desired output
I don't know if this is the fastest algorithm but it works
import random
lst = random.sample(range(10), 5)
partitions = [[8, 12], [2, 4, 16, 19], [1, 6, 7, 13, 14, 17], [3, 15, 18], [5, 9, 10, 11], [0]]
sequence = []
result = []
for i in range(5):
for j in range(len(partitions)):
if lst[i] in partitions[j]:
if j in sequence:
where = sequence.index(j)
result[where] += [lst[i]]
else:
result += [[lst[i]]]
sequence += [j]
break
print(result)

How to split a nested list into a smaller nested list

So I have a nested list as my input (and the nested list is always square. ie. same number of rows as columns). I want to break this list up into another nested list of which the elements are just 2x2 "parts" of the original list.
For example, if my input was
[[1,2,3,4],
[5,6,7,8],
[9,10,11,12],
[13,14,15,16]]
my output should be
[[1,2,5,6], [3,4,7,8], [9,10,13,14], [11,12,15,16]]
Another example:
input:
[[1,2,3],
[5,6,7],
[9,10,11],
output:
[[1,2,5,6],[3,7],[9,10],[11]]
I've tried making a nested for loop that goes through the first two columns and rows and makes that into a list and then appends that to another list and then repeats the process, but I get an index out of bounds exception error
This is what I've done so far
def get_2_by_2(map: List[List[int]]) -> int:
i = 0
j = 0
lst_2d = []
lst = []
for row in range(i, min(i+2, len(map))):
for column in range(j, min(j+2, len(map))):
print(row,column)
lst.append(map[row][column])
lst_2d.append(lst)
return lst_2d
basically this one only returns the first 2x2. I attempted using a while loop on the outside and incrementing the values of i and j and making my while loop dependent on one of them. that resulted in an index out of bounds.
You can iterate through the rows and columns in a step of 2, and slice the list of lists accordingly:
def get_2_by_2(matrix):
output = []
for row in range(0, len(matrix), 2):
for col in range(0, len(matrix[0]), 2):
output.append([i for r in matrix[row: row + 2] for i in r[col: col + 2]])
return output
or with a nested list comprehension:
def get_2_by_2(matrix):
return [
[i for r in matrix[row: row + 2]
for i in r[col: col + 2]] for col in range(0, len(matrix[0]), 2)
]
so that given:
m = [[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12],
[13, 14, 15, 16]]
get_2_by_2(m) returns:
[[1, 2, 5, 6], [3, 4, 7, 8], [9, 10, 13, 14], [11, 12, 15, 16]]
and that given:
m = [[1, 2, 3],
[5, 6, 7],
[9, 10, 11]]
get_2_by_2(m) returns:
[[1, 2, 5, 6], [3, 7], [9, 10], [11]]

Python array segmented in ranges

I have the following array [1, 4, 7, 9, 2, 10, 5, 8] and I need to separate the array in 3 different arrays: one for values between 0 and 3, anther for 3 to 6 and anther for 6 and 25.The result must be something like that:
array1 = [1, 2]
array2 = [4, 5]
array3 = [7, 9, 10, 8]
Any idea about how to do it simple?
First, define your "pole" numbers
Second, generate your intervals from those "pole" numbers
Third, define as many lists as there are intervals.
Then for each interval, scan the list and appends items in the relevant list if they belong to the interval
code:
source = [1, 4, 7, 9, 2, 10, 5, 8]
poles = (0,3,6,25)
intervals = [(poles[i],poles[i+1]) for i in range(len(poles)-1)]
# will generate: intervals = [(0,3),(3,6),(6,25)]
output = [list() for _ in range(len(intervals))]
for out,(start,stop) in zip(output,intervals):
for s in source:
if start <= s <stop:
out.append(s)
print(output)
result:
[[1, 2], [4, 5], [7, 9, 10, 8]]
This solution has the advantage of being adaptable to more than 3 lists/intervals by adding more "pole" numbers.
EDIT: There's a nice & fast solution (O(log(N)*N)) if the output lists order don't matter:
first sort the input list
then generate the sliced sub-lists using bisect which returns insertion position of the provided numbers (left & right)
like this:
import bisect
source = sorted([1, 4, 7, 9, 2, 10, 5, 8])
poles = (0,3,6,25)
output = [source[bisect.bisect_left(source,poles[i]):bisect.bisect_right(source,poles[i+1])] for i in range(len(poles)-1)]
print(output)
result:
[[1, 2], [4, 5], [7, 8, 9, 10]]
You can do that in a very simple way using a combination of a for loop and range functions:
lists = ([], [], [])
for element in [1, 4, 7, 9, 2, 10, 5, 8]:
if element in range(0, 3):
lists[0].append(element)
elif element in range(3, 6):
lists[1].append(element)
elif element in range(6, 25):
lists[2].append(element)
array1, array2, array3 = lists
"One-line" solution using set.intersection(*others) and range(start, stop[, step]) functions:
l = [1, 4, 7, 9, 2, 10, 5, 8]
l1, l2, l3 = (list(set(l).intersection(range(3))), list(set(l).intersection(range(3,6))), list(set(l).intersection(range(6,25))))
print(l1)
print(l2)
print(l3)
The output:
[1, 2]
[4, 5]
[8, 9, 10, 7]
https://docs.python.org/3/library/stdtypes.html?highlight=intersection#set.intersection

How can I merge lists that have common elements within a list of lists?

I have a list like [[1, 2, 4], [2, 5], [0, 3, 7, 8], [12, 3, 6], [18, 14]]. How can I get a list that contains lists of all the lists that contain overlapping elements added together? For the example input, the result should be [[1, 2, 4, 5], [0, 3, 6, 7, 8, 12], [14, 18]].
a = [[1, 2, 4], [2, 5], [0, 3, 7, 8], [12, 3, 6], [18, 14]]
result = []
for s in a:
s = set(s)
for t in result:
if t & s:
t.update(s)
break
else:
result.append(s)
This will go one-by-one through the list and create a set from the current sublist (s). Then it will check in the results, if there is another set t that has a non-empty intersection with it. If that’s the case, the items from s are added to that set t. If there is no t with a non-empty intersection, then s is a new independent result and can be appended to the result list.
A problem like this is also a good example for a fixed-point iteration. In this case, you would look at the list and continue to merge sublists as long as you could still find lists that overlap. You could implement this using itertools.combinations to look at pairs of sublists:
result = [set(x) for x in a] # start with the original list of sets
fixedPoint = False # whether we found a fixed point
while not fixedPoint:
fixedPoint = True
for x, y in combinations(result, 2): # search all pairs …
if x & y: # … for a non-empty intersection
x.update(y)
result.remove(y)
# since we have changed the result, we haven’t found the fixed point
fixedPoint = False
# abort this iteration
break
One way I can think of doing this is through recursion. Start with one item, then loop until you find every number it's connected to. For each of these numbers, you must do the same. Hence the recursion. To make it more efficient, store numbers you've visited in a list and check it at the beginning of each recursive sequence to make sure you don't repeat any explorations.
A two liner:
a_set = [set(x) for x in a]
result = [list(x.union(y)) for i,x in enumerate(a_set) for y in a_set[i:]
if x.intersection(y) and x != y]
I have left the last step for you:
a = [[1, 2, 4], [2, 5], [0, 3, 7, 8], [12, 3, 6], [18, 14]]
result = [[1, 2, 4, 5], [0, 3, 6, 7, 8, 12], [14, 18]]
# each sub list
result2 = []
count = 0
print a
for sub_list in a:
print count
print "sub_list: " + str(sub_list)
a.pop(count)
print "a: " + str(a)
#each int
sub_list_extend_flag = False
for int_in_sub_list in sub_list:
print "int_in_sub_list: " + str(int_in_sub_list)
for other_sub_list in a:
print "current_other_sub_list: " + str(other_sub_list)
if int_in_sub_list in other_sub_list:
sub_list_extend_flag = True
other_sub_list.extend(sub_list)
result2.append(list(set(other_sub_list)))
if not sub_list_extend_flag:
result2.append(sub_list)
count += 1
print result2
Simple answer:
a = [[1, 2, 4], [2, 5], [0, 3, 7, 8], [12, 3, 6], [18, 14]]
for x in a:
for y in x:
print y
its more simple than first one:
box=[]
a = [[1, 2, 4], [2, 5], [0, 3, 7, 8], [12, 3, 6], [18, 14]]
for x in a:
for y in x:
box.append(y)
print box
Result:[1, 2, 4, 2, 5, 0, 3, 7, 8, 12, 3, 6, 18, 14]
And with this, you can compare the numbers:
box=[]
box2=""
a = [[1, 2, 4], [2, 5], [0, 3, 7, 8], [12, 3, 6], [18, 14]]
for x in a:
for y in x:
box.append(y)
print box
for a in box:
box2+=str(a)
print box2
Result: 12425037812361814
Also you can make it more cute:
print " ".join(box2)
Result: 1 2 4 2 5 0 3 7 8 1 2 3 6 1 8 1 4

Categories

Resources