Modify only a specific tuple in python (incomprehensible) - python

I have a list of 10,000 tuples.
Each tuple has 31 entries.
Let's say I just want to modify only the first entry of the 77th tuple.
empty_tuple=[0] * 31
lst=[empty_tuple] * 10000
lst[77][0]='salut'
It does work but all the first entries of all the 10,000 tuples are now the same.
What's going on here?
print lst[1987][0]
'salut'

[empty_tuple] * 10000 creates a list that contains 10000 references to empty_tuple, so when you modify it with lst[77][0]=..., it will be reflected across all of l.
It's a bit tricky if you really want to use tuples, as they are immutable, but you can do something like:
>>> sett = lambda t, idx, val: tuple((v if i != idx else val) for i,v in enumerate(t))
>>> l = [(0,) * 3] * 4
>>> l
[(0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0)]
>>> l[1] = sett(l[1], 2, 42)
>>> l
[(0, 0, 0), (0, 0, 42), (0, 0, 0), (0, 0, 0)]
where sett is a function that takes a tuple t, an index idx, and a value val, and returns a tuple that is identical to t in every index but idx, where it contains val.
One way to set up a list of distinct lists would be:
>>> l = map(lambda _: [0] * 3, xrange(4))
>>> l[0][1] = 2
>>> l
[[0, 2, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]]

Related

remove some elements from a matrix according to the indices

I have a matrix:
a = ([[1, 0, 0, 0],
[0, 0, 1, 1],
[0, 1, 0, 1]
[1, 0, 0, 1]])
and I want to print the 0s in the matrix but not all of the 0s. I only want to keep the 0s in every row with the smallest index and remove all subsequent zeros in the row.
For instance, in the first row of this matrix, the second element (a[0][1]) should be kept and the rest of elements in the first row should be deleted since they are all zeros.
I used pop() for 2D array but I got attribute error. And the output is not correct too. I don't know how to compare indices and select the smallest column index in every row.
This is my code:
for ix, row in enumerate(a):
for iy, i in enumerate(row):
if i==0 and (iy+ix<(iy+1)+ix) :
a[ix].pop((iy+1))
print(ix,iy)
elif i==0 and (iy+ix>(iy+1)+ix):
a[ix].pop(iy)
print(ix,iy+1)
print(a)
my expected result is the set of indices and the modified matrix a.
0 1
1 0
2 0
3 1
a=[[1,0],[0,1,1],[0,1,1],[1,0]]
Could anyone help me?
This solution only works if there is at least one zero in every row.
indices = []
for x,row in enumerate(a):
i = row.index(0)
indices.append((x,i))
a[x] = row[:i+1] + [e for e in row[i:] if e]
print(indices)
print(a)
Output
[(0, 1), (1, 0), (2, 0), (3, 1)]
[[1, 0], [0, 1, 1], [0, 1, 1], [1, 0, 1]]
Assuming there's a zero in every row, you can get its column index with
c = np.argmin(a, axis=1)
Alternatively, if the matrix can contain negative numbers, you can do
c = np.argmax(np.equal(a, 0), axis=1)
The rows are just
r = np.arange(len(a))
The result you want is then
result = np.stack((r, c), axis=-1)
If there are rows without a zero in them, you can filter the result with a mask:
mask = np.array(a)[r, c] == 0
result = result[mask, :]
Looking at your example input
a = [[1,0,0,0],[0,0,1,1],[0,1,0,1],[1,0,0,1]]
and the expected output
>>[(0, 1), (1, 0), (2, 0), (3, 1)]
you can reframe the problem as finding the index of the element in each row which has the value zero (and where more than one element exists, return the first).
By framing it this way, the solution is as simple as iterating through each row of a and retrieving the index of the value 0 (whereby only the first element will be returned by default).
Using list comprehension that would look like this:
value_to_find = 0
desired_indexes = [
row.index(value_to_find) for row in a
]
or using map that would be:
value_to_find = 0
desired_indexes = map(lambda row:row.index(value_to_find), a)
Then you could enumerate them to pair the results with the row number
enumerate(desired_indexes)
Et voila!
>>[(0, 1), (1, 0), (2, 0), (3, 1)]
The entire solution can be written in a single line like so:
answer = list(enumerate(map(lambda row:row.index(0), a)))
try this:
a = [[1, 0, 0, 0],
[0, 0, 1, 1],
[0, 1, 0, 1],
[1, 0, 0, 1]]
b = []
for i in a:
f = False
c = []
for j in i:
if (j==0 and f==False) or j != 0:
c.append(j)
if j == 0: f = True
else:
continue
b.append(c)
output:
[[1, 0], [0, 1, 1], [0, 1, 1], [1, 0, 1]]
For getting indices zero in array you can try this:
list({i : j.index(0) for i,j in enumerate(b)}.items())
# [(0, 1), (1, 0), (2, 0), (3, 1)]

