How is this 2D array being sized by FOR loops? - python

Question background:
This is the first piece of Python code I've looked at and as such I'm assuming that my thread title is correct in explaining what this code is actually trying to achieve i.e setting a 2D array.
The code:
The code I'm looking at sets the size of a 2D array based on two for loops:
n = len(sentences)
values = [[0 for x in xrange(n)] for x in xrange(n)]
for i in range(0, n):
for j in range(0, n):
values[i][j] = self.sentences_intersection(sentences[i], sentences[j])
I could understand it if each side of the array was set with using the length property of the sentences variable, unless this is in effect what xrange is doing by using the loop size based on the length?
Any helping with explaing how the array is being set would be great.

This code is actually a bit redundant.
Firstly you need to realize that values is not an array, it is a list. A list is a dynamically sized one-dimensional structure.
The second line of the code uses a nested list comprehension to create one list of size n, each element of which is itself a list consisting of n zeros.
The second loop goes through this list of lists, and sets each element according to whatever sentences_intersection does.
The reason this is redundant is because lists don't need to be pre-allocated. Rather than doing two separate iterations, really the author should just be building up the lists with the correct values, then appending them.
This would be better:
n = len(sentences)
values = []
for i in range(0, n):
inner = []
for j in range(0, n):
inner.append(self.sentences_intersection(sentences[i], sentences[j]))
values.append(inner)
but you could actually do the whole thing in the list comprehension if you wanted:
values = [[self.sentences_intersection(sentences[i], sentences[j]) for i in xrange(n)] for j in xrange(n)]

Related

Unexpected output after merging two sorted arrays with Python

I found a partial solution to the problem; however, it seems that I'm getting extra numbers from my array than what it should be. This is the question I'm trying to find out:
Given two sorted integer arrays nums1 and nums2, merge nums2 into
nums1 as one sorted array.
Note:
The number of elements initialized in nums1 and nums2 are m and n
respectively. You may assume that nums1 has enough space (size that is
greater or equal to m + n) to hold additional elements from nums2.
Example:
Input: nums1 = [1,2,3,0,0,0], m = 3 nums2 = [2,5,6], n = 3
Output: [1,2,2,3,5,6]
I'm practicing some coding challenges to the hang of Python3 language and prepare myself for an interview. I have tried a few methods like using pop when the beginning of the array are 0s. But it seems that after new test case showed up, I should've expected more. I'm pretty new with the language.
def mergeArrays(nums1, m, nums2, n):
nums1[:] = sorted(nums1 + nums2)
i = 0
while (i < len(nums1[:-1])):
if nums1[i] is 0:
nums1.pop(i)
if i > len(nums1):
break
i += 1
print(nums1)
nums1 = [-49,-48,-48,-47,-45,-42,-39,-36,-33,-33,-28,-28,-23,-23,-7,-4,-3,0,0,4,6,21,29,29,31,34,36,38,40,43,45,46,47,0,0,0,0,0,0,0,0]
m = len(nums1)
nums2 = [-16,-5,-3,26,33,35,38,41]
n = len(nums2)
mergeArrays(nums1, m, nums2, n);
My expected output should be of both arrays sorted and go through. Results should be this: [-49,-48,-48,-47,-45,-42,-39,-36,-33,-33,-28,-28,-23,-23,-16,-7,-5,-4,-3,-3,0,0,4,6,21,26,29,29,31,33,34,35,36,38,38,40,41,43,45,46,47]
However, I'm getting a couple extra zeros, which should look like this:
[-49,-48,-48,-47,-45,-42,-39,-36,-33,-33,-28,-28,-23,-23,-16,-7,-5,-4,-3,-3,0,0,0,0,0,4,6,21,26,29,29,31,33,34,35,36,38,38,40,41,43,45,46,47]
EDIT: added more information to make the problem clear.
As per my understanding you want to sort the two sorted array without having any duplicate element. You can refer the below code:
first_list = [-49,-48,-48,-47,-45,-42,-39,-36,-33,-33,-28,-28,-23,-23,-7,-4,-3,0,0,4,6,21,29,29,31,34,36,38,40,43,45,46,47,0,0,0,0,0,0,0,0]
second_list = [-16,-5,-3,26,33,35,38,41]
merged_list = list(set(first_list+second_list))
merged_list.sort()
print(merged_list)
With one of the old methods that I used was a loop comprehension. Basically, what I did was array splice from beginning to end and do the sort inside of the loop:
def mergeArrays(nums1, m, nums2, n):
nums1[0: m + n] = [x for x in sorted(nums1[:m] + nums2[:n])]
If you have a different explanation than what I just did, please feel free :)
After much back-and-forth on the intent of your code and where your unwanted mystery zeros come from, seems you want to do the following: merge-sort your two arrays, preserving duplicates:
your input is arrays nums1, nums2 which are zero-padded, and can be longer than length m,n respectively
But to avoid picking up those padded zeros, you should only reference the entries 0..(m-1), i.e. nums1[:m], and likewise nums2[:n]
Your mistake was to reference all the way up to nums1[:-1]
Your solution is: sorted(nums1[:m] + nums2[:n]). It's a one-liner list comprehension and you don't need a function.
There is no reason whatsoever that zero entries need special treatment. There's no need for your while-loop.
Also btw even if you wanted to (say) exclude all zeros, you can still use a one-liner list-comprehension: x for x in sorted(nums1[:m] + nums2[:n]) if x != 0]
List comprehensions are a neat idiom and super-powerful! Please read more about them. Often you don't need while-loops in Python; list comprehensions, iterators or generators are typically cleaner, shorter code and more efficient.

