Separating specific values from a list in python - python

I have a list of lists(called table):
table = [[0, 1, 2], [1, 2, 1], [2, 3, 2], [0, 2, 3], [0, 3, 2], [1, 3, 3]]
where the first element of each sub list is the start point, the second is the endpoint and the third is the distance between the two points. So e.g. [0,1,2] means the gap between the point 0 and 1 is 2.
Now I want to use the information in table, to construct another list of lists(called distances) containing all the distances between all the points. So that, I could for example, call distances[0][2] (meaning I want the distance between point 0 and point 2, so the output should be 3).
However I am having trouble correctly separating the date from table and putting it into distances.
My code so far is this:
dstFromOnePoint = []
distances = []
numberOfPoints = 4 #0,1,2,3
for i in range(numberOfPoints): #loops through all start points
for j in range(numberOfPoints): # loops through all endpoints
for val in table:
if (val[0] == i and val[1] == j) or (val[0] == j and val[1] == i): # checks start node , end node
dst = val[2]
dstFromOnePoint.append(dst)
elif (i==j): #distance from one point to itself will be 0
dstFromOnePoint.append(0)
distances.append(dstFromOnePoint)
print(distances)
print(distances[0][2])
The output I get:
[[0, 0, 0, 0, 0, 0, 2, 3, 2, 2, 0, 0, 0, 0, 0, 0, 1, 3, 3, 1, 0, 0, 0, 0, 0, 0, 2, 2, 3, 2, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 2, 3, 2, 2, 0, 0, 0, 0, 0, 0, 1, 3, 3, 1, 0, 0, 0, 0, 0, 0, 2, 2, 3, 2, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 2, 3, 2, 2, 0, 0, 0, 0, 0, 0, 1, 3, 3, 1, 0, 0, 0, 0, 0, 0, 2, 2, 3, 2, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 2, 3, 2, 2, 0, 0, 0, 0, 0, 0, 1, 3, 3, 1, 0, 0, 0, 0, 0, 0, 2, 2, 3, 2, 0, 0, 0, 0, 0, 0]]
0
The output I should get:
[[0,2,3,2], [2,0,1,3], [3,1,0,2], [2,3,2,0]]
3
I think I'm using my loops incorrectly because I end up appending the same list multiple times, but even just looking at one individual sub list, I don't have the correct values, so I probably have more issues that I don't know how to fix?

Here is a correct and more compact solution:
numberOfPoints = 4
distances = [numberOfPoints * [0] for _ in range(numberOfPoints)]
for x, y, d in table:
distances[x][y] = d
distances[y][x] = d

You need to pre-populate the distance matrix and then assign per [i][j]
Fixes on your original solutions:
table = [[0, 1, 2], [1, 2, 1], [2, 3, 2], [0, 2, 3], [0, 3, 2], [1, 3, 3]]
distances = []
numberOfPoints = 4 #0,1,2,3
distances = [[0 for _ in range(numberOfPoints)] for _ in range(numberOfPoints)]
for i in range(numberOfPoints): #loops through all start points
for j in range(numberOfPoints): # loops through all endpoints
for val in table:
if (val[0] == i and val[1] == j) or (val[0] == j and val[1] == i): # checks start node , end node
dst = val[2]
distances[i][j] = dst
# actually you can drop this elif as distances is intitalized to 0
elif (i==j): #distance from one point to itself will be 0
# dstFromOnePoint.append(0)
distances[i][j] = 0
# distances.append(dstFromOnePoint)
print(distances)
print(distances[0][2])
gives:
[[0, 2, 3, 2], [2, 0, 1, 3], [3, 1, 0, 2], [2, 3, 2, 0]]
3

here is one line using itertools.groupby:
from itertools import groupby
[[next(g)[2] if j!=i else 0 for j in range(len(table[0]) + 1 )] for i, (k, g) in enumerate(groupby(sorted(e for f, s, t in table for e in [[f, s, t], [s, f, t]]), key=lambda x: x[0]))]
output:
[[0, 2, 3, 2], [2, 0, 1, 3], [3, 1, 0, 2], [2, 3, 2, 0]]

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])

How to split a list into sublists of 6 zeros in a row and other values