More elegant way of find a range of repeating elements

I have this problem.
let l be a list containing only 0's and 1's, find all tuples that represents the start and end of a repeating sequence of 1's.
example
l=[1,1,0,0,0,1,1,1,0,1]
answer:
[(0,2),(5,8),(9,10)]
i solved the problem with the following code, but i think it is pretty messy, i would like to know if there is a cleaner way to solve this problem (maybe using map/reduce ?)
from collections import deque
def find_range(l):
pairs=deque((i,i+1) for i,e in enumerate(l) if e==1)
ans=[]
p=[0,0]
while(len(pairs)>1):
act=pairs.popleft()
nex=pairs[0]
if p==[0,0]:
p=list(act)
if act[1]==nex[0]:
p[1]=nex[1]
else:
ans.append(tuple(p))
p=[0,0]
if(len(pairs)==1):
if p==[0,0]:
ans.append(pairs.pop())
else:
ans.append((p[0],pairs.pop()[1]))
return ans
With itertools.groupby magic:
from itertools import groupby
lst = [1, 1, 0, 0, 0, 1, 1, 1, 0, 1]
indices, res = range(len(lst)), []
for k, group in groupby(indices, key=lambda i: lst[i]):
if k == 1:
group = list(group)
sl = group[0], group[-1] + 1
res.append(sl)
print(res)
The output:
[(0, 2), (5, 8), (9, 10)]
Or with a more efficient generator function:
def get_ones_coords(lst):
indices = range(len(lst))
for k, group in groupby(indices, key=lambda i: lst[i]):
if k == 1:
group = list(group)
yield group[0], group[-1] + 1
lst = [1, 1, 0, 0, 0, 1, 1, 1, 0, 1]
print(list(get_ones_coords(lst))) # [(0, 2), (5, 8), (9, 10)]
As a short bonus, here's alternative numpy approach, though sophisticated, based on discrete difference between consecutive numbers (numpy.diff) and extracting indices of non-zero items (numpy.faltnonzero):
In [137]: lst = [1,1,0,0,0,1,1,1,0,1]
In [138]: arr = np.array(lst)
In [139]: np.flatnonzero(np.diff(np.r_[0, arr, 0]) != 0).reshape(-1, 2)
Out[139]:
array([[ 0, 2],
[ 5, 8],
[ 9, 10]])
Code:
a = [[l.index(1)]]
[l[i] and len(a[-1])==2 and a.append([i]) or l[i] or len(a[-1])==1 and a[-1].append(i) for i in range(len(l))]
Output:
[[0, 2], [5, 8], [9]]
Code:
l=[1,1,0,0,0,1,1,1,0,1]
indices = [ind for ind, elem in enumerate(l) if elem == 1]
diff = [0]+[x - indices[i - 1] for i, x in enumerate(indices)][1:]
change_ind = [0]+[i for i, change in enumerate(diff) if change > 1]+[len(indices)]
split_indices = [tuple(indices[i:j]) for i,j in zip(change_ind,change_ind[1:])]
proper_tuples = [(tup[0], tup[-1]) if len(tup)>2 else tup for tup in split_indices]
print(proper_tuples)
Logic:
indices is the list of indices where l elements = 1 => [0, 1, 5, 6, 7, 9]
diff calculates the difference between the indices found above and appends a 0 at the start to keep their lengths the same => [0, 1, 4, 1, 1, 2]
change_ind indicates the locations where a split needs to happen which corresponds to where diff is greater than 1. Also append the first index and last index for later use or else you will only have the middle tuple => [0, 2, 5, 6]
split_indices creates tuples based on the range indicated in consecutive elements in change_ind (using zip which creates the combination of ranges) => [(0, 1), (5, 6, 7), (9,)]
Lastly, proper_tuples loops through the tuples create in split_indices and insures that if their length is greater than 2, then only consider the first and last elements, otherwise keep as is => [(0, 1), (5, 7), (9,)]
Output:
[(0, 1), (5, 7), (9,)]
Final Comments:
Although this does not match what OP suggested in the original question:
[(0,2),(5,8),(9,10)]
It does make more logical sense and seems to follow what OP indicated in the comments.
For example, at the start of l there are two ones - so the tuple should be (0, 1) not (0, 2) to match the proposed (start, end) notation.
Likewise at the end there is only a single one - so the tuple corresponding to this is (9,) not (9, 10)