Original list changes despite using independent copies

I would like to iterate through all elements of a matrix (in my code a list of lists) and create an independent copy of that matrix everytime, when the checked element meets a certain condition.
Every time a copy is created, I would like to change one of the elements in the copied matrix (so that the original matrix stays the same). Every copy of the matrix should get an individual name.
After that, I would like to store that copied matrix in a list.
So, for example: Consider the original Matrix is a 2x2 Matrix, containing four integers (let's say the numbers 1 to 4, as shown in the code below). Now let's loop through the matrix elements and create a copy of the matrix everytime, when the checked element is larger than 3. So we should get one copy (because only one element, the number 4, is larger than 3). In this copied matrix, I change one of the elements (e.g. let's say adding the number 10 to the element that was checked). Then I store this copied matrix in a list. My code looks like this:
matrix = [[1,2],[3,4]]
new_copies = []
counter = 0
for i in range(0,2):
for k in range(0,2):
if matrix[i][k] > 3:
exec("item%s = matrix[:]" % counter)
exec("item%s[i][k] = matrix[i][k] + 10" % counter)
exec("new_copies.append(item%s)" % counter)
counter += 1
print(matrix)
print(new_copies)
If you run this code, you will see that the copied matrix is changed correctly and also is stored in the list.
But the original matrix also is changed. Why? I only manipulate the copied versions of the matrix, which should be independent from the original, since I follow this principle:
new_matrix = original_matrix[:]
Why it is happening
Lists are mutable objects, that is why even if you are creating a new list object by doing matrix[:], your sublists are still pointing to the same objects...
A first solution
Here is a first workaround:
matrix = [[1, 2], [3, 4]]
new_copies = []
counter = 0
for i in range(0, 2):
sublist = matrix[i][:]
for k in range(0, 2):
if matrix[i][k] > 3:
sublist[k] += 10
counter += 1
new_copies.append(sublist)
print(matrix)
print(new_copies)
Or with lists comprehension
If possible, you could also use list comprehension, in this case that would be:
new_copies = [[(e + 10 if e > 3 else e) for e in l] for l in matrix]
which will give you the same result that my previous proposition
Or with mutable objects
A nice solution would be to use tuple instead of lists, because they are immutable objects. But it won't be possible if you have to modify your matrix along your program.
Or with deepcopy
You could also use the deepcopy method from the copy library...
And you saw me coming...
I have to remind that the use of eval should be avoided if possible...

Python - Splitting lists of integers during iteration

I have an array of data where I'm taking a slice through a 3D array of floats and appending the numbers of the elements that satisfy a set of upper and lower bounds (also floats).
The first part of my code contains a nested for loop in the style of the following:
x_lst = []
for i in range(len(x1)):
for x in range(len(floatarray[0,:,0])):
if x1[i] <= floatarray[0,x,0] <= x2[i]:
x_lst.append(x)
#issues after this point
The inner loop compares the data in the array (floatarray[0,x,0]) with the boundaries x1 and x2, returning a list of integers, whilst the outer loop iterates through the values of the boundaries.
The issue I have is that the output (x_lst) is a single list, whereas I've been trying to produce a list of lists, with each sublist corresponding to the iterable i.
e.g.
#Desired output
x_lst = [[5,6,7,13,14],[21,22,23,36,37],[44,45,...
#Actual output
x_lst = [5,6,7,13,14,21,22,23,36,37,44,45,...
I feel like there's a very simple way of doing this, but I've not been able come up with anything that works (such as trying to use x_lst.split() after the append).
Also, any feedback on the question would be great as I'm still fairly new to SO.
It seems the following should work: why not create an intermediate sublist for each i, and add appropriate values to the sublist, then finally add the sublist to the main list?
x_lst = []
for i in range(len(x1)):
inner_list = [] # The sublist to be appended.
for x in range(len(floatarray[0,:,0])):
if x1[i] <= floatarray[0,x,0] <= x2[i]:
inner_list.append(x) # Add the numbers to the sublist.
# Issues after this point...
x_lst.append(inner_list) # Add the sublist to the main list.
Everything appears to be correct in the code except that you append into a 1-d array. For solving your problem you can simply change your code so that a temporary array named temp will append data in inner loop which would then be appended to your outer array x_lst as shown below:
x_lst = []
for i in range(len(x1))
temp=[]
for x in range(len(floatarray[0,:,0])):
if x1[i] <= floatarray[0,x,0] <= x2[i]:
temp.append(x)
x_lst.append(temp);
Okay, so Sam's answer is correct insofar as appending the values through the iteration, but trying it out with my code produced lists of lists of len(i) where each sublist contained all values of x that satisfied all elements of x1 and x2, instead of each list containing values specific to the [i]th element of x1 and x2, thus all the sublists were identical.
I got around this by replacing the inner for loop with a list comprehension, as follows, that gave the right result (in the desired form x_lst = [[5,6,7,13,14],[21,22,23,36,37],[44,45,...):
x_lst = []
for i in range(len(x1)):
y = [x for x in range(len(floatarray[0,:,0])) if x1[i] <= floatarray[0,x,0] <= x2[i]]
x_lst.append(y)

Iterating efficiently through indices of arbitrary order array

Say I have an arbitrary array of variable order N. For example:
A is a 2x3x3 array is an order 3 array with 2,3, and 3 dimiensions along it's three indices.
I would like to efficiently loop through each element. If I knew a priori the order then I could do something like (in python),
#for order 3
import numpy as np
shape = np.shape(A)
i = 0
while i < shape[0]:
j = 0
while j < shape[1]:
k = 0
while k < shape[2]:
#code using i,j,k
k += 1
j += 1
i += 1
Now suppose I don't know the order of A, i.e. I don't know a priori the length of shape. How can I permute the quickest through all elements of the array?
There are many ways to do this, e.g. iterating over a.ravel() or a.flat. However, looping over every single element of an array in a Python loop will never be particularly efficient.
I don't think it matters which index you choose to permute over first, which index you choose to permute over second, etc. because your inner-most while statement will always be executed once per combination of i, j, and k.
If you need to keep the results of your operation (and assuming its a function of A and i,j,k) You'd want to use something like this:
import itertools
import numpy as np
results = ( (position, code(A,position))
for indices in itertools.product(*(range(i) for i in np.shape(A))))
Then you can iterate the results getting out the position and return value of code for each position. Or convert the generator expression to a list if you need to access the results multiple times.
If the array of of the format array = [[[1,2,3,4],[1,2]],[[1],[1,2,3]]]
You could use the following structure:
array = [[[1,2,3,4],[1,2]],[[1],[1,2,3]]]
indices = []
def iter_array(array,indices):
indices.append(0)
for a in array:
if isinstance(a[0],list):
iter_array(a,indices)
else:
indices.append(0)
for nonlist in a:
#do something using each element in indices
#print(indices)
indices.append(indices.pop()+1)
indices.pop()
indices.append(indices.pop()+1)
indices.pop()
iter_array(array,indices)
This should work for the usual nested list "arrays" I don't know if it would be possible to mimic this using numpy's array structure.

2D Array Unintended Assignment Bug

I want to create a 2D array, like so:
grid[y][x]
So that there are y amount of rows and x amount of columns.
Below is the way I did it, but I when I tried to assign the (0,0) of the array to contain the value '2', the code assigned the first value of each subarray to '2'.
Why is this happening? How should I pythonically instantiate a 2D array?
n = 4
x=0
y=0
grid = [[None]*n]*n
print grid
grid[y][x]='Here'
print grid
when you use * you create multiple references, it does not copy the data
so when you modify the first line to
[here,none,none,none]
you actually change all lines.
solution
[[None for i in range(n)] for j in range(n)]
Edit (from other post) Since only the lists are mutable (can change in place) you can also do
[[None]*n for j in range(n)].
Each of the rows are then still unique. If the None object could be changed in place this would not work.
grid = [[None]*n for i in range(n)]

Categories