random flip m values from an array - python

I have an array with length n, I want to randomly choose m elements from it and flip their value. What is the most efficient way?
there are two cases, m=1 case is a special case. It can be discussed separately, and m=/=1.
My attempt is:
import numpy as np
n = 20
m = 5
#generate an array a
a = np.random.randint(0,2,n)*2-1
#random choose `m` element and flip it.
for i in np.random.randint(0,n,m):
a[m]=-a[m]
Suppose m are tens and n are hundreds.

To make sure we are not flipping the same element twice or even more times, we can create unique indices in that length range with np.random.choice using its optional replace argument set as False. Then, simply indexing into the input array and flipping in one go should give us the desired output. Thus, we would have an implementation like so -
idx = np.random.choice(n,m,replace=False)
a[idx] = -a[idx]
Faster version : For a faster version of np.random_choice, I would suggest reading up on this post that explores using np.argpartition to simulate identical behavior.

You can make a random permutation of the array indices, take the first m of them and flip their values:
a[np.random.permutation(range(len(a)))[:m]]*=-1
Using permutation validate you don't choose the same index twice.

You need to change the index of the array from m to i to actually change the value.
Result:
import numpy as np
n = 20
m = 5
#generate an array a
a = np.random.randint(0,2,n)*2-1
print(a)
#random choose `i` element and flip it.
for i in np.random.randint(0,n,m):
a[i] = -a[i]
print(a)
My output:
[ 1 1 -1 -1 1 -1 -1 1 1 -1 -1 1 -1 1 1 1 1 -1 1 -1]
[ 1 1 -1 -1 -1 -1 1 1 1 -1 -1 1 -1 -1 1 -1 1 -1 -1 -1]

Related

Split a list by certain value until only the first element remains python

I would like to count how many times I can divide an array of elements by a certain value until only one element remains
An array of 10 elements can be divided by half 3 times, but my loop only does it one time and then stops. Clearly I'm missing something..
# a function that takes an integer as input
def split_a_list(length):
# create an array of elements where the input to the function determines the length of the array
elements = list(range(1, (length +1)))
begin_index = 0
end_index = len(elements)-1
count = 0
# a value that specifies the size of remaining size of the array
divider = 2
while begin_index < end_index:
# because divider = 2 (as defined above), this divides the array in half
mid_index = math.floor(float((begin_index + end_index)/divider))
# this counts the number of times the array has been divided
count += 1
# the last index of the array is now the mid index of the previous array
end_index = mid_index - 1
# here I would like the function to be called again, but with the integer corresponding to
# the the updated end index which is the mid index of the previous array
return split_a_list(end_index), count
# An array of 10 elements can be divided in half 3 times until 1 element remains
# I would like the function to store the number of times the array can be divided in the variable count
# and return this value as output
length = 10
split_a_list(length)
This returns ((None, 1), 1) but I would like it to return 3 since an array of 10 can be divided 3 times until 1 element is left
Thanks so much in advance!
If you're only looking for an integer, just keep it simple use floor division.
length = len(your_list)
n = 0
while length > 1:
length //= 2
n += 1
The // operator (floor division) is used here to divide length into 2 (whole) parts. The result of the floor division is then assigned back to length and the operation continues until length is equal to 1.
You can see it working here:
>>> length = 10
>>> length //= 2
5
>>> length //= 2
2
>>> length //= 2
1

List of binary numbers: How many positions have a one and zero