Changing One Element in 2-Dimensional List Changes all Duplicates of List in List

The purpose of my code is to create a list of lists that contains all combinations of two switches on a binary list.
For example a two switch of [1,0,0,1,0] would be [0,0,0,1,1]
I have code written that makes the first switch. I'm trying to write code that takes input of the first switches and makes a second switch. The order of the input is based on where the first switch was made. So, the first element-list had it's first switch in its first element, the second element-list had its switch in the second element etc... I will not switch those elements since it would be undoing the first switch.
Below is what I have. The nbrhood list is the hypothetical list of lists that have already had one switch
import itertools
n = 4
nbrhood = [[1,1,1,1],[0,0,0,0]]
nbrhood2 = list(itertools.chain.from_iterable(itertools.repeat(x, (n-1)) for x in nbrhood))
print(nbrhood2)
h = 0
f = 0
for j in range(0,6):
f = j//(n-1)
if n <= h:
h = 0
if h != f and h <= n:
if nbrhood2[j][h] == 1:
nbrhood2[j][h] = 0
else:
nbrhood2[j][h] = 1
h = h + 1
elif h == f and h <= n:
if nbrhood2[j][h+1] == 1:
nbrhood2[j][h+1] = 0
else:
nbrhood2[j][h+1] = 1
h = h + 2
elif h >= n:
h = 0
print(nbrhood2[j])
This is the unexpected output:
[1, 0, 1, 1]
[1, 0, 0, 1]
[1, 0, 0, 0]
[1, 0, 0, 0]
[1, 0, 1, 0]
[1, 0, 1, 1]
I need the output to look like this:
[1, 0, 1, 1]
[1, 1, 0, 1]
[1, 1, 1, 0]
[1, 0, 0, 0]
[0, 0, 1, 0]
[0, 0, 0, 1]
I don't understand the change I make to one list in nbrhood2 also applies to the other lists. Like how the 0 put in the second position in the first list also goes to the second position in the second list.
I've been working on this for hours with no real explanation. I imagine it has something to do with how Python treats two-dimensional list changes, but I haven't been able to figure it out.
Lists in Python are always by-reference. You're literally repeating the nested values of in nbrhood when you do itertools.repeat(x, (n-1)) for x in nbrhood.
Simply copy the full list by replacing the first x with x[:]:
itertools.repeat(x[:], (n-1)) for x in nbrhood
It is quite simple to create a list with all combinations
l = list(itertools.product([0,1], repeat=4))
[[i] for i in l]
Output
[[(0, 0, 0, 0)], [(0, 0, 0, 1)], [(0, 0, 1, 0)], [(0, 0, 1, 1)], [(0, 1, 0, 0)], [(0, 1, 0, 1)], [(0, 1, 1, 0)], [(0, 1, 1, 1)], [(1, 0, 0, 0)], [(1, 0, 0, 1)], [(1, 0, 1, 0)], [(1, 0, 1, 1)], [(1, 1, 0, 0)], [(1, 1, 0, 1)], [(1, 1, 1, 0)], [(1, 1, 1, 1)]]
You could iterate across all items for the first switch and in a nested loop iterate across all subsequent items for the second switch. Having the inner loop start at the item after the index of the outer loop ensures that you don't duplicate your coverage.
def two_switch(original):
list_of_2_switches = []
for i in range(len(original)):
one_switch = original[:] #copy to retain original
one_switch[i]=1-one_switch[i]
for j in range(i+1, range(len(original))):
second_switch=one_switch[:]
second_switch[j] = 1-second_switch[j]
lit_of_2_switches.append(second_switch)
return list_of_2_switches

