Python 3.6 Create mapping from an array with sizes - python

Task
I have an array with sizes
sizes = [5, 3, 1, 2]
and based on the sizes I want to create the following array
mapping = [0, 0, 0, 0, 0, 1, 1, 1, 2, 3, 3]
Solution
My first attempt
mapping = []
ctr = 0
for i in range(len(sizes)):
for j in range(sizes[i]):
mapping.append(i)
ctr += 1
Shorter version
mapping = [[i for _ in range(sizes[i])] for i in range(len(sizes))]
mapping = list(itertools.chain(*mapping))
Question
One line version?
Is it possible to do it in just one line with a neat code?

Using enumerate
Ex:
sizes = [5, 3, 1, 2]
result = [i for i, v in enumerate(sizes) for _ in range(v)]
print(result)
Output:
[0, 0, 0, 0, 0, 1, 1, 1, 2, 3, 3]

Another approach would be to multiply the indices into sublists [[0, 0, 0, 0, 0], [1, 1, 1], [2], [3, 3]]
then flatten the result with itertoo.chain.from_iterable:
>>> from itertools import chain
>>> sizes = [5, 3, 1, 2]
>>> list(chain.from_iterable([i] * x for i, x in enumerate(sizes)))
[0, 0, 0, 0, 0, 1, 1, 1, 2, 3, 3]

Related

Extract upper diagonal of a block matrix in numpy - like np.triu_idx, but for block matrices

I want to extract off-block-diagonal elements from a block-diagonal matrix, i.e.
import numpy as np
import scipy as sp
A = np.array([
[1, 1, 1],
[1, 1, 1],
[1, 1, 1]
])
B = np.array([
[2, 2],
[2, 2]
])
C = np.array([
[3]
])
D = sp.linalg.block_diag(A, B, C)
print(D)
>>> array([[1, 1, 1, 0, 0, 0],
[1, 1, 1, 0, 0, 0],
[1, 1, 1, 0, 0, 0],
[0, 0, 0, 2, 2, 0],
[0, 0, 0, 2, 2, 0],
[0, 0, 0, 0, 0, 3]])
So, I need to extract the elements over the diagonal that do not belong to blocks, i.e. that ones which are zeros in D.
How to achieve that?
A straightforwad solution based on loops, can one make it better avoiding loops?
Taking upper triangle and then taking non-zero values is not what I want as it will not allow me to get indices for the original block matrix D, but only for it's upper triangle.
def block_triu_indices(block_sizes=None):
n = np.sum(block_sizes)
blocks = []
for block_size in block_sizes:
blocks.append(np.ones((block_size, block_size)))
A = sp.linalg.block_diag(*blocks)
row_idx = []
col_idx = []
for i in range(n):
for j in range(i+1, n):
if A[i,j]==0:
row_idx.append(i)
col_idx.append(j)
return (np.array(row_idx), np.array(col_idx))
block_triu_idx = block_triu_indices([3, 2, 1])
print(block_triu_idx)
>>> (array([0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 4]),
array([3, 4, 5, 3, 4, 5, 3, 4, 5, 5, 5]))
print(D[block_triu_idx])
>>> array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])

Trying to print multiple text in same line with append method but outputs are different [duplicate]