This is the list that I have:
a = [0, 0, 0, 0, 0, 0, 1, 1, 4, 5, 0, 0, 4, 0, 0, 0, 0, 0, 0, 1, 3, 4]
I would like to split the list into sublists of 6 zeros in a row and other values to have the desired output of
a = [[0, 0, 0, 0, 0, 0], [1, 1, 4, 5, 0, 0, 4], [0, 0, 0, 0, 0, 0], [1, 3, 4]]
I tried to convert list to string to use split function and then convert string back to list
b = ''.join(map(str,a))
c = b.split('0'*6)
d = list(map(int, c))
and ended up with invalid literal for int() with base 10: '' error message.
Is this the right way to do it? or is there a different way that doesnt involve switching between list and string to avoid such error message?
The following is a quite crude way of doing it:
a = [0, 0, 0, 0, 0, 0, 1, 1, 4, 5, 0, 0, 4, 0, 0, 0, 0, 0, 0, 1, 3, 4]
output, temp = [], []
while a:
if a[:6] == [0] * 6:
if temp:
output.append(temp)
temp = []
output.append([0] * 6)
a = a[6:]
else:
temp.append(a[0])
a = a[1:]
else:
if temp:
output.append(temp)
print(output)
# [[0, 0, 0, 0, 0, 0], [1, 1, 4, 5, 0, 0, 4], [0, 0, 0, 0, 0, 0], [1, 3, 4]]
This will deplete the original list a.
One way to do this is to make a generator that keeps track of the seen zeros so far and seen numbers so far. When you get six zeros, you yield it and any previous numbers. Otherwise move the zeros to the other list.
a = [0, 0, 0, 0, 0, 0, 1, 1, 4, 5, 0, 0, 4, 0, 0, 0, 0, 0, 0, 1, 3, 4]
def six_zeros(l):
z = []
nums = []
for n in l:
if n != 0:
nums.extend(z) # add zeros seen so far
nums.append(n) # append the number
z = []
else:
z.append(n) # append the zero
if len(z) == 6: # but you've seen 6 yield them
if nums:
yield nums
yield z
nums = []
z = []
if nums or z: # don't forget any left-overs
nums.extend(z)
yield nums
list(six_zeros(a))
# [[0, 0, 0, 0, 0, 0], [1, 1, 4, 5, 0, 0, 4], [0, 0, 0, 0, 0, 0], [1, 3, 4]]
I think this might be a simpler way:
a = [0, 0, 0, 0, 0, 0, 1, 1, 4, 5, 0, 0, 4, 0, 0, 0, 0, 0, 0, 1, 3, 4]
six_zeros = [0]*6
b, temp = [], []
for x in a:
temp.append(x)
temp_len = len(temp)
if temp_len >= 6 and temp[-6:] == six_zeros:
if temp_len > 6:
b.append(temp[:-6])
b.append(six_zeros)
temp.clear()
if temp:
b.append(temp)
print(b)
Output:
[[0, 0, 0, 0, 0, 0], [1, 1, 4, 5, 0, 0, 4], [0, 0, 0, 0, 0, 0], [1, 3, 4]]
Here's a working version of your attempt:
import re
a = [0, 0, 0, 0, 0, 0, 1, 1, 4, 5, 0, 0, 4, 0, 0, 0, 0, 0, 0, 1, 3, 4]
b = ''.join(map(str, a))
c = re.split(f"({'0'*6})", b)
d = [list(map(int, g)) for g in c if g]
print(d)
Output as desired:
[[0, 0, 0, 0, 0, 0], [1, 1, 4, 5, 0, 0, 4], [0, 0, 0, 0, 0, 0], [1, 3, 4]]
I used re.split because that includes the splitters when asked to.

An elegant way to split specific items in a list (array)?

For example, I have a list which only contains zeroes, ones and twos
ls = [
[1, 1, 0, 2, 2],
[1, 1, 1, 2, 2, 2],
[0, 1, 0, 0, 0, 2, 0]
]
I want to split this list it into two list,
ls1 contains the ones, and ls2 contains the twos. I would like to keep the shape and use 0 to replace 2's in ls1 and 1's in ls2. The expected result is:
ls1 = [
[1, 1, 0, 0, 0],
[1, 1, 1, 0, 0, 0],
[0, 1, 0, 0, 0, 0, 0]
]
ls2 = [
[0, 0, 0, 2, 2],
[0, 0, 0, 2, 2, 2],
[0, 0, 0, 0, 0, 2, 0]
]
I know I can use a for loop to handle it, but is there an elegant way?
ls1, ls2 = ([[b & x for b in a] for a in ls] for x in (1, 2))
Using nested list comprehension:
ls1 = [[1 if e == 1 else 0 for e in l] for l in ls]
ls2 = [[2 if e == 2 else 0 for e in l] for l in ls]
# ls1
[[1, 1, 0, 0, 0],
[1, 1, 1, 0, 0, 0],
[0, 1, 0, 0, 0, 0, 0]]
# ls2
[[0, 0, 0, 2, 2],
[0, 0, 0, 2, 2, 2],
[0, 0, 0, 0, 0, 2, 0]]
Nothing especially "elegant" about it, but you could simply use a list comprehension:
ls = [
[1, 1, 0, 2, 2],
[1, 1, 1, 2, 2, 2],
[0, 1, 0, 0, 0, 2, 0]
]
def keep_only(val, lst):
return [[v if v==val else 0 for v in sublist] for sublist in lst]
ls1 = keep_only(1, ls)
ls2 = keep_only(2, ls)
Output:
print(ls1)
print(ls2)
# [[1, 1, 0, 0, 0], [1, 1, 1, 0, 0, 0], [0, 1, 0, 0, 0, 0, 0]]
# [[0, 0, 0, 2, 2], [0, 0, 0, 2, 2, 2], [0, 0, 0, 0, 0, 2, 0]]

