how do you replace only a certain number of items in a list randomly? - python-3.x

board = []
for x in range(0,8):
board.append(["0"] * 8)
def print_board(board):
for row in board:
print(" ".join(row))
this code creates a grid of zeros but I wish to replace 5 of them with ones and another five with twos
does anyone know a way to do this?

If you want to randomly set some coordinates with "1" and "2", you can do it like this:
import random
board = []
for x in range(0, 8):
board.append(["0"] * 8)
def print_board(board):
for row in board:
print(" ".join(row))
def generate_coordinates(x, y, k):
coordinates = [(i, j) for i in range(x) for j in range(y)]
random.shuffle(coordinates)
return coordinates[:k]
coo = generate_coordinates(8, 8, 10)
ones = coo[:5]
twos = coo[5:]
for i, j in ones:
board[i][j] = "1"
for i, j in twos:
board[i][j] = "2"
print_board(board)
Output
0 1 0 0 0 0 0 0
0 1 0 0 0 0 0 0
0 0 0 0 0 1 0 0
0 0 0 0 0 0 0 0
0 0 2 0 0 0 0 0
1 0 0 0 2 0 0 0
0 0 0 0 2 0 0 2
2 0 0 0 0 0 0 1
Notes:
The code above generates a random sample each time so the output will be different each time (to generate the same use random.seed(42), you can change 42 for any number you want.
The function generate_coordinates receives x (number of rows), y (number of columns) and k (the number of coordinates to pick). It generates a sequence of coordinates of x*y, shuffles it and picks the k first.
In your specific case x = 8, y = 8 and k = 10 (5 for the ones and 5 for the twos)
Finally, this picks the positions for the ones and twos and changes the values:
ones = coo[:5]
twos = coo[5:]
for i, j in ones:
board[i][j] = "1"
for i, j in twos:
board[i][j] = "2"

Related

How to append strings from a loop to a single output line?

So I am trying to make a text-based game of connect-4 for the purposes of better understanding Python and how it actually works.
Short version
How can I append printed text from every run-through of a while loop to a print output that exists just before the while loop
Out of the two methods seen below (The work in progress and the current successfully working one) which is a better practice of executing the desired output?
Long version
I am trying to use a looping system to print out an array in an evenly spaced and aesthetically pleasing format after every turn is taken, so users have clear feedback of what the current board looks like before the next turn is taken.
To do this I want to be able to have lines of code that are as small as possible for making it easier to read the code itself. Although this might not be the best practice for executing this scenario I want to understand this way of coding better so I could apply it to future projects if need be.
In terms of the actual execution, I am trying to use a while loop to append 7 positions of an array one after another in the same output line for array positions that are in the same row. after this, I want to print the next row on the line below the previous one as seen in the code below "Desired output".
Thank you in advance for your answers, suggestions and comments.
Work in progress
import numpy as np
ARRAY = np.zeros(shape=(6, 7), dtype = 'int8')
# In reality I will be using an empty array that gradually gets populated
# Zeros are used for ease of asking the question
def Display_board():
i = 0
while i < 7:
j = 0
print(" ", end = " ")
while j < 8:
print(str(ARRAY[i][j]))
j += 1
i += 1
work in progress output
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
# It goes on but didn't include as it would take up unnessary space in the question
If I change the line that prints the array to as follows I get another undesired output
print(str(ARRAY[i][j]), end = " ")
#output
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
Current working method - Gives desired output
def Display_board():
for i in range(6):
print(" " + str(ARRAY[i][0]) + " " + str(ARRAY[i][1]) + " " + str(ARRAY[i][2]) \
+ " " + str(ARRAY[i][3]) + " " + str(ARRAY[i][4]) + " " + str(ARRAY[i][5])\
+ " " + str(ARRAY[i][6]))
Desired output
0 0 0 0 0 0 0
0 0 0 0 0 0 0
0 0 0 0 0 0 0
0 0 0 0 0 0 0
0 0 0 0 0 0 0
0 0 0 0 0 0 0
The simple fix is to use end=' ' on the print inside the while loop on j and then add a print() after it:
def Display_board():
i = 0
while i < 6:
j = 0
print(" ", end = " ")
while j < 7:
print(str(ARRAY[i][j]), end=" ")
j += 1
print()
i += 1
Output:
0 0 0 0 0 0 0
0 0 0 0 0 0 0
0 0 0 0 0 0 0
0 0 0 0 0 0 0
0 0 0 0 0 0 0
0 0 0 0 0 0 0
You can also use a nested list comprehension with join to achieve the output in one line:
def Display_board():
print('\n'.join(' '.join([' '] + [str(ARRAY[i][j]) for j in range(7)]) for i in range(6)))
came up with two functions
first one:
def display_board1(board):
m, n = board.shape
for i in range(m):
for j in range(n):
print(board[i][j], end= ' ')
print()
return 1
second one:
def display_board2(board):
s = board.__str__()
s = s.replace('[', ']')
s = s.replace(']', '')
s = ' ' + s
print(s)
return 1
the return 1 statements are just for plotting, delete them if you dont want them
here's their performance with respect to input size
display_board2() is faster and more stable
import perfplot
bench = perfplot.bench(
setup= np.zeros,
kernels= [
display_board1,
display_board2
],
n_range= [(i, i) for i in range(10)],
)
bench.show()
FINAL FINAL EDIT:
Fixed the code to ACTUALLY use the width setting!
FINAL EDIT :)
If the numbers can be greater than 9 you can use the wonderful python f-string formatting option:
ARRAY = [
[1, 2, 3, 4, 5],
[10, 20, 3, 4, 5],
[1, 2, 30, 4, 5],
[1, 2, 3, 4, 500],
]
width = 3
for row in ARRAY:
print(" ".join(f'{x:>{width}}' for x in row))
which produces:
1 2 3 4 5
10 20 3 4 5
1 2 30 4 5
1 2 3 4 500
EDIT:
This, while less intuitive is shorter and arguably more pythonic:
for row in ARRAY:
print(" ".join(map(str, row)))
This will work for any ARRAY:
ARRAY = [
[1, 2, 3, 4, 5],
[1, 2, 3, 4, 5],
[1, 2, 3, 4, 5],
[1, 2, 3, 4, 5],
]
for row in ARRAY:
for n in row:
print(n, end = " ")
print()
poduces:
1 2 3 4 5
1 2 3 4 5
1 2 3 4 5
1 2 3 4 5
Edited to remove "" in print("")