This question already has answers here:
List of lists changes reflected across sublists unexpectedly
(17 answers)
Closed 3 years ago.
Trying to print possible number combinations in single line as list but the list have wrong output. My output is like this:
[[1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1]]
When it should be like this:
[0, 0, 0]
[0, 0, 1]
[0, 1, 0]
[0, 1, 1]
[1, 0, 0]
[1, 0, 1]
[1, 1, 0]
[1, 1, 1]
My code is
if __name__ == '__main__':
x = 1
y = 1
z = 1
kordinat = ["x","y","z"]
result = []
for xx in range(x+1):
kordinat[0] = xx
for yy in range(y+1):
kordinat[1] = yy
for zz in range(z+1):
kordinat[2]= zz
print(kordinat)
result.append(kordinat)
print(result)
You should take itertools.product():
from itertools import product
result = list(product(range(2), repeat=3))
print(result)
# [(0, 0, 0), (0, 0, 1), (0, 1, 0), (0, 1, 1), (1, 0, 0), (1, 0, 1), (1, 1, 0), (1, 1, 1)]
EDIT:
This does not explain, why you end up having a list full of [1, 1, 1] elements. The reason can be found here. Through all your iterations, you work with a list called koordinat. When you append this to your overall list result, you append a reference to the very same object all the time. So you will have a list full of references to the same list koordinat. So changing koordinat to [1, 1, 1] in the last iteration, will change all references in your result list to this value as well. This can best be seen, when you print result after each append()
change this line:
result.append(kordinat)
to
result.append(kordinat.copy())
list is passed or assigned as reference so if you change the value it'll change everywhere.
If you want all the possible combinations of 0,1 of size 3, use combinations from itertools and call it as combinations([0,1],3). This will give you all the possible combinations you are expecting
To print elements of list in new line use pprint as below,
>>> from pprint import pprint as pp
>>> pp(result)
[[1, 1, 1],
[1, 1, 1],
[1, 1, 1],
[1, 1, 1],
[1, 1, 1],
[1, 1, 1],
[1, 1, 1],
[1, 1, 1]]
Edit-1:
Assuming, you are trying to find the binary numbers of the sequence. In your example, it is from 0-7(1-byte or 8-bits).
Try this,
>>> result = [[int(j) for j in "{0:03b}".format(i)] for i in range(8)]
>>> pp(result)
[[0, 0, 0],
[0, 0, 1],
[0, 1, 0],
[0, 1, 1],
[1, 0, 0],
[1, 0, 1],
[1, 1, 0],
[1, 1, 1]]

Get all permutations that pad a list with zeroes

I have a list ls of length n and want to get all lists of length m (where m > n) that contain ls in the same order, plus (m - n) zeroes, inserted at each possible combination of positions.
For example: ls = [1, 2, 3] and m = 4 should return
[[1, 2, 3, 0],
[1, 2, 0, 3],
[1, 0, 2, 3],
[0, 1, 2, 3]]
and ls = [1, 2, 3] and m = 5 should return
[[1, 2, 3, 0, 0],
[1, 2, 0, 3, 0],
[1, 2, 0, 0, 3],
[1, 0, 2, 3, 0],
[1, 0, 2, 0, 3],
[1, 0, 0, 2, 3],
[0, 1, 2, 3, 0],
[0, 1, 2, 0, 3],
[0, 1, 0, 2, 3],
[0, 0, 1, 2, 3]]
The solution should be fast and memory efficient - in particular, it should avoid generating duplicate solutions. Any help is appreciated!
A working (but inefficient) attempt:
ls = [1, 2, 3]
m = 4
from itertools import permutations
n = len(ls)
results = []
for t in set(permutations('1' * n + '0' * (m - n))):
idxs = [i for i, j in enumerate(t) if j == '1']
result = [0] * m
for idx, value in zip(idxs, ls):
result[idx] = value
results.append(result)
Use itertools.combinations to generate every combination of places to insert zeroes. Then use a list comprehension to select 0 or the next original element to build the new list.
# Pad list orig with zeroes, out to "m" total elements.
from itertools import combinations
orig = [1, 2, 3]
m = 5
n = len(orig)
padded = []
for pad_idx in combinations(range(m), m-n):
t = orig[:]
padded.append( [0 if i in pad_idx else t.pop(0)
for i in range(m)] )
print(padded)
Output (formatted for readability):
[[0, 0, 1, 2, 3],
[0, 1, 0, 2, 3],
[0, 1, 2, 0, 3],
[0, 1, 2, 3, 0],
[1, 0, 0, 2, 3],
[1, 0, 2, 0, 3],
[1, 0, 2, 3, 0],
[1, 2, 0, 0, 3],
[1, 2, 0, 3, 0],
[1, 2, 3, 0, 0]]

Python: using append within recursive function - overwrites previous elements

