IndexError out of range but it Is in range - python

ratings_dict is a dictionary with the format: {'userName' : [0, 0, 1, 0, etc]}
I'm attempting to access each element in the list (in the dictionary) and it is giving me a specific error at 'if ratings_dict[name][i] is not 0:' and it is saying that IndexError: list index out of range.
Edited: This is my revised code. As you can see from the traceback, the value at ratings_dict['Martin'][0] is 1.
I have also revised my code in mind of your efficiency tips..
I am still at a loss at what to do.
def calculate_average_rating(ratings_dict):
ratings = {}
numBooks = len(ratings_dict)
print ratings_dict['Martin'][0]
for i in range(numBooks):
x = 0
sum = 0
numR = 0
for name in ratings_dict:
if ratings_dict[name][x] != 0:
sum = sum + ratings_dict[name][x]
numR += 1
x = x + 1
if numR is 0:
ratings[i] = 0
if sum != 0:
ratings[i] = float(sum) / float(numR)
return ratings
Output:
1
Traceback (most recent call last):
File "C:\Users\Collin\Dropbox\Python Files\main.py", line 106, in <module>
main()
File "C:\Users\Collin\Dropbox\Python Files\main.py", line 103, in main
print calculate_average_rating(ratingsDict)
File "C:\Users\Collin\Dropbox\Python Files\main.py", line 10, in calculate_average_rating
if ratings_dict[name][x] != 0:
IndexError: list index out of range

If you're getting an index error but don't "believe" that you should be, just print out the list and you will see the problem.
a = [0]
Imagine you didn't know what a was and then ran this code:
for i in range(2):
try :
x = a[i]
except IndexError:
print('IndexError, list = ' + str(a) + ', index = ' + str(i))
Then you would see
IndexError, list = [0], index = 1
and so the problem is clear. If the list is too long to print nicely, you can simply print the length instead of the list.

As others have already pointed out, it is very hard to say what is causing the IndexError without looking at ratings_dict. Most likely, the error is happening because the list containing the ratings has different length for different users. You are calculating the length using len(ratings_dict[Names[0]]), which is the length of the list corresponding to the first (i.e. 0th user). If the second user's list has a shorter length, then you will get an IndexError.
In more detail, here's what I mean. Suppose your ratings_dict is as follows:
ratings_dict = {"martin" : [1, 4, 3, 4, 5],
"tom" : [0, 1, 2, 1, 5],
"christina" : [0, 0, 2, 2, 3],
}
and we use the following function (I basically simplified your function a little bit)
def calculate_average_rating(ratings_dict):
names = [key for key in ratings_dict]
numBooks = len(ratings_dict[names[0]])
ratings = []
for i in range(numBooks):
total = 0
for name in names:
if ratings_dict[name][i] != 0:
total = total + ratings_dict[name][i]
ratings.append(float(total) / len(names))
return ratings
then we get no IndexError.
calculate_average_rating(ratings_dict)
[0.3333333333333333,
1.6666666666666667,
2.3333333333333335,
2.3333333333333335,
4.333333333333333]
However, if we Christina has reviewed an additional book and ratings_dict looks as follows:
ratings_dict = {"martin" : [1, 4, 3, 4, 5],
"tom" : [0, 1, 2, 1, 5],
"christina" : [0, 0, 2, 2, 3, 6],
}
then running the function gives an IndexError:
calculate_average_rating(ratings_dict)
---------------------------------------------------------------------------
IndexError Traceback (most recent call last)
<ipython-input-23-1ad9c374e8d8> in <module>()
----> 1 calculate_average_rating(ratings_dict)
<ipython-input-14-080a1623f225> in calculate_average_rating(ratings_dict)
6 total = 0
7 for name in names:
----> 8 if ratings_dict[name][i] != 0:
9 total = total + ratings_dict[name][i]
10
IndexError: list index out of range
It happens because the lists of different users do not have equal length.
Slightly off topic, it is not considered good practice to use capital letters to start variable names, because it can be confused for being a Class. See PEP8 for more details. Therefore, consider changing Names to names.
Secondly, you shouldn't use is not for doing comparisons. In this particular case, it won't bite you. But in some cases, it will. For example:
>>> a = 1000
>>> b = 1000
>>> a is b
False
The is operator compares whether a and b are the same objects, and not their values. In this case, a and b are different objects with the same value, therefore a is b evaluates to False.

Related

Python: How can I use a String for a If-Statement?