Set to 0 x% of non zero values in numpy 2d array

I tried different ways but it seems impossible for me to do it efficiently without looping through.
Input is an array y and a percentage x.
e.g. input is
y=np.random.binomial(1,1,[10,10])
x=0.5
output
[[0 0 0 0 1 1 1 1 0 1]
[1 0 1 0 0 1 0 1 0 1]
[1 0 1 1 1 1 0 0 0 1]
[0 1 0 1 1 0 1 0 1 1]
[0 1 1 0 0 1 1 1 0 0]
[0 0 1 1 1 0 1 1 0 1]
[0 1 0 0 0 0 1 0 1 1]
[0 0 0 1 1 1 1 1 0 0]
[0 1 1 1 1 0 0 1 0 0]
[1 0 1 0 1 0 0 0 0 0]]
Here's one based on masking -
def set_nonzeros_to_zeros(a, setz_ratio):
nz_mask = a!=0
nz_count = nz_mask.sum()
z_set_count = int(np.round(setz_ratio*nz_count))
idx = np.random.choice(nz_count,z_set_count,replace=False)
mask0 = np.ones(nz_count,dtype=bool)
mask0.flat[idx] = 0
nz_mask[nz_mask] = mask0
a[~nz_mask] = 0
return a
We are skipping the generation all the indices with np.argwhere/np.nonzero in favor of a masking based one to focus on performance.
Sample run -
In [154]: np.random.seed(0)
...: a = np.random.randint(0,3,(5000,5000))
# number of non-0s before using solution
In [155]: (a!=0).sum()
Out[155]: 16670017
In [156]: a_out = set_nonzeros_to_zeros(a, setz_ratio=0.2) #set 20% of non-0s to 0s
# number of non-0s after using solution
In [157]: (a_out!=0).sum()
Out[157]: 13336014
# Verify
In [158]: 16670017 - 0.2*16670017
Out[158]: 13336013.6
There are a few vectorized methods that might help you, depending on what you want to do:
# Flatten the 2D array and get the indices of the non-zero elements
c = y.flatten()
d = c.nonzero()[0]
# Shuffle the indices and set the first 100x % to zero
np.random.shuffle(d)
x = 0.5
c[d[:int(x*len(d))]] = 0
# reshape to the original 2D shape
y = c.reshape(y.shape)
No doubt there are some efficiency improvements to be made here.