Count number of tails since the last head

Consider a sequence of coin tosses: 1, 0, 0, 1, 0, 1 where tail = 0 and head = 1.
The desired output is the sequence: 0, 1, 2, 0, 1, 0
Each element of the output sequence counts the number of tails since the last head.
I have tried a naive method:
def timer(seq):
if seq[0] == 1: time = [0]
if seq[0] == 0: time = [1]
for x in seq[1:]:
if x == 0: time.append(time[-1] + 1)
if x == 1: time.append(0)
return time
Question: Is there a better method?
Using NumPy:
import numpy as np
seq = np.array([1,0,0,1,0,1,0,0,0,0,1,0])
arr = np.arange(len(seq))
result = arr - np.maximum.accumulate(arr * seq)
print(result)
yields
[0 1 2 0 1 0 1 2 3 4 0 1]
Why arr - np.maximum.accumulate(arr * seq)? The desired output seemed related to a simple progression of integers:
arr = np.arange(len(seq))
So the natural question is, if seq = np.array([1, 0, 0, 1, 0, 1]) and the expected result is expected = np.array([0, 1, 2, 0, 1, 0]), then what value of x makes
arr + x = expected
Since
In [220]: expected - arr
Out[220]: array([ 0, 0, 0, -3, -3, -5])
it looks like x should be the cumulative max of arr * seq:
In [234]: arr * seq
Out[234]: array([0, 0, 0, 3, 0, 5])
In [235]: np.maximum.accumulate(arr * seq)
Out[235]: array([0, 0, 0, 3, 3, 5])
Step 1: Invert l:
In [311]: l = [1, 0, 0, 1, 0, 1]
In [312]: out = [int(not i) for i in l]; out
Out[312]: [0, 1, 1, 0, 1, 0]
Step 2: List comp; add previous value to current value if current value is 1.
In [319]: [out[0]] + [x + y if y else y for x, y in zip(out[:-1], out[1:])]
Out[319]: [0, 1, 2, 0, 1, 0]
This gets rid of windy ifs by zipping adjacent elements.
Using itertools.accumulate:
>>> a = [1, 0, 0, 1, 0, 1]
>>> b = [1 - x for x in a]
>>> list(accumulate(b, lambda total,e: total+1 if e==1 else 0))
[0, 1, 2, 0, 1, 0]
accumulate is only defined in Python 3. There's the equivalent Python code in the above documentation, though, if you want to use it in Python 2.
It's required to invert a because the first element returned by accumulate is the first list element, independently from the accumulator function:
>>> list(accumulate(a, lambda total,e: 0))
[1, 0, 0, 0, 0, 0]
The required output is an array with the same length as the input and none of the values are equal to the input. Therefore, the algorithm must be at least O(n) to form the new output array. Furthermore for this specific problem, you would also need to scan all the values for the input array. All these operations are O(n) and it will not get any more efficient. Constants may differ but your method is already in O(n) and will not go any lower.
Using reduce:
time = reduce(lambda l, r: l + [(l[-1]+1)*(not r)], seq, [0])[1:]
I try to be clear in the following code and differ from the original in using an explicit accumulator.
>>> s = [1,0,0,1,0,1,0,0,0,0,1,0]
>>> def zero_run_length_or_zero(seq):
"Return the run length of zeroes so far in the sequnece or zero"
accumulator, answer = 0, []
for item in seq:
accumulator = 0 if item == 1 else accumulator + 1
answer.append(accumulator)
return answer
>>> zero_run_length_or_zero(s)
[0, 1, 2, 0, 1, 0, 1, 2, 3, 4, 0, 1]
>>>

filling numpy array with random element from another array