I have a list of integers, e.g. i=[1,7,3,1,5] which I first transform to a list of the respective binary representations of length L, e.g. b=["001","111","011","001","101"] with L=3.
Now I want to compute at how many of the L positions in the binary representation there is a 1 as well as a zero 0. In my example the result would be return=2 since there is always a 1 in the third (last) position for these entries. I would be happy for any comment. I think, ideally I should do many Xor operations at the same time. However, I'm not sure how I can do this efficiently.
Edit: Thanks for the many answers!! I have to check which one is the fastest.
One observation is that if you take the AND of all numbers, and also the OR of all numbers, then the XOR of those two results will have a 1 where the condition is fulfilled.
So:
from functools import reduce
from operator import and_, or_
def count_mixed_bits(lst):
xor = reduce(and_, lst) ^ reduce(or_, lst)
return bin(xor).count("1")
count_mixed_bits([1,7,3,1,5]) # 2
There's a numpy.binary_repr method that accepts length. Unfortunately, it can't handle arrays. But you can apply a functionality of np.unravel_index instead:
def check(arr, lenght):
positions = np.array(np.unravel_index(i, (2,)*lenght))
return positions, np.sum(np.sum(positions, axis=1) != len(arr))
>>> positions, output = check(i, 3)
>>> print(positions)
>>> print(output)
[[0 1 0 0 1]
[0 1 1 0 0]
[1 1 1 1 1]]
2
Here is a solution, I suspect it is not very efficient, but it is easy to understand.
I loop over the digits and find the unique set, then I count the number of entries with a set length of two:
# create a binary list of 3 elements from input list of integers
i=[1,7,3,1,5]
b=['{0:03b}'.format(x) for x in i]
# loop over the digit position (1,2,3)
cnt=[]
for pos in range(3):
cnt.append(len(set([c[pos] for c in b])))
# cnt now contains a list of either 2(=both 1 and 0 present) or 1 (unique)
# so now we count the number of entries with "2"
result=cnt.count(2)
print (result)
answer:
2
First of all your question is tagged with numpy but your array is not a numpy array.
Here is a solution that uses numpy:
import numpy as np
def has_zeroes_and_ones_at_index(arr, index_from_right):
shifted_arr = np.right_shift(arr, index_from_right)
has_one_at_index = shifted_arr % 2 == 1
return(True in has_one_at_index and False in has_one_at_index)
arr = np.array([1, 7, 3, 1, 5])
res= has_zeroes_and_ones_at_index(arr, 1)
print(res)
Because the numbers are stored in binary we can use bit shifting to move all bits of the numbers to the right and then look at the last bit. We dont have to cast them to a binary format before.
5 (101) right shift by one -> 2 (010)
We then create a mask to see which numbers have a one in the last bit and return True when in the mask there is at least one True element and one false element.
You can use python bitwise operators for this task.
def find(aList, nn):
return sum(
filter(
lambda bb: bb > 0 ,
(
( 1 <= (sum( (aa & 1<<kk) > 0 for aa in aList)) < len(aList) )
for kk in range(nn)
)
)
)
>>> find([1,7,3,1,5],3)
2
>>> find([],3)
0
>>> find([7],3)
0
>>> find([7,1],3)
2
>>> find([7,1,7],3)
2

Find maximum value and indices of a maximum without using max built in functions

Like the title says, I'm trying to find the max value and location of the argument(s) without using any variation of the built in max functions.
I was able to piece this together for a basic np.array, but I'm having difficulty translating it into a matrix ... I think because of how it is indexed.
Here's what I have for the np.array:
a = np.array((1,2,2,3,4,3,2,1,4,3))
def argmax(x):
maximum = 0
for i in range(len(x)):
if x[i] > maximum: maximum = x[i]
pos = np.argwhere(x == maximum)[0][0]
return(print('The maximum value of the array is', maximum, 'and is located at index', pos))
argmax(a)
The maximum value of the array is 4 and is located at index 4.
I'm trying to create something similar for any size matrix without using built in max functions. Can someone help me with the function and help me understand the difference in indexing between a basic array and a matrix?
This works for 1d arrays and 2d arrays:
import numpy as np
import math
matrix = np.arange(20).reshape(4, 5)
print(matrix)
# Important
matrix = np.atleast_2d(matrix)
# set maximum to -inf
maximum = -math.inf
# Search maximum
for j in range(matrix.shape[1]):
for i in range(matrix.shape[0]):
maximum = matrix[i][j] if matrix[i][j] > maximum else maximum
# More than 1 maximum, take the first one?
pos = np.argwhere(matrix == maximum)[0]
print(
f"The maximum value of the array is: {maximum}, located at: row {pos[0]}, column {pos[1]}"
)
Outputs:
[[ 0 1 2 3 4]
[ 5 6 7 8 9]
[10 11 12 13 14]
[15 16 17 18 19]]
The maximum value of the array is: 19, located at: row 3, column 4
I'm assuming that you want to find the maxima along a given axis. Otherwise, do np.unravel_index(argmax(a.ravel()), a.shape).
First let's define a function that steps along the given dimension and keeps track of both the maxima and the indices at which they occur:
def argmax(a, axis):
# index
cur = [slice(None) for _ in range(a.ndim)]
cur[axis] = 0
# trackers
val = a[tuple(index)]
ind = np.zeros(val.shape, dtype=int)
# loop
for i in range(1, a.shape[axis]):
index[axis] = i
v = a[tuple(index)]
mask = v > val
val[mask] = v[mask]
ind[mask] = i
return ind
This returns the index along axis. If you want to get the other indices, do something like
all_indices = list(np.indices(a.shape))
all_indices[axis] = ind
all_indices = tuple(all indices)
Or alternatively,
all_indices = [slice(None) for _ range(a.ndim)]
all_indices[axis] = ind
all_indices = tuple(all indices)
This function skips a couple of corner cases, like when a.shape[axis] == 0 and a.ndim == 0, but you can easily handle them with a simple preliminary test.
You can also special-case axis=None with a recursive call as shown in the beginning of the answer.
If you want to allow multiple axes simultaneously, swap them all to the end, and reshape them into a single axis. So a hybrid of the axis=None and normal processing.
Here is a way to do it for ANY shape and dimensions array (it assumes values are non-negative since you initialize maximum with 0 and returns the first incidence of maximum only as you did in your original answer. Of course you can easily change them):
def argmax(x):
maximum = 0
for i, v in enumerate(x.reshape(-1)):
if v > maximum:
maximum = v
pos = i
print('The maximum value of the array is', maximum, 'and is located at index', np.unravel_index(pos, x.shape))
argmax(a)
Example:
a = np.random.randint(0,10,(3,4))
#[[7 6 2 6]
# [7 2 0 5]
# [4 0 8 7]]
output:
The maximum value of the array is 8 and is located at index (2, 2)

