Splitting list based on difference between consecutive elements - python

I found this question that is related to mine. In that question a specific case is treated, and that's splitting a list of integers when a difference of more than 1 is present between consecutive elements.
I was wondering: is there way to make this work for a difference of N, a parameter? Namely, suppose we have this list:
[1,2,3,6,8,10,14,15,17,20]
For N=2, the output should be:
[[1,2,3], [6,8,10], [14,15,17], [20]]
For N=3, the output should be:
[[1,2,3,6,8,10], [14,15,17,20]]
And for N=4, the output should be the same input list.
I did it like this:
from itertools import takewhile
input_list = [1,2,3,6,8,10,14,15,17,20]
N = 4
def fun(l, N, output=[]):
if len(l):
output.append([x[1] for x in takewhile(lambda x: x[1]-x[0]<=N,
zip([l[0]]+l, l))])
fun(l[len(output[-1]):], N, output)
return output
fun(input_list, N)
But I don't really like it: it's unreadable. Something stylish as a one-liner or something pretty pythonic would be appreciated!

Two lines with list-comprehension:
def split_list(l, n):
index_list = [None] + [i for i in range(1, len(l)) if l[i] - l[i - 1] > n] + [None]
return [l[index_list[j - 1]:index_list[j]] for j in range(1, len(index_list))]
test:
example = [1, 2, 3, 6, 8, 10, 14, 15, 17, 20]
for i in range(2,5):
print(split_list(example, i))
# [[1, 2, 3], [6, 8, 10], [14, 15, 17], [20]]
# [[1, 2, 3, 6, 8, 10], [14, 15, 17, 20]]
# [[1, 2, 3, 6, 8, 10, 14, 15, 17, 20]]

def spacer(data, n=1):
set(data)
output = [[data[0]]]
for i in data[1:]:
if i - output[-1][-1] > n:
output.append([i])
else:
output[-1].append(i)
return output
data = [1, 2, 3, 6, 8, 10, 14, 15, 17, 20]
for i in range(1, 4):
print("N={}, {}".format(i, spacer(data, n=i)))
output:
N=1, [[1, 2, 3], [6], [8], [10], [14, 15], [17], [20]]
N=2, [[1, 2, 3], [6, 8, 10], [14, 15, 17], [20]]
N=3, [[1, 2, 3, 6, 8, 10], [14, 15, 17, 20]]

Related

How do I put data in a two-dimensional array sequentially without duplication?