I'm not sure if this is possible but here goes. Suppose I have an array:
array1 = [0,.1,.2,.3,.4,.5,.6,.7,.8,.9,1]
and now I would like to create a numpy 1D array consisting of 5 elements that are randomly drawn from array1 AND with the condition that the sum is equal to 1. Example is something like, a numpy array that looks like [.2,.2,.2,.1,.1].
currently I use the random module, and choice function that looks like this:
range1= np.array([choice(array1),choice(array1),choice(array1),choice(array1),choice(array1)])
then checking range1 to see if it meets the criteria; I'm wondering if there is faster way , something similar to
randomArray = np.random.random() instead.
Would be even better if I can store this array in some library so that if I try to generate 100 of such array, that there is no repeat but this is not necessary.
You can use numpy.random.choice if you use numpy 1.7.0+:
>>> import numpy as np
>>> array1 = np.array([0,.1,.2,.3,.4,.5,.6,.7,.8,.9,1])
>>> np.random.choice(array1, 5)
array([ 0. , 0. , 0.3, 1. , 0.3])
>>> np.random.choice(array1, 5, replace=False)
array([ 0.6, 0.8, 0.1, 0. , 0.4])
To get 5 elements that the sum is equal to 1,
generate 4 random numbers.
substract the sum of 4 numbers from 1 -> x
if x included in array1, use that as final number; or repeat
>>> import numpy as np
>>>
>>> def solve(arr, total, n):
... while True:
... xs = np.random.choice(arr, n-1)
... remain = total - xs.sum()
... if remain in arr:
... return np.append(xs, remain)
...
>>> array1 = np.array([0,.1,.2,.3,.4,.5,.6,.7,.8,.9,1])
>>> print solve(array1, 1, 5)
[ 0.1 0.3 0.4 0.2 0. ]
Another version (assume given array is sorted):
EPS = 0.0000001
def solve(arr, total, n):
while True:
xs = np.random.choice(arr, n-1)
t = xs.sum()
i = arr.searchsorted(total - t)
if abs(t + arr[i] - total) < EPS:
return np.append(xs, arr[i])
I had to do something similar a while ago.
def getRandomList(n, source):
'''
Returns a list of n elements randomly selected from source.
Selection is done without replacement.
'''
list = source
indices = range(len(source))
randIndices = []
for i in range(n):
randIndex = indices.pop(np.random.randint(0, high=len(indices)))
randIndices += [randIndex]
return [source[index] for index in randIndices]
data = [1,2,3,4,5,6,7,8,9]
randomData = getRandomList(4, data)
print randomData
If you don't care about the order of the values in the output sequences, the number of 5-value combinations of values from your list that add up to 1 is pretty small. In the specific case you proposed though, it's a bit complicated to calculate, since floating point values have rounding issues. You can more easily solve the issue if you use a set of integers (e.g. range(11))and find combinations that add up to 10. Then if you need the fractional values, just divide the values in the results by 10.
Anyway, here's a generator that yields all the possible sets that add up to a given value:
def picks(values, n, target):
if n == 1:
if target in values:
yield (target,)
return
for i, v in enumerate(values):
if v <= target:
for r in picks(values[i:], n-1, target-v):
yield (v,)+r
Here's the results for the numbers zero through ten:
>>> for r in picks(range(11), 5, 10):
print(r)
(0, 0, 0, 0, 10)
(0, 0, 0, 1, 9)
(0, 0, 0, 2, 8)
(0, 0, 0, 3, 7)
(0, 0, 0, 4, 6)
(0, 0, 0, 5, 5)
(0, 0, 1, 1, 8)
(0, 0, 1, 2, 7)
(0, 0, 1, 3, 6)
(0, 0, 1, 4, 5)
(0, 0, 2, 2, 6)
(0, 0, 2, 3, 5)
(0, 0, 2, 4, 4)
(0, 0, 3, 3, 4)
(0, 1, 1, 1, 7)
(0, 1, 1, 2, 6)
(0, 1, 1, 3, 5)
(0, 1, 1, 4, 4)
(0, 1, 2, 2, 5)
(0, 1, 2, 3, 4)
(0, 1, 3, 3, 3)
(0, 2, 2, 2, 4)
(0, 2, 2, 3, 3)
(1, 1, 1, 1, 6)
(1, 1, 1, 2, 5)
(1, 1, 1, 3, 4)
(1, 1, 2, 2, 4)
(1, 1, 2, 3, 3)
(1, 2, 2, 2, 3)
(2, 2, 2, 2, 2)
You can select one of them at random (with random.choice), or if you plan on using many of them and you don't want to repeat yourself, you can use random.shuffle, then iterate.
results = list(picks(range(11), 5, 10))
random.shuffle(results)
for r in results:
# do whatever you want with r

Categories