Suppose you have tuple of tuples(looks like a matrix).
Now I want to change its contents, so I convert it into a list.
Suppose that I have the number of rows and cols of the matrix.
How can I match between the indexes in the matrix to the indexes in the list?
Thanx in advance.
With a list, you can simply use the [] operator again. So, for example:
>>> a = [[1,2], [3, 4]]
>>> a[0][0]
1
>>> a[0][1]
2
>>> a[1][0]
3
>>> a[1][1]
4
>>> type(a)
<type 'list'>
>>> type(a[0])
<type 'list'>
>>> type(a[0][0])
<type 'int'>
The explanation is simple, the first time, you use the [] operator, you get a list, so you can use the [] operator again. Like this, you can emulate a matrix.
If you want to find the indexes, then you can use this nifty little function:
def finder(value_to_find, matrix):
for r, row in enumerate(matrix):
for c, col in enumerate(row):
if col == value_to_find:
return r, c
And, for a demo:
>>> a = [[1,2], [3, 4]]
>>> a[0][0]
1
>>> a[0][1]
2
>>> a[1][0]
3
>>> a[1][1]
4
>>> def finder(value_to_find, matrix):
for r, row in enumerate(matrix):
for c, col in enumerate(row):
if col == value_to_find:
return r, c
>>> finder(4, a)
(1, 1)
And here is an explanation with comments:
def finder(value_to_find, matrix):
"""
Function to find the indexes of a given value, on first notice
#param value_to_find: The value we need to find
#param matrix: The matrix we are to work with
#return: A tuple of row, column
"""
# Looping over the rows (lists within the main matrix)
for r, row in enumerate(matrix): # Using enumerate returns the index, r and the value row (which is a list)
for c, col in enumerate(row): # Looping over the values in each row, with index c and value col
if col == value_to_find: # If the col is equal to the value we want, then we return the row, column tuple
return r, c
If you have a 1-D matrix, then you can look at this solution from Hyperborius:
listindex = row * length_of_row + column
a = [[1,2], [3, 4]]
if a is the matrix, we can access the individual elements by using two parameters, row and column. Here, row refers to the number of list and column will refer to the position of the element in the list.
So, column is nothing but the normal way of referencing an element in the list and row is nothing but the normal way of referencing a list inside a list. Both row and column are zero based indices.
The format is
a[row][column]
when we say
a[row]
it means that, from the list of lists, get the list at position row and when we say
a[row][column]
we say that, from the list which we want, pick the element at position column.
Assuming you have a tuple of tuples:
>>> a = ((1,2,3),(4,5,6),(7,8,9))
which you converted to a flat list, presumably using a technique like this (credits to Joel Cornett, https://stackoverflow.com/a/10636583/219229):
>>> b = list(sum(a, ()))
>>> b
[1, 2, 3, 4, 5, 6, 7, 8, 9]
Such that b effectively lost the original multi-dimensional indexing. If you already know the original index from a, you can calculate its index in b as following:
>>> matrix_width = len(a[0])
... (assuming you have the indices i,j) ...
>>> index_in_b = j*matrix_width + i
>>> item_to_find = b[index_in_b]
If you expand this to multi-dimensional arrays, like 3d-array, and you have indices i, j, k, then it should be index = i + (j * width) + (k * width * span), where width = a[0][0], and span = a[0]
P/S: Just in case you want to convert it to list of lists, here are some shorthands you can use:
>>> b = list[map(list,a)] # using map
>>> b = [list(x) for x in a] # using list comprehension
Related
I have a certain function that I made and I want to run it on each column and each row of a matrix, to check if there are rows and columns that produce the same output.
for example:
matrix = [[1,2,3],
[7,8,9]]
I want to run the function, lets call it myfun, on each column [1,7], [2,8] and [3,9] separatly, and also run it on each row [1,2,3] and [7,8,9]. If there is a row and a column that produce the same result, the counter ct would go up 1. All of this is found in another function, called count_good, which basically counts rows and columns that produce the same result.
here is the code so far:
def count_good(mat):
ct = 0
for i in mat:
for j in mat:
if myfun(i) == myfun(j):
ct += 1
return ct
However, when I use print to check my code I get this:
mat = [[1,2,3],[7,8,9]]
for i in mat:
for j in mat:
print(i,j)
[1, 2, 3] [1, 2, 3]
[1, 2, 3] [7, 8, 9]
[7, 8, 9] [1, 2, 3]
[7, 8, 9] [7, 8, 9]
I see that the code does not return what I need' which means that the count_good function won't work. How can I run a function on each row and each column? I need to do it without any help of outside libraries, no map,zip or stuff like that, only very pure python.
Let's start by using itertools and collections for this, then translate it back to "pure" python.
from itertools import product, starmap, chain # combinations?
from collections import Counter
To iterate in a nested loop efficiently, you can use itertools.product. You can use starmap to expand the arguments of a function as well. Here is a generator of the values of myfun over the rows:
starmap(myfun, product(matrix, repeat=2))
To transpose the matrix and iterate over the columns, use the zip(* idiom:
starmap(myfun, product(zip(*matrix), repeat=2))
You can use collections.Counter to map all the repeats for each possible return value:
Counter(starmap(myfun, chain(product(matrix, repeat=2), product(zip(*matrix), repeat=2))))
If you want to avoid running myfun on the same elements, replace product(..., repeat=2) with combinations(..., 2).
Now that you have the layout of how to do this, replace all the external library stuff with equivalent builtins:
counter = {}
for i in range(len(matrix)):
for j in range(len(matrix)):
result = myfun(matrix[i], matrix[j])
counter[result] = counter.get(result, 0) + 1
for i in range(len(matrix[0])):
for j in range(len(matrix[0])):
c1 = [matrix[row][i] for row in range(len(matrix))]
c2 = [matrix[row][j] for row in range(len(matrix))]
result = myfun(c1, c2)
counter[result] = counter.get(result, 0) + 1
If you want combinations instead, replace the loop pairs with
for i in range(len(...) - 1):
for j in range(i + 1, len(...)):
Using native python:
def count_good(mat):
ct = 0
columns = [[row[col_idx] for row in mat] for col_idx in range(len(mat[0]))]
for row in mat:
for column in columns:
if myfun(row) == myfun(column):
ct += 1
return ct
However, this is very inefficient as it is a triple nested for-loop. I would suggest using numpy instead.
e.g.
def count_good(mat):
ct = 0
mat = np.array(mat)
for row in mat:
for column in mat.T:
if myfun(row) == myfun(column):
ct += 1
return ct
TL;DR
To get a column from a 2D list of N lists of M elements, first flatten the list to a 1D list of N×M elements, then choosing elements from the 1D list with a stride equal to M, the number of columns, gives you a column of the original 2D list.
First, I create a matrix of random integers, as a list of lists of equal
length — Here I take some liberty from the objective of "pure" Python, the OP
will probably input by hand some assigned matrix.
from random import randrange, seed
seed(20220914)
dim = 5
matrix = [[randrange(dim) for column in range(dim)] for row in range(dim)]
print(*matrix, sep='\n')
We need a function to be applied to each row and each column of the matrix,
that I intend must be supplied as a list. Here I choose a simple summation of
the elements.
def myfun(l_st):
the_sum = 0
for value in l_st: the_sum = the_sum+value
return the_sum
To proceed, we are going to do something unexpected, that is we unwrap the
matrix, starting from an empty list we do a loop on the rows and "sum" the
current row to unwrapped, note that summing two lists gives you a single
list containing all the elements of the two lists.
unwrapped = []
for row in matrix: unwrapped = unwrapped+row
In the following we will need the number of columns in the matrix, this number
can be computed counting the elements in the last row of the matrix.
ncols = 0
for value in row: ncols = ncols+1
Now, we can compute the values produced applying myfunc to each column,
counting how many times we have the same value.
We use an auxiliary variable, start, that is initialized to zero and
incremented in every iteration of the following loop, that scans, using a
dummy variable, all the elements of the current row, hence start has the
values 0, 1, ..., ncols-1, so that unwrapped[start::ncols] is a list
containing exactly one of the columns of the matrix.
count_of_column_values = {}
start = 0
for dummy in row:
column_value = myfun(unwrapped[start::ncols])
if column_value not in count_of_column_values:
count_of_column_values[column_value] = 1
else:
count_of_column_values[column_value] = count_of_column_values[column_value] + 1
start = start+1
At this point, we are ready to apply myfun to the rows
count = 0
for row in matrix:
row_value = myfun(row)
if row_value in count_of_column_values: count = count+count_of_column_values[row_value]
print(count)
Executing the code above prints
[1, 4, 4, 1, 0]
[1, 2, 4, 1, 4]
[1, 4, 4, 0, 1]
[4, 0, 3, 1, 2]
[0, 0, 4, 2, 2]
3
I have two arrays v and c (can read as value and cost).
I need to perform argsort() on v such that if 2 elements in v are the same, then they need to be sorted according to their corresponding elements in c.
Example
v = [4,1,4,4] # Here 0th, 2nd and 3rd elemnt are equal
c = [5,0,30,10]
numpy.argsort(v) = [1,0,2,3] # equal values sorted by index
Required output
[1,0,3,2] # c[0] < c[3] < c[2]
How to achieve this in Python?
The function argsort receives an order parameter, from the docs:
When a is an array with fields defined, this argument specifies which
fields to compare first, second, etc.
So you could create a structured array from the two values, and the pass the fields in order:
import numpy as np
v = [4, 1, 4, 4]
c = [5, 0, 30, 10]
s = np.array(list(zip(v, c)), dtype=[('value', 'i4'), ('cost', 'i4')])
result = np.argsort(s, order=['value', 'cost'])
print(result)
Output
[1 0 3 2]
I am trying to perform in-place modification of a list of list on the level of the primary list. However, when I try to modify the iterating variable (row in the example below), it appears to create a new pointer to it rather than modifying it.
Smallest example of my problem.
c = [1,2,3]
for x in c:
x = x + 3
print(c) #returns [1,2,3], expected [4,5,6]
The above example is a trivial example of my problem. Is there a way to modify x elementwise, in-place and have the changes appear in C?
Less trivial example of my problem. I am switching all 0's to 1's and vice-versa.
A = [[1,1,0],
[1,0,1],
[0,0,0]]
for row in A:
row = list(map(lambda val: 1 - val, row))
print(A)
Expected
A = [[0,0,1],
[0,1,0],
[1,1,1]]
Returned
A = [[1,1,0],
[1,0,1],
[0,0,0]]
update:
Great answers so far. I am interested how the iterating variable (row in the second example) is linked to the iterable variable (A in the second example).
If I do the following, which reverses each sublist of A, it works perfectly.
Why does the following example, where I modify the iterating variable works but the above examples do not?
A = [[1,1,0],
[1,0,1],
[0,0,0]]
for row in A:
row.reverse()
print(A)
#returns, as expected
A = [[0, 1, 1],
[1, 0, 1],
[0, 0, 0]]
I found this in the docs: https://docs.python.org/3/tutorial/controlflow.html#for
Python’s for statement iterates over the items of any sequence (a list
or a string), in the order that they appear in the sequence.
If you need to modify the sequence you are iterating over while inside
the loop (for example to duplicate selected items), it is recommended
that you first make a copy. Iterating over a sequence does not
implicitly make a copy.
I was wrong in my first response, when iterating through a list it returns the actual items in that list. However, it seems they cannot be edited directly while they are being iterated through. This is why iterating through the integers the length of the list works.
As for why the .reverse() function works, I think it's because it is affecting a list instead of a value. I tried to use similar built in functions on nonlist datatypes like .replace() on strings and it had no effect.
All of the other list functions I tried worked: .append(), .remove(), and .reverse() as you showed. I'm not sure why this is, but I hope it clears up what you can do in for loops a bit more.
Answer to old question below:
The way you are using the for loops doesn't affect the actual list, just the temporary variable that is iterating through the list. There are a few ways you can fix this. Instead of iterating through each element you can can count up to the length of the list and modify the list directly.
c = [1,2,3]
for n in range(len(c)):
c[n] += 3
print(c)
You can also use the enumerate() function to iterate through both a counter and list items.
c = [1,2,3]
for n, x in enumerate(c):
c[n] = x + 3
print(c)
In this case, n is a counter and x is the item in the list.
Finally, you can use list comprehension to generate a new list with desired differences in one line.
c = [1, 2, 3]
d = [x + 3 for x in c]
print(d)
The usual way to poke values into an existing list in Python is to use enumerate which lets you iterate over both the indices and the values at once -- then use the indices to manipulate the list:
c = [1,2,3]
for index, value in enumerate(c):
c[index] = value + 3
For your second example you'd do almost the same:
A = [[1,1,0],
[1,0,1],
[0,0,0]]
for row in A:
for index, val in row:
row[index] = 0 if val > 0 else 1
In the second example the list objects in A become the loop variable row -- and since you're only mutating them (not assigning to them) you don't need enumerate and the index
If you want to keep it consice without creating an additional variable, you could also do:
c = [1,2,3]
print(id(c))
c[:] = [i+3 for i in c]
print(c, id(c))
Output:
2881750110600
[4, 5, 6] 2881750110600
Using list comprehension here also will work:
A = [[1,1,0],
[1,0,1],
[0,0,0]]
A = [[0 if x > 0 else 1 for x in row] for row in A]
print(A)
Output:
[[0, 0, 1],
[0, 1, 0],
[1, 1, 1]]
I know this is similar to Efficient way to compare elements in 2 lists, but I have an extension on the question basically.
Say I have two lists:
a = [1,2,4,1,0,3,2]
b = [0,1,2,3,4]
I want to find out the indices of a where the element is equal to each element of b.
For instance, I would want the sample output for b[1] to tell me that a = b[1] at [0,3].
A data frame output would be useful as well, something like:
b index_a
0 4
1 0
1 3
2 1
2 6
3 5
4 3
What I used before was:
b = pd.DataFrame(b)
a = pd.DataFrame(a)
pd.merge(b.reset_index(),a.reset_index(),
left_on=b.columns.tolist(),
right_on = a.columns.tolist(),
suffixes = ('_b','_a'))['index_b','index_a']]
However, I am unsure if this is necessary since these are for lists. ( I used this method previously when I was working with dataframes ).
I am doing this operation thousands of times with much larger lists so I am wondering if there is a more efficient method.
In addition, b is just list(range(X)) where in this case X = 5
If anyone has some input I'd greatly appreciate it!
Thanks
A very simple and efficient solution is to build a mapping from the values in the range 0..N-1 to indices of a. The mapping can be a simple list, so you end up with:
indices = [[] for _ in b]
for i, x in enumerate(a):
indices[x].append(i)
Example run:
>>> a = [1,2,4,1,0,3,2]
>>> b = [0,1,2,3,4]
>>> indices = [[] for _ in b]
>>> for i,x in enumerate(a):
... indices[x].append(i)
...
>>> indices[1]
[0, 3]
Note that b[i] == i so keeping the b list is pretty useless.
import collections
dd=collections.defaultdict(list)
for i,x in enumerate(a):
dd[x].append(i)
>>> sorted(dd.items())
[(0, [4]), (1, [0, 3]), (2, [1, 6]), (3, [5]), (4, [2])]
If b is sorted consecutive integers as you shown here, then bucket sort is most effective.
Otherwise, you may construct a hash table, with value b as the key, and construction a list of a's as values.
I'm not sure if this is efficient enough for your needs, but this would work:
from collections import defaultdict
indexes = defaultdict(set)
a = [1,2,4,1,0,3,2]
b = [0,1,2,3,4]
for i, x in enumerate(a):
indexes[x].add(i)
for x in b:
print b, indexes.get(x)
With list a = [1, 2, 3, 5, 4] I wish to find the index of the nth largest value. function(a, 4) = 2 since 2 is the index of the 4th largest value. NOTE: Needs to function for lists containing 500 or more elements and works with looping.
You could index into the result of sorted(a) to find the n-th largest value:
>>> a = [1, 2, 3, 5, 4]
>>> n = 4
>>> x = sorted(a)[-n]
>>> x
2
Then use a.index() to find the element's index in the original list (assuming the elements are unique):
>>> a.index(x) + 1 # use 1-based indexing
2
P.S. If n is small, you could also use heapq.nlargest() to get the n-th largest element:
>>> import heapq
>>> heapq.nlargest(n, a)[-1]
2
If not using index and max (easiest way) i would just:
def funcion (a):
highest_value = [0, position]
for x in range(len(a),0,-1):
value = a.pop()
if value > highest_value[0]:
highest_value[0] = value
highest_value[1] = len(a)-x
return highest_value
This way, you would get the highest value and save it index at the same time, so it should be quite efficient. Using pop is much faster than looking from 0 to end, because access is backward.
I think you're using 1 based indexing. Assumes values are unique.
a.index(sorted(a)[4-1])+1
will give you 5.