In Python I have to build a (long) if statement dynamically.
How can I do this?
I tried the following test code to store the necessary if-statement within a string with the function "buildFilterCondition".
But this doesn´t work...
Any ideas? What is going wrong?
Thank you very much.
Input = [1,2,3,4,5,6,7]
Filter = [4,7]
FilterCondition = ""
def buildFilterCondition():
global FilterCondition
for f in Filter:
FilterCondition = FilterCondition + "(x==" + str(f) +") | "
#remove the last "| " sign
FilterCondition = FilterCondition[:-2]
print("Current Filter: " + FilterCondition)
buildFilterCondition()
for x in Input:
if( FilterCondition ):
print(x)
With my Function buildFilterCondition() I want to reach the following situation, because the function generates the string "(x==4) | (x==7)", but this doesn´t work:
for x in Input:
if( (x==4) | (x==7) ):
print(x)
The output, the result should be 4,7 (--> filtered)
The background of my question actually had a different intention than to replace an if-statement.
I need a longer multiple condition to select specific columns of a pandas dataframe.
For example:
df2=df.loc[(df['Discount1'] == 1000) & (df['Discount2'] == 2000)]
I wanted to keep the column names and the values (1000, 2000) in 2 separate lists (or dictionary) to make my code a little more "generic".
colmnHeader = ["Discount1", "Discount2"]
filterValue = [1000, 2000]
To "filter" the data frame, I then only need to adjust the lists.
How do I now rewrite the call to the .loc method so that it works for iterating over the lists?
df2=df.loc[(df[colmHeader[0] == [filterValue[0]) & (df[colmHeader[1]] == filterValue[1])]
Unfortunately, my current attempt with the following code does not work because the panda-loc function has not to be called sequentially, but in parallel.
So I need ALL the conditions from the lists directly in the .loc call.
#FILTER
colmn = ["colmn1", "colmn2", "colmn3"]
cellContent = ["1000", "2000", "3000"]
# first make sure, the lists have the same size
if( len(colmn) == len(cellContent)):
curIdx = 0
for curColmnName in colmn:
df_columns= df_columns.loc[df_columns [curColmnName]==cellContent[curIdx]]
curIdx += 1
Thank you again!
Use in operator
Because simple if better than complex.
inputs = [1, 2, 3, 4, 5, 6, 7]
value_filter = [4, 7]
for x in inputs:
if x in value_filter:
print(x, end=' ')
# 4 7
Use operator module
With the operator module, you can build a condition at runtime with a list of operator and values pairs to test the current value.
import operator
inputs = [1, 2, 3, 4, 5, 6, 7]
# This list can be dynamically changed if you need to
conditions = [
(operator.ge, 4), # value need to be greater or equal to 4
(operator.lt, 7), # value need to be lower than 7
]
for x in inputs:
# all to apply a and operator on all condition, use any for or
if all(condition(x, value) for condition, value in conditions):
print(x, end=' ')
# 4 5 6

Python percentage function not working as intended

For a college project I need to output of the number of votes + the percentage of votes out of the total votes for each team that (input)(there are six in total).
I made the program using lists, and got to the part where i made a list with 7 elements: the total number of votes the program registered + the votes consecutively each team got.
I then use this list to run a function that changes the values of the indexes of the list to their percentage, with another function working as a percentage calculator. (Called 'porcentagem' that I tested out and works as intended.)
def porcentagem(p, w):
pc = 100 * float(p)/float(w)
return str(pc) + "%"
def per(list):
listF = [0,0,0,0,0,0,0]
for x in list[1:7]:
if x != 0:
listF[x] = porcentagem(x, list[0])
else:
listF[x] = 0
return listF
For some reason when I input the votes, the results come all out of order. For example:
The list input is List = [6, 3, 2, 1, 0, 0, 0,] but the output is [0, '16.666666666666668%', '33.333333333333336%', '50.0%', 0, 0, 0] (Index 0 is the total if it wasn't clear, and
I have no idea what could be causing this, its changing the orders of the elements apparently (its supposed to come out as 50%, then 33,3...% etc..)
I'm 'new' at programming + spent two months not coding anything + english is not my first language and I'm learning python in portuguese, sorry if it looks obvious lol
The x in for x in list[1:7]: returns the actual value, not the index. So x will be: 3, 2, 1, 0, 0, 0. That means the first listF[x] is listF[3] which is assigning to the 4th element.
A word of caution: list is a constructor for a built-in function, so if you use list as a variable, it might have unintended consequences. Change it to something like percentage_list.
Do something like the following:
def per(per_list):
listF = [0,0,0,0,0,0,0]
for i in range(1, len(per_list)):
x = per_list[i]
if x != 0:
listF[i] = porcentagem(x, per_list[0])
else:
listF[i] = 0
return listF
Output: [0, '50.0%', '33.333333333333336%', '16.666666666666668%', 0, 0, 0]

Finding element on index position of a list which has same elements

Here loopid1 becomes 1 and when the it's index is found out everytime it gives 0 as the list is made of 1. What I want to do is after every iteration it gives the index value as 0, 1, 2 but everytime I get 0,0,0.
LoopIdlist = [1, 4,8]
BendLoopId1List = [1, 1, 1]
indexoffacel = -1
bendcounter = -1
for bendata in BendList:
bendcounter = bendcounter+1
loopid1=BendLoopIdiList[bendcounter]
print("loopidl: + str(loopid1))
if(loopidi in LoopIdlist):
indexoffacel = LoopIdlist.index(loopidl)
print("indexoffacel: " + str(indexoffacel))
The list index method always gives the first found position. An easy trick to solve this can be: reversed(LoopIdlist).index(what_you_are_looking_for)

How to find a certain amount of same adjacent elements in a row of a nested list

There is a hotel e.g. of size 7x5. I need to create a function where
a number is given as parameter for finding the amount of consecutive empty rooms
returns the number of floor and room number in that.
(depicted below: 0 is empty room and 1 is full)
e.g.:
if the parameter is 1, output will be
"floor no: 5, start from room no: 1"
if the parameter is 2, output will be
"floor no: 5, start from room no: 3"
if the parameter is 3, output will be
"floor no: 5, start from room no: 3"
if the parameter is 4, output will be
"floor no: 4, start from room no: 4"
if the parameter is 5, output will be
"floor no: 2, start from room no: 1"
if the parameter is 6 (or 7), output will be
"floor no: 1, start from room no: 1"
if the parameter is > 7, output will be
"not possible to find in one floor"
preferably without using itertools.grupby.
My try:
def adjacent_rooms (amount):
nested_list_temp = [[0]*7]*5
nested_list = [list(i) for i in nested_list_temp]
nested_list [1][5] = 1
nested_list [2][3] = 1
nested_list [2][4] = 1
nested_list [3][2] = 1
nested_list [4][1] = 1
nested_list [4][5] = 1
# [[0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 1, 0], [0, 0, 0, 1, 1, 0, 0], [0, 0, 1, 0, 0, 0, 0], [0, 1, 0, 0, 0, 1, 0]]
try:
for i in range(len(nested_list), 0, -1):
for j in range(len(nested_list[0])):
if nested_list[i-1][j] == 0:
count += 1
if count == amount:
return (i, j-amount+2)
else:
count = 0
except:
return "not possible to find in one floor"
Any effective hints or suggestions will be highly appreciated.
Use narrow try-except conditions. The first problem is your all-encompassing
error catching, which hides a wide variety of failures -- including outright
code bugs, such as your failure to initialize count. So the first thing I did
was to delete the try-except. You don't really need it here. And even if you
did, you want to declare one or more specific exceptions in the except clause
rather than leaving it wide open.
Work within Python's list indexing as long as possible. It seems that you
want to return human-oriented floor/room numbers (starting at 1) rather
than computer-oriented numbers (starting at 0). That's fine. But defer the
computer-to-human conversion as long as possible. Within the guts of your
algorithmic code, work with Python's indexing scheme. In your case, your
code straddles both, sometimes using 1-based indexing, sometimes 0-based.
That's confusing.
You are resetting count too often. It should be set whenever the room
is full. But you are resetting it whenever count does not equal amount.
As a result, count is almost always being reset to zero.
You are also resetting count too infrequently. It must be reset at the
start of each new floor.
If we make those changes, we get this:
def adjacent_rooms(nested_list, amount):
for i in range(len(nested_list), 0, -1):
count = 0
for j in range(len(nested_list[0])):
if nested_list[i-1][j] == 0:
count += 1
if count == amount:
return (i, j-amount+2)
else:
count = 0
Python lists are directly iterable. As a result, you almost never
need to mess around with list indexes and range() to process list data.
Just iterate directly to access the values. And for those cases where
you need both the value and the index, use enumerate().
Use more declarative variable names. Names like hotel, floor, and
room help the reader understand your code.
Return data, not textual messages. If a function returns a tuple of
integers upon success, what should it do upon non-serious failure? It depends
on the context, but you can either raise an exception or return some variant of
None. In your case, I would probably opt for a parallel tuple: (None, None). This allows the caller to interact with the function in a fairly
natural way and then simply check either value for None. But returning a
textual message is quite unhelpful for callers: the returned data bundle has a
different outer structure (string vs tuple), and it has a different inner data
type (string vs int).
Don't depend on global variables. Pass the hotel data into
the function, as a proper argument.
If we make those changes, we get something like this:
def adjacent_rooms(hotel, wanted):
for fi, floor in enumerate(hotel):
n = 0
for ri, room in enumerate(floor):
if room == 0:
n += 1
if n == wanted:
return (fi + 1, ri - n + 2)
else:
n = 0
return (None, None)
I'm getting the desired output with slightly different indentation:
nested_list_temp = [[0]*7]*5
nested_list = [list(i) for i in nested_list_temp]
nested_list [1][5] = 1
nested_list [2][3] = 1
nested_list [2][4] = 1
nested_list [3][2] = 1
nested_list [4][1] = 1
nested_list [4][5] = 1
def adjacent_rooms(amount):
for i in range(len(nested_list), 0, -1):
count = 0
for j in range(len(nested_list[0])):
if nested_list[i-1][j] == 0:
count += 1
if count == amount:
return (i, j-amount+2)
else:
count = 0
return "not possible to find in one floor"
Another solution:
def adjacent_rooms(nested_list, amount):
to_search = "0" * amount
for floor in range(len(nested_list) - 1, -1, -1):
try:
idx = "".join(map(str, nested_list[floor])).index(to_search)
return "floor no: {}, start from room no: {}".format(
floor + 1, idx + 1
)
except ValueError:
continue
return "not possible to find in one floor"
nested_list = [[0 for _ in range(7)] for _ in range(5)]
nested_list[1][5] = 1
nested_list[2][3] = 1
nested_list[2][4] = 1
nested_list[3][2] = 1
nested_list[4][1] = 1
nested_list[4][5] = 1
for f in range(1, 10):
print("f={}, result: {}".format(f, adjacent_rooms(nested_list, f)))
Prints:
f=1, result: floor no: 5, start from room no: 1
f=2, result: floor no: 5, start from room no: 3
f=3, result: floor no: 5, start from room no: 3
f=4, result: floor no: 4, start from room no: 4
f=5, result: floor no: 2, start from room no: 1
f=6, result: floor no: 1, start from room no: 1
f=7, result: floor no: 1, start from room no: 1
f=8, result: not possible to find in one floor
f=9, result: not possible to find in one floor

removing an IndexError on python

To start off, I'm basically a beginner in python and coding since its the first programming language that I've learned so if y'all respond could you please simplify what you're trying to say, thanks.
So I have a code that keeps outputing an IndexError: list index out of range error message and yet it also gives me the output I'm looking for.
Here's the code:
def read_voltages(f_name):
f = open(f_name, "r")
# list comprehension
# inside np.array(), builds nested lists, separating each nested list by " " and \n for each line it reads
data = np.array([line.strip("\n").split(" ") for line in f.readlines()])
data_t = data.T
return data_t
def rms(v, N):
RMS = np.sqrt((1 / N) * ((v**2).sum()))
return RMS
f_name = input("Enter file name(eg. file1.txt): ")
data_t = read_voltages(f_name)
# list comprehension ~ converts strings in original output to integer values
# inner comp builds list of int from sequence of valid objects in row
# outer comp builds list of results of inner comp applied to each term in data_t
int_list = [[int(column) for column in row] for row in data_t]
v = int_list
print("Voltage values: \n", v)
print()
size = len(v[0])
for i in range(size):
if size > 0:
N = size
rms_val = rms(np.array(v[i]), N)
print(rms_val)
elif size == 0:
print(None)
And here's the output:
Enter file name(eg. file1.txt): voltages_test.txt
Traceback (most recent call last):
File "C:\Users\amand\Documents\Voltages.py", line 79, in <module>
rms_val = rms(np.array(v[i]), N)
IndexError: list index out of range
Voltage values:
[[1, 2, 3, 4, 5, 6, 7, 8, 9, 10], [2, 2, 3, 4, 5, 6, 7, 8, 9, 10], [3, 2, 3, 4, 5, 6, 7, 8, 9, 10]]
6.2048368229954285
6.228964600958975
6.268971207462992
Process finished with exit code 1
For reference heres the .txt file being used:
1 2 3
2 2 2
3 3 3
4 4 4
5 5 5
6 6 6
7 7 7
8 8 8
9 9 9
10 10 10
Like I said before, the code outputs what I want, but it gives me the IndexError and I don't want that since I need to add more code to finish the program I'm working on. When I removed the [i] in rms_val = rms(np.array(v[i]), N) it gave me a repeated set of 10 identical numbers which were calculated incorrectly. I'm not really sure what else to move around and am quite stuck on this :/.
If anyone can help that'd be great, thanks.
edit: The code is supposed to take in values for "v" which are taken in from a transposed list that comes from a user inputted .txt file. Then, it uses the v values to calculate the root-mean-square and outputs the values in a table of sorts, but that part I can manage.
You are defining your loop variable according to the length of v[0]
size = len(v[0])
Later in the code you use this variable to loop through v
rms_val = rms(np.array(v[i]), N)
The length of v len(v) is shorter than the len of v[0] len(v[0]) thus you are getting the error.
Make sure that you use the appropriate length when looping through your data.

Categories