Let's say I got a dictionary defined this way: d ={(0, 1): 1, (1, 0): 4, (1, 3): 7, (2, 3): 11}
I want to convert it to a list in a way that each key represents that index of the value within the nested list and index of the nested list itself in the list. Each nested list has 4 items, indices with no defined values are set to 0.
I suck at describing. Here's what I want my function to return: lst = [[0,1,0,0], [4,0,0,7], [0,0,0,11]]. Here's my unfinished, non working code:
def convert):
lst = []
for i in range(len(d)):
lst += [[0,0,0,0]] # adding the zeros first.
for i in d:
for j in range(4):
lst[j] = list(i[j]) # and then the others.
How about:
for (x,y), value in d.items():
list[x][y] = value
Here is the entire function, which also creates the correct list size automatically
def convert(d):
# Figure out how big x and y can get
max_x = max([coord[0] for coord in d.keys()])
max_y = max([coord[1] for coord in d.keys()])
# Create a 2D array with the given dimensions
list = [[0] * (max_y + 1) for ix in range(max_x + 1)]
# Assign values
for (x,y), value in d.items():
list[x][y] = value
return list
if __name__ == "__main__":
d ={(0, 1): 1, (1, 0): 4, (1, 3): 7, (2, 3): 11}
print(convert(d))
# Input
example_d = {(0, 1): 1, (1, 0): 4, (1, 3): 7, (2, 3): 11}
def get_list(d):
# Figure out the required lengths by looking at the highest indices
max_list_idx = max(x for (x, _), _ in d.items())
max_sublist_idx = max(y for (_, y), _ in d.items())
# Create an empty list with the max sizes
t = [[0] * (max_sublist_idx + 1) for _ in range(max_list_idx + 1)]
# Fill out the empty list according to the input
for (x, y), value in d.items():
t[x][y] = value
return t
print(get_list(example_d))
# Output: [[0, 1, 0, 0], [4, 0, 0, 7], [0, 0, 0, 11]]
You can try this.
max_x=max(d,key=lambda x:x[0])[0] # For finding max number of rows
# 2
max_y=max(d,key=lambda x:x[1])[1] # For finding max of columns
# 3
new_list=[[0]*(max_y+1) for _ in range(max_x+1)] # Creating a list with max_x+1 rows and max_y+1 columns filled with zeros
# [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]
for (x,y),v in d:
new_list[x][y]=v
new_list
# [[0, 1, 0, 0], [4, 0, 0, 7], [0, 0, 0, 11]]
You can use a list comprehension:
d ={(0, 1): 1, (1, 0): 4, (1, 3): 7, (2, 3): 11}
m_x, m_y = map(max, zip(*d))
m = [[d.get((b, a), 0) for a in range(m_y+1)] for b in range(m_x+1)]
Ouptut:
[[0, 1, 0, 0], [4, 0, 0, 7], [0, 0, 0, 11]]
Related
I have an n x k binary numpy array, I am trying to find an efficient way to find the number of pairs of ones that belong to some column[j] but not to any higher column, in this case higher means in increasing index value.
For example in the array:
array([[1, 1, 1, 0, 1, 0],
[1, 0, 1, 1, 1, 0],
[1, 0, 1, 0, 0, 0],
[1, 1, 1, 1, 1, 0],
[1, 0, 1, 0, 1, 1],
[1, 0, 1, 0, 1, 1],
[1, 1, 1, 1, 0, 0],
[1, 1, 1, 0, 1, 0]], dtype=int32)
the output should be array([ 0, 0, 11, 2, 14, 1], dtype=int32). This makes sense because we see column[2] has all ones, so any pair of ones will necessarily have a highest column in common of at least 2, because even though column[0] also has all ones, it's lower, so no pair of ones have it as their highest in common. In all cases I am considering, column[0] will always have all ones.
Here is some example code that works and I believe is something like O(n^2 k)
def hcc(i, j, k, bin_mat):
# hcc means highest common columns
# i: index i
# j: index j
# k: number of columns - 1
# bin_mat: binary matrix
for q in range(k, 0, -1):
if (bin_mat[i, q] and bin_mat[j, q]):
return q
return 0
def get_num_pairs_columns(bin_mat):
k = bin_mat.shape[1]-1
num_pairs_hcc = np.zeros(k+1, dtype=np.int32) # number of one-pairs in columns
for i in range(bin_mat.shape[0]):
for j in range(bin_mat.shape[0]):
if(i < j):
num_pairs_hcc[hcc(i, j, k, bin_mat)] += 1
return num_pairs_highest_column
Another way I've though of approaching the problem is through sets. So every column gets its own set, and the index of every row with a one gets added to such a set. So for the example above, this would look like:
set = [{0, 1, 2, 3, 4, 5, 6, 7},
{0, 3, 6, 7},
{0, 1, 2, 3, 4, 5, 6, 7},
{1, 3, 6},
{0, 1, 3, 4, 5, 7},
{4, 5}]
The idea is to find the number of pairs in set[j] that are in no higher set (it can be in a lower set, just not higher). Since, I mentioned before, all cases will have column zero with all ones, every set is a subset of set[0]. So a much worse performing code using this approach looks like this:
def generate_sets(bin_mat):
sets = []
for j in range(bin_mat.shape[1]):
column = set()
for i in range(bin_mat.shape[0]):
if bin_mat[i, j] == 1:
column.add(i)
sets.append(column)
return sets
def get_hcc_sets(bin_mat):
sets = generate_sets(bin_mat)
pairs_sets = []
num_pairs_hcc = np.zeros(len(sets), dtype=np.int32)
for subset in sets:
pairs_sets.append({p for p in itertools.combinations(sorted(subset), r = 2)})
for j in range(len(sets)-1):
intersections = [pairs_sets[j].intersection(pairs_sets[q]) for q in range(j+1, len(sets))]
num_pairs_hcc[j] = len(pairs_sets[j] - set.union(*intersections))
num_pairs_hcc[len(sets)-1]=len(pairs_sets[len(sets)-1])
return num_pairs_hcc
I haven't checked that this sets implementation always produces the same results as the previous one, but in the finitely many cases I tried, it works. However, I am 100% certain that my first implementation gives exactly the result I need.
another reference example:
input:
array([[1, 1, 0],
[1, 1, 0],
[1, 1, 0],
[1, 1, 0],
[1, 0, 1],
[1, 0, 1],
[1, 0, 1],
[1, 0, 1]], dtype=int32)
output:
array([16, 6, 6], dtype=int32)
Is there a way to beat my O(n^2 k) implementation. It seems rather brute force and like there should be something I can exploit to make this calculation faster. I always expect n to be greater than k, by a orders of magnitude in many cases. So I'd rather the k have a higher exponent than the n.
If you are going for the O(n² k) approach in python, you can do it with much shorter code using itertools and set; the code might be more efficient too.
import itertools
t = [[1, 1, 1, 0, 1, 0],
[1, 0, 1, 1, 1, 0],
[1, 0, 1, 0, 0, 0],
[1, 1, 1, 1, 1, 0],
[1, 0, 1, 0, 1, 1],
[1, 0, 1, 0, 1, 1],
[1, 1, 1, 1, 0, 0],
[1, 1, 1, 0, 1, 0]]
n,k = len(t),len(t[0])
# build set of pairs of 1 in column j
def candidates(j):
return {(i1, i2) for (i1, i2) in itertools.combinations(range(n), 2) if 1 == t[i1][j] == t[i2][j]}
# build set of pairs of 1 in higher columns
def badpairs(j):
return {(i1, i2) for (i1, i2) in itertools.combinations(range(n), 2) if any(1 == t[i1][j0] == t[i2][j0] for j0 in range(j+1, k))}
# set difference
def finalpairs(j):
return candidates(j) - badpairs(j)
# print pairs
for j in range(k):
print(j, finalpairs(j))
# 0 set()
# 1 set()
# 2 {(2, 4), (1, 2), (2, 7), (4, 6), (0, 6), (2, 3), (6, 7), (0, 2), (2, 6), (5, 6), (2, 5)}
# 3 {(1, 6), (3, 6)}
# 4 {(0, 1), (0, 7), (0, 4), (3, 4), (1, 5), (3, 7), (0, 3), (1, 4), (5, 7), (1, 7), (0, 5), (1, 3), (4, 7), (3, 5)}
# 5 {(4, 5)}
# print number of pairs
for j in range(k):
print(j, len(finalpairs(j)))
# 0 0
# 1 0
# 2 11
# 3 2
# 4 14
# 5 1
Alternate definition for badpairs:
def badpairs(j):
return set().union(*(candidates(j0) for j0 in range(j+1, k)))
Slightly different approach: avoid building badpairs
def finalpairs(j):
return {(i1, i2) for (i1, i2) in itertools.combinations(range(n), 2) if 1 == t[i1][j] == t[i2][j] and not any(1 == t[i1][j0] == t[i2][j0] for j0 in range(j+1, k))}
I have a list of 1s and 0s as follows:
lst = [1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1]
I'm looking for a method that finds all the sequences of 0s within this list and returns their indices, i.e.:
[1, 3]
[8, 9]
[13, 13]
[15, 16]
This answer shows a method of getting the longest sequence, but I can't think of a way to work from it to get all the sequences.
def f(l):
_1to0 = [ i+1 for i, (x, y) in enumerate(zip(l[:-1], l[1:])) if y == 0 and x != y ]
_0to1 = [ i for i, (x, y) in enumerate(zip(l[:-1], l[1:])) if x == 0 and x != y ]
if l[0] == 0:
_1to0.insert(0,0)
if l[-1] == 0:
_0to1.append(len(l))
return zip(_1to0, _0to1)
Detect changes 1 -> 0 (starts) and 0 -> 1 (ends)
If start with 0, add a start at indice 0
If ends with 0, add an end at the last indice
Combine starts and ends in pairs
In [1]: list(f([1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1]))
Out[1]: [(1, 3), (8, 9), (13, 13), (15, 16)]
For Python 3.8 you can modify the first answer in referenced code by using the Walrus operator
Code
from itertools import groupby
import operator
lst = [1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1]
r = [(p[1][0][0], p[1][-1][0]) for (x,y) in groupby(enumerate(lst), operator.itemgetter(1)) if (p := (x, list(y)))[0] == 0]
print(r)
Output
[(1, 3), (8, 9), (13, 13), (15, 16)]
Explanation
Adding a Walrus operator to OP code reference we have:
r = [p for (x,y) in groupby(enumerate(lst), operator.itemgetter(1)) if (p := (x, list(y)))[0] == 0]
# Outputs: [(0, [(1, 0), (2, 0), (3, 0)]), (0, [(8, 0), (9, 0)]), (0, [(13, 0)]), (0, [(15, 0), (16, 0)])]
Conditional in the list comprehension:
(p := (x, list(y)))[0] # is a check for x == 0
Need to capture the right terms in p
First p[1] for instance is:
[(1, 0), (2, 0), (3, 0)]
We want the (1, 3) which index 0 of the first and last term of the list
p[1][0][0] # index zero of first tuple -> 1
p[1][-1][0] # index zero of last tuple -> 3
So in general we have the tuple:
(p[1][0][0], p[1][-1][0])
list = [1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1]
indexes_counts = []
start_zero_index = -1
is_inside_zero_sequence = False
zero_length = 0
# Use enumerate to loop on the lists and indexes
for i, x in enumerate(list):
# If inside a zeroes sequence
if is_inside_zero_sequence:
# If current item is zero too
if 0 == x:
# Increase the zro_length counter
zero_length += 1
# Else, current element is not zero
else:
# Handle end of zeroes sequence
indexes_counts.append([start_zero_index, zero_length])
is_inside_zero_sequence = False
zero_length = 0
# If not in zeroes sequence and current number is not zero
elif 0 == x:
# Handle not zero
is_inside_zero_sequence = True
start_zero_index = i
zero_length = 1
# [[1, 3], [8, 2], [13, 1], [15, 2]]
print(indexes_counts)
I am beginner in Python. I need to solve an issue with summing up neighboring nonzero value in a list.
Say, I have list called
a = [2, 3, 0, 0, 1, 0, 3, 3, 1, 0, 0].
In a, there will be multiple instances where the elements are nonzero. For example, 2 and 3 are neigbouring so I want to sum them up so I would get 5.
Then, there another neighbouring elements which are 3, 3 and 1. This is where I face problem because i want to sum it up to 7.
But in the code that I attempted on, it still print out 4, which is the sum of a[7] and a[8]. Is there any way I can avoid this?
c =[]
for i in range(1, len(a)):
if a[i-1] != 0:
if a[i] != 0:
tot = a[i] + a[i-1]
c.append(tot)
if a[i+1] != 0:
tot = tot + a[i+1]
c.append(tot)
else:
tot = 0;
continue
You can use itertools.groupby and a comprehension:
>>> import itertools
>>> a = [2, 3, 0, 0, 1, 0, 3, 3, 1, 0, 0]
>>> [sum(v) for k, v in itertools.groupby(a, key=lambda x: x != 0) if k != 0]
[5, 1, 7]
You can also use a simple generator function:
def groups(d):
_sum = 0
for i in d:
if not i:
if _sum:
yield _sum
_sum = 0
else:
_sum += i
if _sum:
yield _sum
print(list(groups([2, 3, 0, 0, 1, 0, 3, 3, 1, 0, 0])))
Output:
[5, 1, 7]
As I understand, you want to calculate sub-sums of consecutive non-zero elements.
How about:
#!python3
from typing import List
def solve(arr: List[int]) -> List[int]:
ret = []
tmp = []
for elem in arr+[0]:
if elem != 0:
tmp.append(elem)
else:
if len(tmp):
ret.append(sum(tmp))
tmp = []
return ret
arr = [2, 3, 0, 0, 1, 0, 3, 3, 1, 0, 0]
assert(solve(arr) == [5, 1, 7])
The original list 'a' is [2, 3, 0, 0, 1, 0, 3, 3, 1, 0, 0] where the neighboring non-zero elements in list 'a' are highlighted in bold text.
a = [2, 3, 0, 0, 1, 0, 3, 3, 1, 0, 0]
def fun(a):
tot =[] # sub-list of list a
val=0
for i in a:
if i==0:
if val!=0:
tot.append(val)
val=0
else:
val+=i
return tot
print(fun(a))
# output [5, 1, 7]
I create a cartesian product using the itertools.product function:
from itertools import product
a = list(map(list, itertools.product(list(range(2)), repeat=3)))
Output:
[[0, 0, 0], [0, 0, 1], [0, 1, 0], [0, 1, 1], [1, 0, 0], [1, 0, 1], [1, 1, 0], [1, 1, 1]]
Then I get rid of mirrors reflections in the following way:
b = []
for k, v in enumerate(a):
if v[::-1] not in a[:k]:
b.append(v[::-1])
Output:
[[0, 0, 0], [1, 0, 0], [0, 1, 0], [1, 1, 0], [1, 0, 1], [1, 1, 1]]
But can I get the same effect step by step without saving all the results of itertools.product in the list? For example, with the usual approach on the for loop:
for i in list(map(list, itertools.product(list(range(2)), repeat=3))):
# blah blah blah
Because ultimately I will use large cartesian products, at least repeat = 18. And that is why I have to give up the approach on the lists. Unless there is another way to do it? I will be grateful for any tips.
import itertools
l = (list(i) for i in itertools.product(tuple(range(2)), repeat=3) if tuple(reversed(i)) >= tuple(i))
print list(l)
Output:
[[0, 0, 0], [0, 0, 1], [0, 1, 0], [0, 1, 1], [1, 0, 1], [1, 1, 1]]
Here is an idea for a recursive algorithm to generate only the necessary combinations (as opposed to generate the whole Cartesian product and discarding the unnecessary ones):
def noReflections(n, k, current=None, idx=0, symmetric=True):
# n: number of distinct elements
# k: sequences length
# current: sequence being generated
# idx: current generated index
# symmetric: true if the chosen elements up to now are symmetric
assert n >= 0 and k >= 0
if n == 0 or k == 0:
return
if idx == 0:
current = k * [0]
if idx < k // 2:
# Choose the value for current position (idx) and symmetric (idx2)
idx2 = k - idx - 1
for i in range(n):
# Value for current position
current[idx] = i
# If all previously selected values were symmetric,
# the symmetric position must have a value equal or greater
# than the current; otherwise it can take any value.
first = i if symmetric else 0
for j in range(first, n):
# Value for symmetric position
current[idx2] = j
# Recursive call
# Only keep symmetric flag if previously selected values
# and the ones selected now are symmetric.
yield from noReflections(n, k, current, idx + 1, symmetric and (i == j))
elif idx == k // 2 and (k % 2 == 1):
# In middle position of odd-length sequence
# Make one sequence with each possible value
for i in range(n):
current[idx] = i
yield tuple(current)
else:
# Even-length sequence completed
yield tuple(current)
print(list(noReflections(2, 3)))
>>> [(0, 0, 0), (0, 1, 0), (0, 0, 1), (0, 1, 1), (1, 0, 1), (1, 1, 1)]
I'm not sure this should perform better than the other answer though, because of the recursion and so on (in a couple of quick tests bot performed similarly in my machine).
Using the matrix created below:
matrix = []
for i in range(0, N):
row = []
for j in range(0, M):
row.append(i *j)
matrix.append(row)
I need get all row combinations from this matrix. For example:
[matrix[1][1], matrix[2][1], ... , matrix[m][1]]
[matrix[1][1], matrix[2][1], ... , matrix[m][2]]
[...]
[matrix[1][1], matrix[2][n], ... , matrix[m][n]]
[...]
[matrix[1][n], matrix[2][n], ... , matrix[m][n]]
Any ideas on how to do this using Python?
Starting off with a simpler way to define your matrix:
>>> M, N = 3, 4
>>> matrix = [
[i * j for j in range(M)]
for i in range(N)
]
>>> matrix
[[0, 0, 0], [0, 1, 2], [0, 2, 4], [0, 3, 6]]
Then with itertools.product:
import itertools
for row_combination in itertools.product(*matrix):
print row_combination
(0, 0, 0, 0)
(0, 0, 0, 3)
(0, 0, 0, 6)
...
(0, 2, 4, 3)
(0, 2, 4, 6)