Fast way to create two list of certain pattern

Viewed 95

I'd like to create two list (appearantly it doesnt need to be a list, can be tuple, array, numpy.array, see edit below) of a certain partern, lets call them list_1 and list_2

list_1 has the patern that can be described as (i is an integer)

{i}, {i, i + 17}, {i, i+17 , i + 17 + 17}, {i, i + 17, i + 17 + 17, i + 17 + 17 + 17},

But the pattern occurs twice, which means that the resulting list will look like

list_1 = [i, i, i + 17, i, i + 17 , i + 34, i, i + 17, i + 34, i + 51, 
          i, i, i + 17, i, i + 17 , i + 34, i, i + 17, i + 34, i + 51]

Currently I'm doing it as (with i = 2 in this example)

some_limit = 5    
list_1 = [17 * x + i for b in range(some_limit + 1) for x in range(b)]
list_1 += list_1

result

[2, 2, 19, 2, 19, 36, 2, 19, 36, 53, 2, 19, 36, 53, 70, 2, 2, 19, 2, 19, 36, 2, 19, 36, 53, 2, 19, 36, 53, 70]

When some_limit is a big number this takes time. Is there a faster way?

list_2 has a pattern that can be described as (j is an integer)

{j} {j+1, j+1}, {j+2, j+2, j+2}, {j+3, j+3, j+3, j+3}

This pattern also occours twice but with a shift which means that the resulting list will look like

list_2 = [j, j+1, j+1, j+2, j+2, j+2, j+3, j+3, j+3, j+3,
          j+shift, j+1+shift, j+1+shift, j+2+shift, j+2+shift, j+2+shift, j+3+shift, j+3+shift, j+3+shift, j+3+shift,

Currently I'm doing it as (with j = 0 in this example)

some_limit = 5
arithemic_list = [k for k in range(some_limit + 1)]
rows_index_temp = [item -1 + some_limit * j * 2 for item, count in zip(arithemic_list, arithemic_list) for k in range(count)]
rows_index_temp += [some_limit + elem for elem in rows_index_temp]

result

[0, 1, 1, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 5, 6, 6, 7, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, 9]

When some_limit is a big number this takes time. Is there a faster way?

Edit

This is used to create sparse matrices for the optimization package cvxopt. A matrix is built by providing elements with their row and column indicies. For example a 3x3 identity matrix is created by 3 lists, elements = [1,1,1], rows = [0, 1, 2] and columns = [0, 1, 2]. My matrix is very large which implies that the lists will be very large.

From the documentation

cvxopt.spmatrix(x, I, J[, size[, tc ] ]) I and J are sequences of integers (lists, tuples, array arrays, . . . ) or integer matrices (matrix objects with typecode 'i'), containing the row and column indices of the nonzero entries. The lengths of I and J must be equal. If they are matrices, they are treated as lists of indices stored in column-major order, i.e., as lists list(I), respectively, list(J)

https://readthedocs.org/projects/cvxopt/downloads/pdf/1.2.0/

Example Lets say we would like to create the following matrix:

[ 1.00e+00     0         0         0         0    ]
[ 2.00e+00     0         0      3.00e+00     0    ]
[    0         0         0         0      4.00e+00]

We are only interested in the non-zero-elements and we can group them as

element   row   column    
   1       0       0    
   2       1       0
   3       1       3
   4       2       4

Thus, by having

elements = [1,2,3,4,0] 
rows = [0,1,1,2, 3]
columns = [0,0, 3, 4, 4]
print(spmatrix(elements, rows, columns))
[ 1.00e+00     0         0         0         0    ]
[ 2.00e+00     0         0      3.00e+00     0    ]
[    0         0         0         0      4.00e+00]

Notice that the order doesnt mather, as long as the element,row,column are grouped together. Thus this would be equivalent.

elements = [4,2,3,1] 
rows = [2,1,1,0]
columns = [4,0, 3,0]
print(spmatrix(elements, rows, columns))
[ 1.00e+00     0         0         0         0    ]
[ 2.00e+00     0         0      3.00e+00     0    ]
[    0         0         0         0      4.00e+00]

What am I trying to create? (some_limit = 3)

enter image description here

This is basically a concatination of two lower triangual matrix, with a constant shift between its elements. So how would we describe this matrix? If we focus on the "upper" lower triangual part which contains -1:s.

We can group as

element   row   column    
   -1       0       2    
   -1       1       2
   -1       1       19
   -1       2       2
   -1       2       19
   -1       2       36

The "lower" triangular part with only 1:s can be grouped as

We can group as

element   row   column    
   1       3       2    
   1       4       2
   1       4       19
   1       5       2
   1       5       19
   1       5       36

Together this become

element   row   column    
  -1       0       2    
  -1       1       2
  -1       1       19
  -1       2       2
  -1       2       19
  -1       2       36       
   1       3       2    
   1       4       2
   1       4       19
   1       5       2
   1       5       19
   1       5       36

Or,

element   row   column    
  -1       0       2    
  -1       1       2
  -1       1       2 + 17
  -1       2       2
  -1       2       2 + 17
  -1       2       2 + 2*17       
   1       1 + 2   2    
   1       2 + 2   2
   1       2 + 2   2 + 17
   1       3 + 2   2
   1       3 + 2   2 + 17
   1       3 + 2   2 + 2*17

Since the order doesnt mather, as long as element, row, column is grouped togethere, there might be a "better" pattern which I havent consider.

The size of this matrix is directly correlated with the number of elements in the list describing the elements, rows, and columns. I wish to have some_limit >=4343 => the lenght of each of the list elements, rows, and columns will be (4343*(4343 + 1)/2)*2 which explodes quite fast...

some_limit
4343
len(list_1)
18865992
#size_of_matrix
<34744x73831 sparse matrix, tc='d', nnz=75463969>
1 Answers

You can convert your list comprehensions to generator expressions, then you do not have to create the entire list at once but can generate values as needed. In particular note the additional loop I added so that you do not have to create the entire list, then add a copy to itself.

i, j, limit, shift = 2, 0, 5, 10

gen_1 = (17 * x + i for _ in range(2)      # repeat twice
                    for b in range(limit)  # len of subsegments
                    for x in range(b+1))   # multiplier

gen_2 = (j + b + s for s in (0, 10)        # repeat with shift
                   for b in range(limit)   # len of subsegments
                   for _ in range(b+1))    # repeat b times

You can then either iterate the values as needed (or still create a list from all the values):

for x in gen_1:
    print(x)
print(list(gen_2))
Related