Having a bit of writing out the code.
For example, if I have an array of:
a = ([0, 0, 1, 2], [0, 1, 1, 0], [0, 0, 1, 0], [1, 0, 1, 3], [0, 1, 1, 3])
if I want to add first element of each item,
as in to return a list of 0 + 0 + 0 + 1 + 0, 0 + 1 + 0, 0 + 0 ...
I wrote the code:
def test(lst):
sum = 0
test_lst = []
i = 0
while i in range(0, 4):
for j in range(0, len(lst)):
sum += lst[j][i]
test_lst.append(sum)
i += 1
return test_lst
I get index size error.
How can I go about this?
sum(zip(*a)[0])
zip is a function that takes any number of n-length sequences and returns n tuples (among other things). The first of these tuples has the elements that came first in the tuples passed to zip. sum adds them together.
EDIT:
In Python 3, the above doesn't work. Use:
sum(next(zip(*a)))
instead. For all such sums,
map(sum, zip(*a))
a = ([0, 0, 1, 2], [0, 1, 1, 0], [0, 0, 1, 0], [1, 0, 1, 3], [0, 1, 1, 3])
Try using list comprehensions:
sum([item[0] for item in a])
The line above takes the first element of each list in the tuple, then puts it into a temporary list. We then call sum on that temporary list, which yields the answer.
Related
I have a list:
hash_table = [1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1]
I want to change this to:
result = [[0, 0], [1, 2], [4, 5]]
How to generate:
array: [1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1]
map: 0 0.5 1.0 1.5 2.0 2.5 3.0 3.5 4.0 4.5 5.0
# start to end, generate the result like `[int(start), int(end)]`
combine them:[[0, 0], [1, 2], [4, 5]]
0 and 1 wouldn't appear in pairs. So the numbers in result must be an integer.
What I have tried:
hash_table = [1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1]
output = [[]]
for pre, next_ in zip(hash_table, hash_table[1:]):
output[-1].append(pre)
if {next_, pre} == {0, 1}:
output.append([])
output[-1].append(hash_table[-1])
# the output is [[1], [0], [1, 1, 1], [0, 0, 0], [1, 1, 1]]
start = index = 0
result = []
while index < len(output):
# output[index]
if output[0] != 0:
res.append([start, math.ceil(len(output[index]))])
# I don't know how to handle the list "output".
# I couldn't know it. My mind has gone blank
start += len(output[index])/2
Any good ideas? I thought I made it too complicated.
You can use itertools.groupby to group the 0s and 1s:
import itertools
hash_table = [1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1]
result = []
cur_ind = 0
for (val, vals) in itertools.groupby(hash_table):
vals = list(vals) # itertools doesn't make it a list by default
old_ind = cur_ind
cur_ind += len(vals)
if val == 0:
continue
result.append([old_ind // 2, (cur_ind - 1) // 2])
print(result)
Essentially, itertools.groupby will give an iterator of [(1, [1]), (0, [0]), (1, [1, 1, 1]), (0, [0, 0, 0]), (1, [1, 1, 1])] (more or less). We can iterate through this iterator and keep track if the index we're on by adding the length of the sublist to the current index. If the value is 1, then we have a run of ones so we append it to the results. The old_ind // 2 is integer division and is equivalent to int(old_ind / 2).
You could use groupby from itertools library:
import itertools
hash_table = [1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1]
s = "".join(map(str, hash_table)) # s = "10111000111"
gs = [(i, list(g)) for i, g in itertools.groupby(s)]
idx, result = 0, []
for i, g in gs: # i can be '1' or '0' (i.e, if the group consist in 1's or 0's)
if i == '1':
result.append([idx/2, (idx + len(g) - 1)/2])
idx += len(g)
return result
This is the input and the output should be as shown. This is what i have tried:
input a = [1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0]
output a = [[1,1,0],[1,1,1,0],[1,0],[1,1,0]]
def new_list(x):
new = []
for item in range(length(x)):
if x[item]== 0:
new.append(x[:item+1])
return new
First extract to inides of 0 elements. Then use it to slice the original list:
a = [1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0]
def new_list(x):
indices = [i for i,e in enumerate(x) if e == 0] # indices of zeros
indices = [0] + [i+1 for i in indices] # 0 is 1st slice, +1 is for slicing (1 higher than index)
ii1,ii2 = indices[:-1],indices[1:] # first and second index for each sub-list
return [x[i1:i2] for i1,i2 in zip(ii1,ii2)]
print(new_list(a))
Few modifications in your function to get your required output.
def new_list(x):
new = []
slice_idx = 0
for index, val in enumerate(x):
if val == 0:
new.append(x[slice_idx:index+1])
slice_idx = index +1
return new
Input: a = [1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0]
new_list(a)
[[1, 1, 0], [1, 1, 1, 0], [1, 0], [1, 1, 0]]
I am implementing a homomorphic encryption algorithm, and need to convert matrix like this
[[3 1 3]
[3 2 3]
[0 1 0]]
which splits a vector of integers ≤q into a log(q,2) longer vector of the bits of the integers,like:
[[0 1 1 0 0 1 0 1 1]
[0 1 1 0 1 0 0 1 1]
[0 0 0 0 0 1 0 0 0]]
Then it can be calculated as a normal matrix, and the final result can be converted from the binary to integer form.
I used some algorithms in numpy that convert matrix elements to binary, but I didn't achieve what I wanted.
You can do it with np.unpackbits.
>>> matrix = np.array([3,1,3,3,2,3,0,1,0],'uint8').reshape(3,-1)
>>> matrix
array([[3, 1, 3],
[3, 2, 3],
[0, 1, 0]], dtype=uint8)
>>> np.unpackbits(matrix.reshape(3,-1,1),2)[:,:,-3:].reshape(3,-1)
array([[0, 1, 1, 0, 0, 1, 0, 1, 1],
[0, 1, 1, 0, 1, 0, 0, 1, 1],
[0, 0, 0, 0, 0, 1, 0, 0, 0]], dtype=uint8)
Unpackbits will unpack your ints into 8 bits, but since you only seem to be interested in the 3 least significant bits, we unpack into a new axis, and use slicing [:,:,-3:] to strip out the padding zeros.
Here is one way of doing it:
import itertools
def expand_to_binary(my_list, q):
my_list = [list(('{0:0' + str(q) + 'b}').format(elem)) for elem in my_list]
my_list = [list(map(int, elem)) for elem in my_list]
my_list = list(itertools.chain(*my_list))
return my_list
x = [[3, 1, 3], [3, 2, 3], [0, 1, 0]]
x = [expand_to_binary(elem, 3) for elem in x]
q is the number of bits in every binary number. Although this is only the forward pass. Implementing the reverse part shouldn't be too difficult.
And this would be one way of implementing the reverse:
def decode_binary_to_int(my_list, q):
my_list = [list(map(str, my_list[i: i+q])) for i in range(0, len(my_list), q)]
my_list = [''.join(elem) for elem in my_list]
my_list = [int(elem, 2) for elem in my_list]
return my_list
x = [[0, 1, 1, 0, 0, 1, 0, 1, 1], [0, 1, 1, 0, 1, 0, 0, 1, 1], [0, 0, 0, 0, 0, 1, 0, 0, 0]]
x = [decode_binary_to_int[elem] for elem in x]
Although this code works i should say it's probably not the most fastest way of implementing what you want, i just tried to provide an example for what you required.
I have a long list (several hundred thousand items) of numbers and I want to create a new list of equal size to find out the places where there are consecutive repetitions of numbers. The new list will have 0 and 1 values, such that for consecutive repeated indexes the new list will have 1 and for remaining indexes it will have 0 value.
If there is something as a pandas column that can be helpful as well.
Sample given list and resultant array. List can have float values also.
given_array = [1, 2, 3, 5, 5, 5, 5, 0, -2, -4, -6, -8, 9, 9, 9]
result_array = [0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1]
I have given a small working example of my code below.
import itertools
def list_from_count(list_item):
"""
Function takes an iterator and based on the length of the item
returns 1 if length is 1 or list of 0 for length greater than 1
"""
if len(list(list_item[1])) == 1:
return 1
else:
return [0] * len(list(list_item[1]))
r0 = list(range(1,4))
r1 = [5]*4
r2 = list(range(0,-10,-2))
r3 = [9]*3
r = r0 + r1 + r2 + r3
gri = itertools.groupby(r)
res = list(map(list_from_count,gri))
print ("Result",'\n',res)
Result
[1, 1, 1, [], 1, 1, 1, 1, 1, []]
Thanks in advance!
You can use itertools.groupby and output repeated 1s if the length of a group is greater than 1:
from itertools import groupby
result_array = []
for _, g in groupby(given_array):
size = sum(1 for i in g)
if size == 1:
result_array.append(0)
else:
result_array.extend([1] * size)
or with a list comprehension:
result_array = [i for _, g in groupby(given_array) for s in (sum(1 for i in g),) for i in ([0] if s == 1 else [1] * s)]
result_array becomes:
[0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1]
You're using len(list(list_item[1])) twice. The first time you use it, it processes all the items in the iterator. When you call it the second time, the iterator is all used up, so it returns 0, that's why you get a zero-element list.
You need to save the length in a variable the first time:
def list_from_count(list_item):
l = len(list(list_item[1]))
if l == 1:
return [0]
else:
return [1] * l
You also need to return a list consistently from this function, then you can concatenate all the results, so you don't get a mix of numbers and sublists.
res = []
for el in gri:
res += list_from_count(el)
print(res)
This situation is more akin to a run length encoding problem. Consider more_itertools.run_length:
Given
import more_itertools as mit
iterable = [1, 2, 3, 5, 5, 5, 5, 0, -2, -3, -6, -8, 9, 9, 9]
Code
result = [[0] if n == 1 else [1] * n for _, n in mit.run_length.encode(iterable)]
result
# [[0], [0], [0], [1, 1, 1, 1], [0], [0], [0], [0], [0], [1, 1, 1]]
Now simply flatten the sub-lists (however you wish) into one list:
list(mit.flatten(result))
# [0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1]
Details
mit.run_length.encode compresses an iterable by yielding tuples of (value, # of repititions), e.g.:
list(mit.run_length.encode("abaabbba"))
# [('a', 1), ('b', 1), ('a', 2), ('b', 3), ('a', 1)]
Our comprehension ignores the value, uses repetitions n and creates sub-lists of [0] and [1] * n.
Note: more_itertools is a third-party package. Install via > pip install more_itertools.
Use the PANDAS shift operator to create a vector shifted 1 element. Compare that to the original. This will give you a vector of True/False values, showing where an element matched the previous one. Run a linear search down that list to extend one element at the front: change [False, True] to [True, True]. Convert to int, and you have the list you specified.
I have a problem with correct initialization within a list
import random
a = [random.randint(0,1) for x in range(10)] # getting random 0 and 1
b = a[:] # copying 'a' list for purpose of analysis
for x,y in enumerate(b): # adding + 1 where value is 1
if y != 0:
b[x] += b[x-1]
print(a) # > [1, 0, 0, 1, 1, 1, 0, 0, 1, 1]
print(b) # > [2, 0, 0, 1, 2, 3, 0, 0, 1, 2]
# wanted # > [1, 0, 0, 1, 2, 3, 0, 0, 1, 2]
from a[1:] everything is ok. Python does correct initialization, however if a[0] == 1 and a[9] == 1, Python ofcourse takes a[9] as a start value in my case.
I am just asking if there is any pythonic way to solve this > explaining python to just start initialization from 0 at a[0] and passing a[9] as first value.
Thanks
You can skip the first value rather easily:
for x,y in enumerate(b[1:]): # adding + 1 where value is 1
if y != 0:
b[x + 1] += b[x]
b[1:] just skips the first value from the list for the enumeration. This way the first number is untouched. But because the indexes in x are now all one too low, we have to add one in both cases, turning x into x + 1 and x - 1 into x. This way we access the right index.
Using your test list, it produced the following output:
[1, 0, 0, 1, 1, 1, 0, 0, 1, 1] # original list
[1, 0, 0, 1, 2, 3, 0, 0, 1, 2] # processed list