Optimization problem of connection between nodes

I have a problem that I am struggling to solve efficiently. I have 2 sets of nodes, A_1, ... A_m and B_1,... B_m. Each node from one set can be connected to a node from the other set with a given constant probability p. Then, what I want is to have the maximum links between the set A and the set B under the condition that there is one and only one node remaining between each single node of A and B.
For example if A_i is connected to B_j and B_j', then we have either to remove the link with either B_j or B_j'.
The successful link between A_i and B_j can be stored in a matrix where M_{ij} = 1 if there is a link between A_i and B_j, 0 if not.
This matrix can for example be simulated by the given code for a matrix dimension 5 and a probability 0.7 of successful link (in python):
import numpy as np
m = 5
proba = 0.7
M = np.random.choice([0, 1], size=(m, m), p=[1 - proba, proba])
This can give for example the matrix:
M =
[[0 1 0 1 0]
[1 1 1 0 1]
[1 1 1 0 0]
[0 0 0 1 0]
[1 1 1 1 1]]
What I want is to implement the transformation on this matrix that satisfies the condition of maximum one link between nodes while maximizing the number of links. This transformation convert M into M_2.
Here's the condition on M_2:
In the end, there is k<= m links between the set A and the set B as each node of A can in fine connect to only one single node (or zero) of the the set of B.
This translate in terms of matrix into a transformation from M to M_2 where M_2_{ij} is equal either to M_{ij} or 0. If M_2_{i0,j0} = 1 then M_2_{i, j0} = 0 and M'_{i0, j} = 0 for all i, j != i0, j0. It means that there is only one (or zero) non zero term per row and per column.
It is easy to remove any terms in a row if one is already equal to one. The hard part of what I want my code to do is to maximize the number of non-zero terms of the matrix M_2 while respecting the conditions on the matrix M_2.
I've managed to do painfully the "automatic reduction part" where if you find a row with only one non-zero term, you remove the non-zero terms of the associated column and conversely. I've done it recursively, until the transformed matrix stays the same (The code is at the end, and is quite complicated to understand, because I think that I've not found the elegant way to do it...)
With this code there is some improvements:
M_2' =
[[0 1 0 0 0]
[1 0 1 0 1]
[1 0 1 0 0]
[0 0 0 1 0]
[1 0 1 0 1]]
There are less non-zero terms compared to M in this matrix but still, it does not respect the conditions.
What I want is to automatically do the final step to find the matrix M_2 which by hand should look like this:
M_2 =
[[0 1 0 0 0]
[1 0 0 0 0]
[0 0 1 0 0]
[0 0 0 1 0]
[0 0 0 0 1]]
But I have no idea how to automatically and efficiently do such optimization... Anyone have ideas how to realize that?
Thank you in advance.
Paul
The code for the straightforward transformation that gave me M_2' is the following (if you have any improvement for a more pythonic or efficient way to write it, I'd be happy to see it):
def remove_straightforward_terms(M):
"""Transform the matrix M by removing a maximum of non-zero terms."""
# Index of the non-zero (=1) terms:
ind_i, ind_j = np.where(M == 1)
# Remove automatically some terms:
new_ind_i, new_ind_j = automatic_remove_straightforward(ind_i, ind_j)
# Rebuild the matrix using this terms
return build_matrix_non_zero(M, new_ind_i, new_ind_j)
def build_matrix_non_zero(M, ind_i, ind_j):
"""Rebuild the matrix using non-zero terms indices."""
M_2 = np.zeros(M.shape, dtype=int)
for ind, val in np.ndenumerate(ind_i):
M_2[ind_i[ind], ind_j[ind]] = 1
return M_2
def automatic_remove_straightforward(ind_i, ind_j):
"""Recursively remove the terms from ind_i and ind_j."""
ind_j_int_2 = []
ind_i_int_2 = []
while len(ind_i) != len(ind_i_int_2) and len(ind_j) != len(ind_j_int_2):
if len(ind_i_int_2) != 0:
# Necessary for entering the while loop...
ind_i = ind_i_int_2
ind_j = ind_j_int_2
# If there is only one non zero term for a given row, remove the other corresponding terms from the column.
ind_i_int_2, ind_j_int_2 = remove_indices_straightforward(ind_i, ind_j)
# If there is only one non zero term for a given column, remove the other corresponding terms from the row.
ind_j_int_2, ind_i_int_2 = remove_indices_straightforward(
ind_j_int_2, ind_i_int_2)
return ind_i, ind_j
def remove_indices_straightforward(ind_i, ind_j):
"""Remove the non-zero terms automatically.
Let's consider i is the column and j the row"""
unique_i, counts_i = np.unique(ind_i, return_counts=True)
# Record all the indices that will be removed (it correspond to removing non-zero terms)
remove_ind = []
for ind, val_counts in np.ndenumerate(counts_i):
if val_counts == 1:
# If a term is in ind_i is only here once (it is alone on its line).
val_i = unique_i[ind]
# We find its position in the list ind_i
index_ind_i = np.where(ind_i == val_i)[0]
# We find the correspond row of the list ind_j
val_j = ind_j[index_ind_i]
# We find the indices of all the non-zero terms share the same row.
indices_ind_j = np.where(ind_j == val_j)[0]
# We record all but the one that was found in the first place
indices_ind_j = indices_ind_j[indices_ind_j != index_ind_i]
# and put them in the remove_ind
remove_ind = np.concatenate((remove_ind, indices_ind_j))
# We remove the indices that we don't want anymore
new_ind_j = np.delete(ind_j, remove_ind)
new_ind_i = np.delete(ind_i, remove_ind)
return new_ind_i, new_ind_j
M_2 = remove_straightforward_terms(M)
EDIT:
Using the solution proposed by btilly, here is how to obtain the desired matrix:
import numpy as np
import networkx as nx
from networkx.algorithms import bipartite
m = 5
p = 0.7
def connecting(m, p):
G_1 = nx.bipartite.random_graph(m, m, p)
top_nodes = {n for n, d in G_1.nodes(data=True) if d['bipartite'] == 0}
A = nx.bipartite.hopcroft_karp_matching(G_1, top_nodes)
M = np.zeros((m, m), dtype=int)
for node_1, node_2 in A.items():
if node_1 < m:
M[node_1, node_2 - m] = 1
return M