Could someone explain how to fix the below? I've read some explanations, but couldn't get my head around it...
Many thanks in advance!
k = 2 # number of possible values for each element, in this case 0 or 1
length = 3 # length of list
result = [0] * length # initialise list
results = []
# generate permutations of list
def permutations(i, k, length):
j = 0
while j < k:
result[i] = j
if i == length - 1:
print("Result: ", result)
results.append(result)
print("Results: ", results)
else:
permutations(i + 1, k, length)
j += 1
permutations(0, k, length)
Below the output. The problem is that all previous elements in the list are overwritten...
Result: [0, 0, 0]
Results: [[0, 0, 0]]
Result: [0, 0, 1]
Results: [[0, 0, 1], [0, 0, 1]]
Result: [0, 1, 0]
Results: [[0, 1, 0], [0, 1, 0], [0, 1, 0]]
Result: [0, 1, 1]
Results: [[0, 1, 1], [0, 1, 1], [0, 1, 1], [0, 1, 1]]
Result: [1, 0, 0]
Results: [[1, 0, 0], [1, 0, 0], [1, 0, 0], [1, 0, 0], [1, 0, 0]]
Result: [1, 0, 1]
Results: [[1, 0, 1], [1, 0, 1], [1, 0, 1], [1, 0, 1], [1, 0, 1], [1, 0, 1]]
Result: [1, 1, 0]
Results: [[1, 1, 0], [1, 1, 0], [1, 1, 0], [1, 1, 0], [1, 1, 0], [1, 1, 0], [1, 1, 0]]
Result: [1, 1, 1]
Results: [[1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1]]
You are appending the same list everytime. Modifying the list via that reference will propagate changes to every where the list object lives; it is the same list.
You should append a shallow copy instead, so the reference result only modifies the current list:
...
results.append(result[:])
Otherwise, you could create a new list object at the start of the function so each recursive call gets its own list:
def permutations(i, k, length):
result = []
...
I believe changing results.append(result) to result.append(result[:]) should fix the problem. It is because of the mutability of lists
What you implement can be described as repeated permutations or cartesian product.
There are k ** length lists or tuples that can be generated this way.
As with any combination, permutation or product, itertools can help you :
from itertools import product
k = 2 # number of possible values for each element, in this case 0 or 1
length = 3 # length of list
print(list(product(range(k), repeat=length)))
#[(0, 0, 0), (0, 0, 1), (0, 1, 0), (0, 1, 1), (1, 0, 0), (1, 0, 1), (1, 1, 0), (1, 1, 1)]
Done!

Appending a 1D list into a 2D list and modifying it later modifies previous entries too

I guess, list is an object, so this behavior must be due to its reference being appended, and so all the elements get updated! How does this work exactly and what's the right way to do this?
Code (to increment index position discovered):
# generating labels for debugging
#labels = [3]*3 + [1]*3 + [4]*2
labels = [3, 3, 3, 1, 1, 1, 4, 4]
print "labels list is", labels
counter_table = []
#counter = [0]*6
counter = [0 for i in range(6)]
for i in labels:
curr_label = i
counter[curr_label] = counter[curr_label] + 1
print "current counter list is", counter
counter_table.append(counter)
print "final counter_table is", counter_table
Output:
labels list is [3, 3, 3, 1, 1, 1, 4, 4]
current counter list is [0, 0, 0, 1, 0, 0]
current counter list is [0, 0, 0, 2, 0, 0]
current counter list is [0, 0, 0, 3, 0, 0]
current counter list is [0, 1, 0, 3, 0, 0]
current counter list is [0, 2, 0, 3, 0, 0]
current counter list is [0, 3, 0, 3, 0, 0]
current counter list is [0, 3, 0, 3, 1, 0]
current counter list is [0, 3, 0, 3, 2, 0]
final counter_table is [[0, 3, 0, 3, 2, 0], [0, 3, 0, 3, 2, 0], [0, 3, 0, 3, 2, 0], [0, 3, 0, 3, 2, 0], [0, 3, 0, 3, 2, 0], [0, 3, 0, 3, 2, 0], [0, 3, 0, 3, 2, 0], [0, 3, 0, 3, 2, 0]]
you're reusing the reference of counter, which is probably what you want to keep memory of what's happening.
So just store a copy of your list
counter = [0]*6
for curr_label in labels:
counter[curr_label] += 1
print "current counter list is", counter
counter_table.append(counter[:]) # create a copy
Aside: I saw that you were reluctant to use counter = [0]*6: it's perfectly all right to use * on list of immutable objects (no risk of using the same reference in that case)
You need to generate a copy of the list:
counter_table.append(list(counter))
otherwise a modification to counter will alter all occurrences in counter_table.

Categories