I want to put data consisting of a one-dimensional array into a two-dimensional array. I will assume that the number of rows and columns is 5.
The code I tried is as follows.
data = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]
a = []
for i in range(5):
a.append([])
for j in range(5):
a[i].append(j)
print(a)
# result : [[0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4]]
# I want this : [[0, 1, 2, 3, 4], [5, 6, 7, 8, 9], [10, 11, 12, 13, 14], [15, 16, 17, 18, 19], [20]]
You don't have to worry about the last [20].
The important thing is that the row must change without duplicating the data.
I want to solve it, but I can't think of any way. I ask for your help.
There are two issues with the current code.
It doesn't actually use any of the values from the variable data.
The data does not contain enough items to populate a 5x5 array.
After adding 0 to the beginning of the variable data and using the values from the variable, the code becomes
data = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]
a = []
for i in range(5):
a.append([])
for j in range(5):
if i*5+j >= len(data):
break
a[i].append(data[i*5+j])
print(a)
The output of the new code will be
[[0, 1, 2, 3, 4], [5, 6, 7, 8, 9], [10, 11, 12, 13, 14], [15, 16, 17, 18, 19], [20]]
This should deliever the desired output
data = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]
a = []
x_i = 5
x_j = 5
for i in range(x_i):
a.append([])
for j in range(x_j):
a[i].append(i*x_j+j)
print(a)
Output:
[[0, 1, 2, 3, 4], [5, 6, 7, 8, 9], [10, 11, 12, 13, 14], [15, 16, 17, 18, 19], [20, 21, 22, 23, 24]]
By using list comprehension...
data = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]
columns = 5
rows = 5
result = [data[i * columns: (i + 1) * columns] for i in range(rows)]
print(result)
# [[0, 1, 2, 3, 4], [5, 6, 7, 8, 9], [10, 11, 12, 13, 14], [15, 16, 17, 18, 19], [20]]
You could use itertools.groupby with an integer division to create the groups
from itertools import groupby
data = [0, 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]
grouped_data = [list(v) for k, v in groupby(data, key=lambda x: x//5)]
print(grouped_data)
Output
[[0, 1, 2, 3, 4], [5, 6, 7, 8, 9], [10, 11, 12, 13, 14], [15, 16, 17, 18, 19], [20]]

Turn 1D list to a triangle 2D list

I have a list made of range from 1 to 55 with step 2: [1, 3, 5, 7, 9, 11, 13, 15, ..., 53].
What I'm trying to do is to fill another list, which is 2D and triangular, with numbers from the first list.
def odd_triangle(n):
a = []
b = []
for number in range(1, 55, 2):
a.append(number)
for i in range(n):
b.append([])
for j in range(i+1):
b[i].append(a[i])
print(b)
After I call that function, for example odd_triangle(5) (5 rows will be created), it gives me not exactly what I want to:
[[1], [3, 3], [5, 5, 5], [7, 7, 7, 7], [9, 9, 9, 9, 9]]
What I want it to be is: [[1], [3, 5], [7, 9, 11], [13, 15, 17, 19], [21, 23, 25, 27, 29]]
This will work:
a = list(range(1,55,2))
n = 5
it = iter(a)
b = list([next(it) for _ in range(i)] for i in range(1, n+1))
Gives:
[[1], [3, 5], [7, 9, 11], [13, 15, 17, 19], [21, 23, 25, 27, 29]]
Here, next(it) simply gets the next value from the iterator over a every time it is called.

Finding Non-Overlapping Sub Matrices in Python

Non-Overlapping Sub Matrices
I'm stuck at finding a way to get only the non-overlapping sub matrices.
My code below finds all the sub matrices.
Code:
n = 4
matrix = [[1,2,3,4],
[5,6,7,8],
[9,10,11,12],
[13,14,15,16]]
k = 2 #Finding 2x2 submatrices
t=[]
for i in range(n-k+1):
for j in range(n-k+1):
l=[]
for x in range(i,i+k):
for y in range(j,j+k):
if x==i or x==i+k-1 or y==j or y==j+k-1:
l.append(matrix[x][y])
t.append(l)
So if I print t I get:
for i in t:
print(i)
O/P:
[1, 2, 5, 6]
[2, 3, 6, 7]
[3, 4, 7, 8]
[5, 6, 9, 10]
[6, 7, 10, 11]
[7, 8, 11, 12]
[9, 10, 13, 14]
[10, 11, 14, 15]
[11, 12, 15, 16]
But the o/p I want is:
[1,2,5,6]
[3,4,7,8]
[9,10,13,14]
[11,12,15,16]
When you're looping over elements in range(n-k+1), you're calling range with arguments start=0, stop=n-k+1=3, step=1, which results in the array [0, 1, 2]. Using this as your starting point is the problem.
To ensure your result includes only mutually exclusive elements, change the step argument to k:
In [7]: t = []
...: for i in range(0, n, k):
...: for j in range(0, n, k):
...: t.append([
...: matrix[i+ii][j+jj]
...: for ii in range(k) for jj in range(k)])
In [8]: t
[[1, 2, 5, 6], [3, 4, 7, 8], [9, 10, 13, 14], [11, 12, 15, 16]]

Generate a 2D array with counter using list comprehension

I am new to python and learning more about list comprehensions. I want to generate a simple 2D array like this:
Expected List:
[[1, 2, 3, 4],[5, 6, 7, 8],[9, 10, 11, 12],[13, 14, 15, 16]]
What I have tried:
[[j for j in range(6)] for _ in range(6)]
[[0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4]]
Maybe i need some kind of counter instead of j inside the second loop, however doing count += 1 is not allowed here and will give a syntax error.
Simple solution using range(start, stop[, step]) function with step argument:
result = [list(range(i, i+4)) for i in range(1, 16, 4)]
print(result)
The output:
[[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, 16]]
For a positive step, the contents of a range r are determined by the
formula r[i] = start + step*i where i >= 0 and r[i] < stop.
[[a for a in range(1+(4*b), 5+(4*b))] for b in range(0, 4)]
this uses the same list comprehension.
Output:
[[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, 16]]
Using Nested list comprehensions :
[[j+i*4 for j in range(1,5)] for i in range(4)]
Output :
[[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, 16]]
Do your derivation by creating a for loop:
l = []
n = 4
for i in range(3):
l2 = [j + (n*i) for j in range(n)]
l.append(l2)
Then convert that to a list comprehension:
x = [[j + (n*i) for j in range(n)] for i in range(3)]
Output:
[[0, 1, 2, 3], [4, 5, 6, 7], [8, 9, 10, 11]]
You may also use get_chunks() function of utilspie library:
>>> from utilspie.iterutils import get_chunks
>>> list(get_chunks(range(1, 17), 4))
[[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, 16]]
Note: utilspie is not a built-in libary. You have to install it explicitly from pip via doing:
sudo pip install utilspie

Efficient way to find index of elements in a large list of integers starting with max to min elements

I have a large list of integers unsorted, numbers might be duplicated. I would like to create another list which is a list of sub-lists of indexes from the first list starting with max element to min, in decreasing order.
For example, if I have a list like this:
list = [4, 1, 4, 8, 5, 13, 2, 4, 3, 7, 14, 4, 4, 9, 12, 1, 6, 14, 10, 8, 6, 4, 11, 1, 2, 11, 3, 9]
The output should be:
indexList = [[10, 17], [5], [14], [22, 25], [18], [13, 27], [3, 19], [9], [16, 20], [4], [0, 2, 7, 11, 12, 21], [8, 26], [6, 24], [1, 15, 23]]
where, [10, 17] is the index of where '14' is present and so on...
Shared my code below. Profiling it using cProfile for a list of around 9000 elements takes around ~6 seconds.
def indexList(list):
# List with sorted elements
sortedList = sorted(list, reverse = True)
seen = set()
uSortedList = [x for x in sortedList if x not in seen and not seen.add(x)]
indexList = []
for e in uSortedList:
indexList.append([i for i, j in enumerate(list) if j == e])
return indexList
Here you go:
def get_list_indices(ls):
indices = {}
for n, i in enumerate(ls):
try:
indices[i].append(n)
except KeyError:
indices[i] = [n]
return [i[1] for i in sorted(indices.items(), reverse=True)]
test_list = [4, 1, 4, 8, 5, 13, 2, 4, 3, 7, 14, 4, 4, 9, 12, 1, 6, 14, 10, 8, 6, 4, 11, 1, 2, 11, 3, 9]
print(get_list_indices(test_list))
Based on some very basic testing, it is about twice as fast as the code you posted.

Categories