Create a new large matrix by stacking in its diagonal K matrices

l have K (let K here be 7) distincts matrices of dimension (50,50).
I would like to create a new matrix L by filling it in diagonal with the K matrices. Hence L is of dimension (50*K,50*K).
What l have tried ?
K1=np.random.random((50,50))
N,N=K1.shape
K=7
out=np.zeros((K,N,K,N),K1.dtype)
np.einsum('ijik->ijk', out)[...] = K1
L=out.reshape(K*N, K*N) # L is of dimension (50*7,50*7)=(350,350)
Its indeed creating a new matrix L by stacking K1 seven times within its diagonal. However, l would like to stack respectively K1,K2,K3,K5,K6,K7 rather than K1 seven times.
Inputs :
K1=np.random.random((50,50))
K2=np.random.random((50,50))
K3=np.random.random((50,50))
K4=np.random.random((50,50))
K5=np.random.random((50,50))
K6=np.random.random((50,50))
K7=np.random.random((50,50))
L=np.zeros((50*7,50*7))#
Expected outputs :
L[:50,:50]=K1
L[50:100,50:100]=K2
L[100:150,100:50]=K3
L[150:200,150:200]=K4
L[200:250,200:250]=K5
L[250:300,250:300]=K6
L[300:350,300:350]=K7
You could try scipy.linalg.block_diag. If you look at the source, this function basically just loops over the given blocks the way you have written as your output. It can be used like:
K1=np.random.random((50,50))
K2=np.random.random((50,50))
K3=np.random.random((50,50))
K4=np.random.random((50,50))
K5=np.random.random((50,50))
K6=np.random.random((50,50))
K7=np.random.random((50,50))
L=sp.linalg.block_diag(K1,K2,K3,K4,K5,K6,K7)
If you have your K as a ndarray of shape (7,50,50) you can unpack it directly like:
K=np.random.random((7,50,50))
L=sp.linalg.block_diag(*K)
If you don't want to import scipy, you can always just write a simple loop to do what you have written for the expected output.
Here is a way to do that with NumPy:
import numpy as np
def put_in_diagonals(a):
n, rows, cols = a.shape
b = np.zeros((n * rows, n * cols), dtype=a.dtype)
a2 = a.reshape(-1, cols)
ii, jj = np.indices(a2.shape)
jj += (ii // rows) * cols
b[ii, jj] = a2
return b
# Test
a = np.arange(24).reshape(4, 2, 3)
print(put_in_diagonals(a))
Output:
[[ 0 1 2 0 0 0 0 0 0 0 0 0]
[ 3 4 5 0 0 0 0 0 0 0 0 0]
[ 0 0 0 6 7 8 0 0 0 0 0 0]
[ 0 0 0 9 10 11 0 0 0 0 0 0]
[ 0 0 0 0 0 0 12 13 14 0 0 0]
[ 0 0 0 0 0 0 15 16 17 0 0 0]
[ 0 0 0 0 0 0 0 0 0 18 19 20]
[ 0 0 0 0 0 0 0 0 0 21 22 23]]

Change one element in a nested list

I have this code to make a game board and change one of the elements
def firstBoard():
rows = int(input("Please enter the number of rows: "))
col = int(input("Please enter the number of columns: "))
myList = [[0]*col]*rows
return myList
def getInput(myList):
rows = int(input("Enter the row or 'q': "))
col = int(input("enter the column: "))
myList[rows-1][col-1] = "X"
return myList
def printBoard(myList):
for n in myList:
for s in n:
print(s, end= " ")
print()
def main():
myList = firstBoard()
myList = getInput(myList)
print(printBoard(myList))
main()
and I want the output for the game board:
Please enter the number of rows: 5
Please enter the number of columns: 5
Enter the row or 'q': 1
Enter the column: 1
X 0 0 0 0
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
but instead I'm getting:
X 0 0 0 0
X 0 0 0 0
X 0 0 0 0
X 0 0 0 0
X 0 0 0 0
None
Any idea how to fix this and get rid of the "None at the bottom".
Posting comments as an answer...
In the fourth line do instead:
myList = [[0]*col for i in range(rows)]
The behaviour you were seeing was due to the fact that you were creating the same list on each row.
And since you've already got code to print the board in printBoard(...) remove the final call to print(), e.g.: the final line in main() should be just:
printBoard(myList)

Count the number of overlapping substrings within a string

example:
s <- "aaabaabaa"
p <- "aa"
I want to return 4, not 3 (i.e. counting the number of "aa" instances in the initial "aaa" as 2, not 1).
Is there any package to solve it? Or is there any way to count in R?
I believe that
find_overlaps <- function(p,s) {
gg <- gregexpr(paste0("(?=",p,")"),s,perl=TRUE)[[1]]
if (length(gg)==1 && gg==-1) 0 else length(gg)
}
find_overlaps("aa","aaabaabaa") ## 4
find_overlaps("not_there","aaabaabaa") ## 0
find_overlaps("aa","aaaaaaaa") ## 7
will do what you want, which would be more clearly expressed as "finding the number of overlapping substrings within a string".
This a minor variation on Finding the indexes of multiple/overlapping matching substrings
substring might be useful here, by taking every successive pair of characters.
( ss <- sapply(2:nchar(s), function(i) substring(s, i-1, i)) )
## [1] "aa" "aa" "ab" "ba" "aa" "ab" "ba" "aa"
sum(ss %in% p)
## [1] 4
I needed the answer to a related more-general question. Here is what I came up with generalizing Ben Bolker's solution:
my.data <- read.table(text = '
my.string my.cov
1.2... 1
.21111 2
..2122 3
...211 2
112111 4
212222 1
', header = TRUE, stringsAsFactors = FALSE)
desired.result.2ch <- read.table(text = '
my.string my.cov n.11 n.12 n.21 n.22
1.2... 1 0 0 0 0
.21111 2 3 0 1 0
..2122 3 0 1 1 1
...211 2 1 0 1 0
112111 4 3 1 1 0
212222 1 0 1 1 3
', header = TRUE, stringsAsFactors = FALSE)
desired.result.3ch <- read.table(text = '
my.string my.cov n.111 n.112 n.121 n.122 n.222 n.221 n.212 n.211
1.2... 1 0 0 0 0 0 0 0 0
.21111 2 2 0 0 0 0 0 0 1
..2122 3 0 0 0 1 0 0 1 0
...211 2 0 0 0 0 0 0 0 1
112111 4 1 1 1 0 0 0 0 1
212222 1 0 0 0 1 2 0 1 0
', header = TRUE, stringsAsFactors = FALSE)
find_overlaps <- function(s, my.cov, p) {
gg <- gregexpr(paste0("(?=",p,")"),s,perl=TRUE)[[1]]
if (length(gg)==1 && gg==-1) 0 else length(gg)
}
p <- c('11', '12', '21', '22', '111', '112', '121', '122', '222', '221', '212', '211')
my.output <- matrix(0, ncol = (nrow(my.data)+1), nrow = length(p))
for(i in seq(1,length(p))) {
my.data$p <- p[i]
my.output[i,1] <- p[i]
my.output[i,(2:(nrow(my.data)+1))] <-apply(my.data, 1, function(x) find_overlaps(x[1], x[2], x[3]))
apply(my.data, 1, function(x) find_overlaps(x[1], x[2], x[3]))
}
my.output
desired.result.2ch
desired.result.3ch
pre.final.output <- matrix(t(my.output[,2:7]), ncol=length(p), nrow=nrow(my.data))
final.output <- data.frame(my.data[,1:2], t(apply(pre.final.output, 1, as.numeric)))
colnames(final.output) <- c(colnames(my.data[,1:2]), paste0('x', p))
final.output
# my.string my.cov x11 x12 x21 x22 x111 x112 x121 x122 x222 x221 x212 x211
#1 1.2... 1 0 0 0 0 0 0 0 0 0 0 0 0
#2 .21111 2 3 0 1 0 2 0 0 0 0 0 0 1
#3 ..2122 3 0 1 1 1 0 0 0 1 0 0 1 0
#4 ...211 2 1 0 1 0 0 0 0 0 0 0 0 1
#5 112111 4 3 1 1 0 1 1 1 0 0 0 0 1
#6 212222 1 0 1 1 3 0 0 0 1 2 0 1 0
A tidy, and I think more readable solution is
library(tidyverse)
PatternCount <- function(text, pattern) {
#Generate all sliding substrings
map(seq_len(nchar(text) - nchar(pattern) + 1),
function(x) str_sub(text, x, x + nchar(pattern) - 1)) %>%
#Test them against the pattern
map_lgl(function(x) x == pattern) %>%
#Count the number of matches
sum
}
PatternCount("aaabaabaa", "aa")
# 4

Resources