How to express j+1 in this case? - python

I'm writing a piece of code to filter some data, and I've come across a problem. I currently have:
def accountforfilter(wavelength, flux, filterwavelength, throughput):
filteredwavelength=[]
filteredflux=[]
for i in wavelength:
for j in filterwavelength:
if wavelength==filterwavelength:
j=filterwavelength.index(wavelength)
filteredwavelength.append(wavelength)
filteredflux.append(flux*throughput)
elif filterwavelength<wavelength<filterwavelength(j+1):
filteredwavelength.append(wavelength)
filteredflux.append(flux*f(wavelength))
#f is a function that interpolates using y=mx+c when wavelength and filterwavelength are not the same
elif wavelength<filterwavelength:
i+=1
elif wavelength>filterwavelength:
j+=1
return filteredwavelength, filteredflux
The problem I have is with the line:
elif filterwavelength<wavelength<filterwavelength(j+1):
This line doesn't work, I get the error:
TypeError: 'list' object is not callable
If I change the round brackets to square brackets, i.e.
elif filterwavelength<wavelength<filterwavelength[j+1]:
I get the error:
TypeError: list indices must be integers, not float
I'm not sure how to express what I mean in another way.

You should really use more descriptive variable names to avoid numerous errors that exist in your code.
I assume that in your function parameters, wavelength and filterwavelength are lists. So let’s make that clear by using plural names:
def accountforfilter(wavelengths, flux, filterwavelengths, throughput)
As soon as you do that, comparison like these don’t make much sense anymore:
if wavelengths == filterwavelengths:
elif filterwavelengths < wavelengths < filterwavelengths[j+1]:
Especially in the second comparison, you are comparing a list, with a list, with a list item.
Finally, when using for, the loop variable (your i and j) is set to individual list items, not indexes. So you definitely can’t tread those values as indexes. To reflect that, you should name them appropriately:
# one wavelength out of all wavelengths
for wavelength in wavelengths:
# one filterwavelength out of all filterwavelengths
for filterwavelength in filterwavelengths:
Also note, that when using for, you can’t change the way they are iterating. Every iteration, the next item will be handled; you can’t skip or repeat one item (as you seem to be trying with your i += 1 and j += 1. If you need to do that, you will have to use a classic while loop instead:
i, j = 0, 0
while i < len(wavelengths):
while j < len(filterwavelengths):
if condition:
i += 1
else:
j += 1
So, in total, the function might look like this (tried my best to understand what’s going on):
def accountforfilter (wavelengths, flux, filterwavelengths, throughput):
filteredWavelengths = []
filteredFluxes = []
i, j = 0, 0
while i < len(wavelengths):
wavelength = wavelengths[i]
while j < len(filterwavelengths):
filterwavelength = filterwavelengths[j]
if wavelength == filterwavelength:
filteredWavelengths.append(wavelength)
filteredFluxes.append(flux * throughput)
elif j + 1 < len(filterwavelengths) and filterwavelength < wavelength < filterwavelengths[j + 1]:
filteredWavelengths.append(wavelength)
filteredFluxes.append(flux * f(wavelength))
if wavelength < filteredwavelength:
i += 1
else:
j += 1
# increase j even when both are equal, otherwise we end up in an infinite loop
return filteredWavelengths, filteredFluxes

A few things: This statement:
for i in wavelength:
produces a sequence of the values in the wavelength list, not indexes to them.
If you want the indexes as well, use
for i, wv in enumerate(wavelength):
...
Similarly for the j loop. Also, this comparison: wavelength==filterwavelength is dangerous if the values are floating point. It's a little hard to advise further without more information about what you're trying to acheive.

Related

How to make a loop stop after it's done iterating through a key's values?

Title is a bit complex, and I'm fairly new to Python.
Here's what my problem is,
I currently have a dictionary, and I'm trying to create a tuple according a rule. I have got the tuple creation under control, I just need a way to iterate differenly so that I can create my tuple
Here's what I have:
The list that "i" goes through is titled DEMANDPOINT= [1,2,3.......954]
The second list I have is, a tuple that consists of 734 groups each has 5 numbers, titled tuples of stations "j" goes through this
I have a nested dictionary that goes like this: distance5 = {1: {513: 0.6291397367847646, 279: 0.7823399663201693, 582: 0.8703625254182541, 392: 1.0191414569956174, 297: 1.0660162833624383}, 2: {297: 1.1155101452481397, 279: 1.458507500394341, 513: 1.7296683927743512, 582: 1.8516961940275365, 392: 2.0394772611422898}
I want "iterator i =1 " to stop after it checks tuple_of_stations's first group of 5 numbers, and switch to 2.
Here's the code that I'm using to create my tuple.
for i in DEMANDPOINT:
for j in tuple_of_stations:
if distance5[i][j] <= Dc:
N[i] += (j,)
if len(N[i]) == 0:
N[i] += ((min(distance5[i], key=distance5[i].get)),)
I want the "iterator i" to stop after it has checked the values of 1st dictionary. I have no idea how I can do that.
Basically like this: example image In the example, I have explained how I want the iterators to go through.
Sorry if that's not a clear explanation, I tried to explain my problem as much as I could. Thanks in advance
I have changed a little the code, let me know if now it does what you need:
N = {}
for i in distance5:
tuple_of_stations = list(distance5[i].keys())
c = 0
for j in tuple_of_stations:
if distance5[i][j] <= Dc:
if i not in N:
N[i] = (j,)
else:
N[i] += (j,)
c += 1
if c >= 5:
break
if i not in N:
N[i] = ((min(distance5[i], key=distance5[i].get)),)
elif not N[i]:
N[i] += ((min(distance5[i], key=distance5[i].get)),)

Breaking an iterative function in Python before a condition turns False

This is for a school assignment.
I have been tasked to define a function determining the largest square pyramidal number up to a given integer(argument). For some background, these are square pyramidal numbers:
1 = 1^2
5 = 1^2+2^2
14 = 1^2+2^2+3^2
So for a function and parameter largest_square_pyramidal_num(15), the function should return 14, because that's the largest number within the domain of the argument.
I get the idea. And here's my code:
def largest_square_pyramidal_num(n):
sum = 0
i = 0
while sum < n:
sum += i**2
i += 1
return sum
Logically to me, it seemed nice and rosy until I realised it doesn't stop when it's supposed to. When n = 15, sum = 14, sum < n, so the code adds one more round of i**2, and n is exceeded. I've been cracking my head over how to stop the iteration before the condition sum < n turns false, including an attempt at break and continue:
def largest_square_pyramidal_num(n):
sum = 0
for i in range(n+1):
sum += i**2
if sum >= n:
break
else:
continue
return sum
Only to realise it doesn't make any difference.
Can someone give me any advice? Where is my logical lapse? Greatly appreciated!
You can do the following:
def largest_pyr(x):
pyr=[sum([i**2 for i in range(1,k+1)]) for k in range(int(x**0.5)+1)]
pyr=[i for i in pyr if i<=x]
return pyr[-1]
>>>largest_pyr(15)
14
>>> largest_pyr(150)
140
>>> largest_pyr(1500)
1496
>>> largest_pyr(15000)
14910
>>> largest_pyr(150000)
149226
Let me start by saying that continue in the second code piece is redundant. This instruction is used for scenario when you don't want the code in for loop to continue but rather to start a new iteration (in your case there are not more instructions in the loop body).
For example, let's print every number from 1 to 100, but skip those ending with 0:
for i in range(1, 100 + 1):
if i % 10 != 0:
print(i)
for i in range(1, 100 + 1):
if i % 10 == 0:
# i don't want to continue executing the body of for loop,
# get me to the next iteration
continue
print(i)
The first example is to accept all "good" numbers while the second is rather to exclude the "bad" numbers. IMHO, continue is a good way to get rid of some "unnecessary" elements in the container rather than writing an if (your code inside if becomes extra-indented, which worsens readability for bigger functions).
As for your first piece, let's think about it for a while. You while loop terminates when the piramid number is greater or equal than n. And that is not what you really want (yes, you may end up with a piramid number which is equal to n, but it is not always the case).
What I like to suggest is to generate a pyramid number until in exceedes n and then take a step back by removing an extra term:
def largest_square_pyramidal_num(n):
result = 0
i = 0
while result <= n:
i += 1
result += i**2
result -= i ** 2
return result
2 things to note:
don't use sum as a name for the variable (it might confuse people with built-in sum() function)
I swapped increment and result updating in the loop body (such that i is up-to-date when the while loop terminates)
So the function reads like this: keep adding terms until we take too much and go 1 step back.
Hope that makes some sense.
Cheers :)

Guidance on removing a nested for loop from function

I'm trying to write the fastest algorithm possible to return the number of "magic triples" (i.e. x, y, z where z is a multiple of y and y is a multiple of x) in a list of 3-2000 integers.
(Note: I believe the list was expected to be sorted and unique but one of the test examples given was [1,1,1] with the expected result of 1 - that is a mistake in the challenge itself though because the definition of a magic triple was explicitly noted as x < y < z, which [1,1,1] isn't. In any case, I was trying to optimise an algorithm for sorted lists of unique integers.)
I haven't been able to work out a solution that doesn't include having three consecutive loops and therefore being O(n^3). I've seen one online that is O(n^2) but I can't get my head around what it's doing, so it doesn't feel right to submit it.
My code is:
def solution(l):
if len(l) < 3:
return 0
elif l == [1,1,1]:
return 1
else:
halfway = int(l[-1]/2)
quarterway = int(halfway/2)
quarterIndex = 0
halfIndex = 0
for i in range(len(l)):
if l[i] >= quarterway:
quarterIndex = i
break
for i in range(len(l)):
if l[i] >= halfway:
halfIndex = i
break
triples = 0
for i in l[:quarterIndex+1]:
for j in l[:halfIndex+1]:
if j != i and j % i == 0:
multiple = 2
while (j * multiple) <= l[-1]:
if j * multiple in l:
triples += 1
multiple += 1
return triples
I've spent quite a lot of time going through examples manually and removing loops through unnecessary sections of the lists but this still completes a list of 2,000 integers in about a second where the O(n^2) solution I found completes the same list in 0.6 seconds - it seems like such a small difference but obviously it means mine takes 60% longer.
Am I missing a really obvious way of removing one of the loops?
Also, I saw mention of making a directed graph and I see the promise in that. I can make the list of first nodes from the original list with a built-in function, so in principle I presume that means I can make the overall graph with two for loops and then return the length of the third node list, but I hit a wall with that too. I just can't seem to make progress without that third loop!!
from array import array
def num_triples(l):
n = len(l)
pairs = set()
lower_counts = array("I", (0 for _ in range(n)))
upper_counts = lower_counts[:]
for i in range(n - 1):
lower = l[i]
for j in range(i + 1, n):
upper = l[j]
if upper % lower == 0:
lower_counts[i] += 1
upper_counts[j] += 1
return sum(nx * nz for nz, nx in zip(lower_counts, upper_counts))
Here, lower_counts[i] is the number of pairs of which the ith number is the y, and z is the other number in the pair (i.e. the number of different z values for this y).
Similarly, upper_counts[i] is the number of pairs of which the ith number is the y, and x is the other number in the pair (i.e. the number of different x values for this y).
So the number of triples in which the ith number is the y value is just the product of those two numbers.
The use of an array here for storing the counts is for scalability of access time. Tests show that up to n=2000 it makes negligible difference in practice, and even up to n=20000 it only made about a 1% difference to the run time (compared to using a list), but it could in principle be the fastest growing term for very large n.
How about using itertools.combinations instead of nested for loops? Combined with list comprehension, it's cleaner and much faster. Let's say l = [your list of integers] and let's assume it's already sorted.
from itertools import combinations
def div(i,j,k): # this function has the logic
return l[k]%l[j]==l[j]%l[i]==0
r = sum([div(i,j,k) for i,j,k in combinations(range(len(l)),3) if i<j<k])
#alaniwi provided a very smart iterative solution.
Here is a recursive solution.
def find_magicals(lst, nplet):
"""Find the number of magical n-plets in a given lst"""
res = 0
for i, base in enumerate(lst):
# find all the multiples of current base
multiples = [num for num in lst[i + 1:] if not num % base]
res += len(multiples) if nplet <= 2 else find_magicals(multiples, nplet - 1)
return res
def solution(lst):
return find_magicals(lst, 3)
The problem can be divided into selecting any number in the original list as the base (i.e x), how many du-plets we can find among the numbers bigger than the base. Since the method to find all du-plets is the same as finding tri-plets, we can solve the problem recursively.
From my testing, this recursive solution is comparable to, if not more performant than, the iterative solution.
This answer was the first suggestion by #alaniwi and is the one I've found to be the fastest (at 0.59 seconds for a 2,000 integer list).
def solution(l):
n = len(l)
lower_counts = dict((val, 0) for val in l)
upper_counts = lower_counts.copy()
for i in range(n - 1):
lower = l[i]
for j in range(i + 1, n):
upper = l[j]
if upper % lower == 0:
lower_counts[lower] += 1
upper_counts[upper] += 1
return sum((lower_counts[y] * upper_counts[y] for y in l))
I think I've managed to get my head around it. What it is essentially doing is comparing each number in the list with every other number to see if the smaller is divisible by the larger and makes two dictionaries:
One with the number of times a number is divisible by a larger
number,
One with the number of times it has a smaller number divisible by
it.
You compare the two dictionaries and multiply the values for each key because the key having a 0 in either essentially means it is not the second number in a triple.
Example:
l = [1,2,3,4,5,6]
lower_counts = {1:5, 2:2, 3:1, 4:0, 5:0, 6:0}
upper_counts = {1:0, 2:1, 3:1, 4:2, 5:1, 6:3}
triple_tuple = ([1,2,4], [1,2,6], [1,3,6])

Python: Altering list values from a loop

I am creating a simple Bin packing program in Python for a class and am having a scope issue that I cannot quite track down. I am attempting to alter the value in the ffBins list however the list the value is unaltered.
The print results show what I expect ("10 5 5") as i is subtracted from j but it is not altering the value in the actual list as I am expecting. I suspect I need to point at the list in a different way during the loop but the solution is eluding me. Is there a simple way to alter the values in the list instead of just the current j variable?
ffBins.append(10)
for i in ffItems:
itemPacked = 0
for j in ffBins:
#print(j)
if j >= i:
print(j),
print(i),
j = j-i
print(j)
itemPacked = 1
#break
if itemPacked == 0:
ffBins.append(10)
ffBins[-1] = ffBins[-1]-ffItems[i]
All you're doing in this code is rebinding j to a new value. You need to set the value back in the list by its index. You can use enumerate to get the index as well as the value.
for index, j in enumerate(ffBins):
...
ffbins[index] = j - i

How to update array Index in loop (IndexError: list index out of range)

I should not use advance function, as this is a logical test during interview.
Trying to remove all digits which appear more than once in array.
testcase:
a=[1,1,2,3,2,4,5,6,7]
code:
def dup(a):
i=0
arraySize = len(a)
print(arraySize)
while i < arraySize:
#print("1 = ",arraySize)
k=i+1
for k in range(k,arraySize):
if a[i] == a[k]:
a.remove(a[k])
arraySize -= 1
#print("2 = ",arraySize)
i += 1
print(a)
result should be : 1,2,3,4,5,6,7
But i keep getting index out of range. i know that it is because the array list inside the loop changed, so the "while" initial index is different with the new index.
The question is : any way to sync the new index length (array inside the loop) with the parent loop (index in "while" loop) ?
The only thing i can think of is to use function inside the loop.
any hint?
Re-Calculating Array Size Per Iteration
It looks like we have a couple issues here. The first issue is that you can't update the "stop" value in your inner loop (the range function). So first off, let's remove that and use another while loop to give us the ability to re-calculate our array size every iteration.
Re-Checking Values Shifted Into Removed List Spot
Next, after you fix that you will run into a larger issue. When you use remove it moves a value from the end of the list or shifts the entire list to the left to use the removed spot, and you are not re-checking the value that got moved into the old values removed spot. To resolve this, we need to decrement i whenever we remove an element, this makes sure we are checking the value that gets placed into the removed elements spot.
remove vs del
You should use del over remove in this case. remove iterates over the list and removes the first occurrence of the value and it looks like we already know the exact index of the value we want to remove. remove might work, but it's usage here over complicates things a bit.
Functional Code with Minimal Changeset
def dup(a):
i = 0
arraySize = len(a)
print(arraySize)
while i < arraySize:
k = i + 1
while k < arraySize: # CHANGE: use a while loop to have greater control over the array size.
if a[i] == a[k]:
print("Duplicate found at indexes %d and %d." % (i, k))
del a[i] # CHANGE: used del instead of remove.
i -= 1 # CHANGE: you need to recheck the new value that got placed into the old removed spot.
arraySize -= 1
break
k += 1
i += 1
return a
Now, I'd like to note that we have some readability and maintainability issues with the code above. Iterating through an array and manipulating the iterator in the way we are doing is a bit messy and could be prone to simple mistakes. Below are a couple ways I'd implement this problem in a more readable and maintainable manner.
Simple Readable Alternative
def remove_duplicates(old_numbers):
""" Simple/naive implementation to remove duplicate numbers from a list of numbers. """
new_numbers = []
for old_number in old_numbers:
is_duplicate = False
for new_number in new_numbers:
if old_number == new_number:
is_duplicate = True
if is_duplicate == False:
new_numbers.append(old_number)
return new_numbers
Optimized Low Level Alternative
def remove_duplicates(numbers):
""" Removes all duplicates in the list of numbers in place. """
for i in range(len(numbers) - 1, -1, -1):
for k in range(i, -1, -1):
if i != k and numbers[i] == numbers[k]:
print("Duplicate found. Removing number at index: %d" % i)
del numbers[i]
break
return numbers
You could copy contents in another list and remove duplicates from that and return the list. For example:
duplicate = a.copy()
f = 0
for j in range(len(a)):
for i in range(len(duplicate)):
if i < len(duplicate):
if a[j] == duplicate[i]:
f = f+1
if f > 1:
f = 0
duplicate.remove(duplicate[i])
f=0
print(duplicate)

Categories

Resources