I have created an empty array which I want to fill.
The array is 10 by 10. I want the first row and column to display text names, which I have in a list of 9. I want the inner 9 by 9 cells to contain another matrix, which I already have filled in with the values.
Here is how I made the matrix and tried to fill in the names so far:
rows, cols = (10, 10)
array = [[0 for i in range (cols)] for j in range (rows)]
array [0][1:9] = photographs
array [1:9][0] = photographs
where photographs is my list of 9 words.
This gives me an array where the first row is as desired, but the first column is still all displaying 0.
This is what my array looks like:
[[0, 'DSC001 \n', 'DSC4587 \n', 'DSC3948 \n', 'DSC98798 \n', 'DSC44 \n', 'DSC098098d \n', 'DSC098734a-796876 \n', 'DSC8976 \n', 'DSC098707-a-b \n', 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, 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, 0, 0, 0, 0, 0, 0, 0]]
I tried to make the cell in the first row and first column display - or just a space, but got this error back:
array [0][0] = -
^
SyntaxError: invalid syntax
I have also tried to fill in my array with the values from my 9 by 9 matrix like this:
array [1:9][1:9] = matrix
But this did not work at all.
Filling in the first column should be
array[0][1:10] = photographs
In python, list slices go from the starting number to one less than the ending number, just like range
You can't use array[1:9][0] to refer to the first column.
array[1:9] is a list containing rows indexed 1 to 8 (so 2nd row to 9th row) so array[1:9][0] is just the second row. You could use a for loop to insert the column names instead like:
for row in array[1:10]:
row[0] = photographs[i]
Also, to insert a value into the first cell you want:
array[0][0] = '-'
just like how you would assign a variable.
nrows = 4
ncols = 4
# Initialize an empty list of lists.
# NB this is a list of lists, not an array. Think of the outer list as a list of rows. Each row is an inner list of 1 element per column.
array = [[0] * ncols for _ in range(ncols)]
# Note that array[n] gets the nth row. array[n][m] gets the element at (n, m).
# But to get the mth column, you need to do [array[row][m] for row in range(nrows)].
# This is reason enough to start thinking about numpy or pandas for an application list this.
headers = ["A", "B", "C"]
# Add the row headers to your 'array'
array[0][1:] = headers
# remember that array[0] gets the first row. It is a list. You can get all the elements except the first by slicing it with [1:]
# Add the column headers to your 'array'
for row_number, row in enumerate(array[1:]):
row[0] = headers[row_number]
# in this case we need a loop as we want to change the first element of each of the inner lists. A loop over array gives us a row at each iteration. row[0] is then the first column of that row.
# put - in the corner
array[0][0] = "-"
# fill the array with another list
data = [[1, 2, 3],
[4, 5, 6],
[7, 8, 9]]
# because both data and array are lists of rows, we do this row by row, skipping the first row
for data_row_number, array_row in enumerate(array[1:]):
array_row[1:] = data[data_row_number]
gives the output for array of
[['-', 'A', 'B', 'C'], ['A', 1, 2, 3], ['B', 4, 5, 6], ['C', 7, 8, 9]]
Related
I have a two dimensional list like :
data = [[0,0,0,0,0,1,0,0,0,0], [0,1,0,0,0,0,0,0,0,0]]
How can I access the index of the neighbours, where the value equals 1?
Expected output:
[[4, 5, 6], [0, 1, 2]]
For example, the indices of an array data in first row at value 1 is 5, so I need to access its left and right side neighbour indices like 4 and 6. Same way for row 2.
If I understand description well (please clarify) , maybe you can try this one. Additionally, you can check edge case where there is no 1, or no left or right .
import numpy as np
a = np.array([
[0, 0, 0, 0, 0, 1, 0, 0, 0, 0],
[0, 1, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 1, 0, 0, 0, 0, 0, 0, 0, 0]])
if __name__ == "__main__":
indices = np.where(a == 1)[1]
indices = indices.reshape(-1,1)
indices = np.concatenate([indices-1,indices,indices+1],-1)
print(indices)
One efficient solution is using FOR loops:
for i in range(2):
for j in range(10):
if a[i][j]==1:
print(str(i)+' '+str(j))
If using lists, here is a one approach which identifies the indexes of the neighbours of 1. As a caveat, this will fail with a index out of range, if the 1 value is the first of last element in the list.
Input:
data = [[0,0,0,0,0,1,0,0,0,0], [0,1,0,0,0,0,0,0,0,0]]
Example:
[[i-1, i, i+1] for sub in data for i, j in enumerate(sub) if j == 1]
Output:
[[4, 5, 6], [0, 1, 2]]
Code that works perfectly:
rows, cols = (4,2)
arr=[]
for i in range(rows):
col = []
for j in range(cols):
col.append(0)
arr.append(col)
print(arr)
Output:
[[0, 0], [0, 0], [0, 0], [0, 0]]
Alternative implementation:
rows, cols = (4,2)
arr=[]
col = []
for i in range(rows):
for j in range(cols):
col.append(0)
arr.append(col)
print(arr)
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]]
Why both codes generate different outputs just by changing place of col where an empty list is assigned to it. I have tried to get concept with trace table but got confused.
This is one of the classic Python blunders. Your entire second program contains EXACTLY two lists. There's one bound to col, and one bound to arr, and the one bound to arr contains multiple references to the first list. It does not contain 4 different lists, it contains 4 links to the SAME list.
To do what you want, you need to create a NEW empty list in each iteration, as you have done in the first example.
(Python 3.9) I'm trying to solve a percolation problem/assignment, and just to start, I'm creating an n-by-n grid. While the grid looks correct on creation, when I try to alter a specific value inside the grid (in a nested list), it's assigning that value to every sublist, rather than only the list I'm referencing with it's index.
Here's the class and relevant bits:
class Percolation:
"""Create an n-by-n grid, with all sites initially blocked."""
def __init__(self, n):
self.grid = self.create_grid(n)
def create_grid(self, n):
""" Create n-by-n grid with default 0 (blocked) values."""
grid = []
# Create x, rows.
values = [0 for _ in range(n)]
# Create y, columns.
for _ in range(n):
grid.append(values)
return grid
def show_grid(self):
for row in self.grid:
print(row)
size = 5
perc = Percolation(size)
# I chose 8 and 5 arbitrarily to test this issue so they're easy to spot.
perc.grid[0][2] = 8
perc.grid[1][1] = 5
perc.show_grid()
show_grid() is then showing me all sublists are changed.
>>>
[0, 5, 8, 0, 0]
[0, 5, 8, 0, 0]
[0, 5, 8, 0, 0]
[0, 5, 8, 0, 0]
[0, 5, 8, 0, 0]
While what I was expecting is this:
>>>
[0, 0, 8, 0, 0]
[0, 5, 0, 0, 0]
[0, 0, 0, 0, 0]
[0, 0, 0, 0, 0]
[0, 0, 0, 0, 0]
Am I making the mistake when I create the grid by appending "values" multiple times in a loop? Or am I incorrectly referencing indexes? I thought maybe at first show_grid() was accidentally printing the same row over and over, but printing self.grid directly still shows duplicate values across all sublists, regardless of n or how I assign values.
What I do see is that when I change only a value in the main list, I do get what I expect.
perc.grid[2] = [0, 0, 8, 0, 0]
perc.show_grid()
>>>
[0, 0, 0, 0, 0]
[0, 0, 0, 0, 0]
[0, 0, 8, 0, 0]
[0, 0, 0, 0, 0]
[0, 0, 0, 0, 0]
How do I properly change values in these nested lists in this case? What is actually happening when I'm setting perc.grid[0][2] = 8?
In python when you create a list and assign it to multiple places, it is the same list, not a new list with the same values. So when you create the list values and append it to grid, each nested list is actually the same list.
One way you can avoid this problem is by copying the list using an index of the entire list [:] like:
grid.append(values[:])
I want to create a grid with a variable number of rows and columns. What I have done to achieve this is this
BaseRow = []
for j in range (0, columns):
BaseRow.append(0)
Grid = []
for j in range (0, rows):
Grid.append(BaseRow)
So all seems fine until now, I print the rows in order with this piece of code
for i in range (1, rows+1):
print Grid[rows-i]
and a grid that looks something like this
[0, 0, 0]
[0, 0, 0]
[0, 0, 0]
is printed. Thing is, afterwards, I want to change a specific element. But when I do,
Grid[0][0] = 1
and print again, instead of just changing the rightmost down most 0 to a 1, the whole column changes, so it becomes
[1, 0, 0]
[1, 0, 0]
[1, 0, 0]
I suspect it sees that Grid is taking from BaseRow, so it changes BaseRow, and then the Grid takes the rows from BaseRow and just puts that value everywhere. I suspect .append might not be what I am looking for, but for all the research I have done I have not managed to find something else to use. If I understand correctly, .extend will not add it as a list but as individual numbers. What should I change, or how should I structure this?
Please excuse my limited knowledge, I just started programming in python half a week ago. Thanks for your help!
BaseRow = []
for j in range (0, columns):
BaseRow.append(0)
Grid = []
for j in range (0, rows):
Grid.append(BaseRow)
When you do this, the same instance of BaseRow is appended to Grid multiple times. So, if you change even row in Grid, the effect will be on all rows, as it is basically the same instance of list in all rows.
If you want a copy of BaseRow to be appended to Grid, use the below code:
for j in range(0, rows):
Grid.append(BaseRow[:])
You could also use list comprehension:
Grid = [[0 for j in range(0, columns)] for i in range(0, rows)]
Output for Columns = 3 and rows = 4:
[[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]]
Output after setting Grid[0][0] = 1:
[[1, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]]
If you ask me, I would any day use List comprehension because it's so clean and easy:
columns, rows = 3, 3
lst = [[0 for j in range(columns)] for i in range(rows)] # List of List with 3 Columns and 3 Rows
lst[0][0] = 1 # modifying a member
print (lst) # Print the result
# Result: [[1, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]
I personally prefer list comprehension but your code needs just little changes and it will serve you well. You append list and to that list you append elements:
matrix = []
for i in range(3):
matrix.append([])
for j in range(4):
matrix[-1].append(0)
print(matrix)
[[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]
matrix[0][0] = 1
print(matrix)
[[1, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]
Given a 3 dimensional boolean data:
np.random.seed(13)
bool_data = np.random.randint(2, size=(2,3,6))
>> bool_data
array([[[0, 0, 0, 0, 0, 0],
[0, 1, 0, 0, 1, 0],
[0, 0, 0, 0, 0, 1]],
[[1, 0, 1, 1, 0, 0],
[0, 1, 1, 1, 1, 0],
[1, 1, 1, 0, 0, 0]]])
I wish to count the number of consecutive 1's bounded by two 0's in each row (along axis=1) and return a single array with the tally. For bool_data, this would give array([1, 1, 2, 4]).
Due to the 3D structure of bool_data and the variable tallies for each row, I had to clumsily convert the tallies into nested lists, flatten them using itertools.chain, then back-convert the list into an array:
# count consecutive 1's bounded by two 0's
def count_consect_ones(input):
return np.diff(np.where(input==0)[0])-1
# run tallies across all rows in bool_data
consect_ones = []
for i in range(len(bool_data)):
for j in range(len(bool_data[i])):
res = count_consect_ones(bool_data[i, j])
consect_ones.append(list(res[res!=0]))
>> consect_ones
[[], [1, 1], [], [2], [4], []]
# combines nested lists
from itertools import chain
consect_ones_output = np.array(list(chain.from_iterable(consect_ones)))
>> consect_ones_output
array([1, 1, 2, 4])
Is there a more efficient or clever way for doing this?
consect_ones.append(list(res[res!=0]))
If you use .extend instead, the content of the sequence is appended directly. That saves the step to combine the nested lists afterwards:
consect_ones.extend(res[res!=0])
Furthermore, you could skip the indexing, and iterate over the dimensions directly:
consect_ones = []
for i in bool_data:
for j in i:
res = count_consect_ones(j)
consect_ones.extend(res[res!=0])
We could use a trick to pad the columns with zeros and then look for ramp-up and ramp-down indices on a flattened version and finally filter out the indices corresponding to the border ones to give ourselves a vectorized solution, like so -
# Input 3D array : a
b = np.pad(a, ((0,0),(0,0),(1,1)), 'constant', constant_values=(0,0))
# Get ramp-up and ramp-down indices/ start-end indices of 1s islands
s0 = np.flatnonzero(b[...,1:]>b[...,:-1])
s1 = np.flatnonzero(b[...,1:]<b[...,:-1])
# Filter only valid ones that are not at borders
n = b.shape[2]
valid_mask = (s0%(n-1)!=0) & (s1%(n-1)!=a.shape[2])
out = (s1-s0)[valid_mask]
Explanation -
The idea with padding zeros at either ends of each row as "sentients" is that when we get one-off sliced array versions and compare, we could detect the ramp-up and ramp-down places with b[...,1:]>b[...,:-1] and b[...,1:]<b[...,:-1] respectively. Thus, we get s0 and s1 as the start and end indices for each of the islands of 1s. Now, we don't want the border ones, so we need to get their column indices traced back to the original un-padded input array, hence that bit : s0%(n-1) and s1%(n-1). We need to remove all cases where the start of each island of 1s are at the left border and end of each island of 1s at the right side border. The starts and ends are s0 and s1. So, we use those to check if s0 is 0 and s1 is a.shape[2]. These give us the valid ones. The island lengths are obtained with s1-s0, so mask it with valid-mask to get our desired output.
Sample input, output -
In [151]: a
Out[151]:
array([[[0, 0, 0, 0, 0, 0],
[0, 1, 0, 0, 1, 0],
[0, 0, 0, 0, 0, 1]],
[[1, 0, 1, 1, 0, 0],
[0, 1, 1, 1, 1, 0],
[1, 1, 1, 0, 0, 0]]])
In [152]: out
Out[152]: array([1, 1, 2, 4])