Changing the value of some indexes of an array in python

I am trying this simple code to search in an array and replace the elements that are greater than 1 to 1:
import numpy as np
j = np.array([[1],[3],[1],[0],[9]])
for x in j:
if abs(x) > 1 :
j[x] = 1
But I get such errors:
IndexError: index 9 is out of bounds for axis 0 with size 5
If all you're doing is making all values if absolute(j[i]) is greater than 1 to 1 then numpy has this capability built in and it's so simple it can be done in one line and more efficient than any python loop:
j[np.absolute(j) > 1] = 1
To show you how this would work:
#made 3 a negitive value to prove absolute works.
j = np.array([[1],[-3],[1],[0],[9]])
j[np.absolute(j) > 1] = 1
j is now:
[[1]
[1]
[1]
[0]
[1]]
When you traverse an array in a for loop you are actually accessing the elements, not the index. After all, you are comparing x against 1. You can retrieve the index in many ways, one of the common ones is to use enumerate, like so:
import numpy as np
j = np.array([[1],[3],[1],[0],[9]])
for i,x in enumerate(j): # i is the index, x is the value
if abs(x) > 1 :
j[i] = 1
Try to change the for loop using enumerate to :
import numpy as np
j = np.array([[1],[3],[1],[0],[9]])
for i,x in enumerate(j):
if abs(x) > 1 :
j[i] = 1
as you see in your error output
IndexError: index 9 is out of bounds for axis 0 with size 5
you are trying to update a value at index 9 but your array is of size 5.
which clearly means you are not using the index of array but actually the value at index.
enumerate your array and run a loop with both index & value
for i,x in enumerate(j):
if abs(x) > 1 :
j[i] = 1
Are you trying to make a two dimensional array? You have your elements in brackets within brackets "[[1],[3],[1],[0],[9]]" .... also, you're iterating over values, not indices: x is an array value "[3]" not an index "1".
Change to:
import numpy as np
j = np.array([1,3,1,0,9])
# Keep track of index starting at 0
i = 0
for x in j:
if abs(x) > 1 :
# change value at index i
j[i] = 1
# increment index
i += 1
You may want to replace the for statement with this:
for x in range(len(j))

Categories