For loop with condition in Python

I'm stuck on this problem. How can I create a program that adds 1 randomly to a list of [0, 0, 0, 0, 0, 0]. And I wanted to keep it going until all the numbers in the list is at least > 0.
I have three problems in my code and I don't know what it is. This my code and the list Votes = [0, 0, 0, 0, 0, 0]:
It stops printing after the 7th time
Sometimes it’s starts with more than 1 in an item
I want it to start it with an just 1 in a list and from there add 1 randomly to a item in the list.
So I want at least one of each in the list which would be greater than 0.
My code is
Def get_all_prizes():
For items in votes:
Items = random.radiant(0, 5)
Votes[items] + = 1
If items == 0:
False
Else:
Print(votes)
This is what is printed
[0, 0, 0, 0, 1, 0]
[0, 0, 1, 0, 1, 0]
[0, 1, 1, 0, 1, 0]
[0, 1, 2, 0, 1, 0]
[0, 1, 3, 0, 1, 0]
[0, 1, 3, 1, 1, 0]
It only does 6 times, and sometimes it starts like this. I wanted it to start just by having just 1 number in the list like the previously printed stuffs
[0, 1, 2, 0, 1, 0]
[0, 1, 3, 0, 1, 0]
[0, 1, 3, 1, 1, 0]
If I understand your problem correctly, you could use all to check if every element is larger than 0 and keep looping with while until this condition is fullfilled:
import random
n = 6
votes = [0] * n
while not all(votes):
i = random.randrange(0, n)
votes[i] += 1
print(votes)
# [0, 0, 0, 0, 1, 0]
# [1, 0, 0, 0, 1, 0]
# [1, 0, 1, 0, 1, 0]
# [1, 0, 2, 0, 1, 0]
# [1, 0, 2, 0, 1, 1]
# [1, 0, 2, 1, 1, 1]
# [1, 0, 2, 2, 1, 1]
# [1, 0, 3, 2, 1, 1]
# [2, 0, 3, 2, 1, 1]
# [2, 0, 3, 3, 1, 1]
# [2, 0, 3, 4, 1, 1]
# [2, 0, 3, 4, 2, 1]
# [2, 0, 3, 5, 2, 1]
# [2, 0, 3, 6, 2, 1]
# [3, 0, 3, 6, 2, 1]
# [4, 0, 3, 6, 2, 1]
# [4, 0, 3, 6, 3, 1]
# [4, 0, 3, 6, 4, 1]
# [4, 1, 3, 6, 4, 1]

Modify every two largest elements of matrix rows and columns

In python, I have a matrix and I want to find the two largest elements in every row and every column and change their values to 1 (seperately, I mean get two matrices where one of them modified the rows and the other modified the cols).
The main goal is to get a corresponding matrix with zeros everywhere except those ones I've put in the 2 largest element of each row and column (using np.where(mat == 1, 1, 0).
I'm trying to use the np.argpartition in order to do so but without success.
Please help.
See image below.
Here's an approach with np.argpartition -
idx_row = np.argpartition(-a,2,axis=1)[:,:2]
out_row = np.zeros(a.shape,dtype=int)
out_row[np.arange(idx_row.shape[0])[:,None],idx_row] = 1
idx_col = np.argpartition(-a,2,axis=0)[:2]
out_col = np.zeros(a.shape,dtype=int)
out_col[idx_col,np.arange(idx_col.shape[1])] = 1
Sample input, output -
In [40]: a
Out[40]:
array([[ 3, 7, 1, -5, 14, 2, 8],
[ 5, 8, 1, 4, -3, 3, 10],
[11, 3, 5, 1, 9, 2, 5],
[ 6, 4, 12, 6, 1, 15, 4],
[ 8, 2, 0, 1, -2, 3, 5]])
In [41]: out_row
Out[41]:
array([[0, 0, 0, 0, 1, 0, 1],
[0, 1, 0, 0, 0, 0, 1],
[1, 0, 0, 0, 1, 0, 0],
[0, 0, 1, 0, 0, 1, 0],
[1, 0, 0, 0, 0, 0, 1]])
In [42]: out_col
Out[42]:
array([[0, 1, 0, 0, 1, 0, 1],
[0, 1, 0, 1, 0, 1, 1],
[1, 0, 1, 0, 1, 0, 0],
[0, 0, 1, 1, 0, 1, 0],
[1, 0, 0, 0, 0, 0, 0]])
Alternatively, if you are into compact codes, we can skip the initialization and use broadcasting to get the outputs from idx_row and idx_col directly, like so -
out_row = (idx_row[...,None] == np.arange(a.shape[1])).any(1).astype(int)
out_col = (idx_col[...,None] == np.arange(a.shape[0])).any(0).astype(int).T

Categories