Simple List Error Python - python

In the following code, I created a list of lists, like an array. I began getting a "list assignment out of range" error. As a workaround for this error, I added 2 extra instances of diceSumTable, is you can see in the code. It will print now, but it is preceded by "2, 3". In my studying, I can't recall any reason why this would be happening if every instance of diceSumTable is already defined.
EDIT: Here was the original code, without applying the workaround.
def dice():
diceSumTable = [2,3,4,5,6,7,8,9,10,11,12]
diceSumTable[2] = [(1,1)]
diceSumTable[3] = [(1,2),(2,1)]
diceSumTable[4] = [(1,3),(2,2),(3,1)]
diceSumTable[5] = [(1,4),(2,3),(3,2),(4,1)]
diceSumTable[6] = [(1,5),(2,4),(3,3),(4,2),(5,1)]
diceSumTable[7] = [(1,6),(2,5),(3,4),(4,3),(5,2),(6,1)]
diceSumTable[8] = [(2,6),(3,5),(4,4),(5,3),(6,2)]
diceSumTable[9] = [(3,6),(4,5),(5,4),(6,3)]
diceSumTable[10] = [(4,6),(5,5),(6,4)]
diceSumTable[11] = [(5,6),(6,5)]
diceSumTable[12] = [(6,6)]
#for illustrative purposes
for i in diceSumTable:
print i
dice()

As said, you start indexing the diceSumTable from index #2 onward, leaving entries 0 and 1 untouched. The error you got was because you were indexing past the end of the array.
For your problem a "dict" might be a better solution:
diceSumTable = {}
diceSumTable[ 2 ] = [(1,1)]
diceSumTable[ 3 ] = [(1,2), (2,1)]

Try this:
def dice():
diceSumTable = [] # define diceSumTable as a list
diceSumTable.append((1,1)) # append the tuple to diceSumTable
diceSumTable.append(((1,2),(2,1))) # append a tuple of tuples to diceSumTable
diceSumTable.append(((1,3),(2,2),(3,1)))
diceSumTable.append(((1,4),(2,3),(3,2),(4,1)))
diceSumTable.append(((1,5),(2,4),(3,3),(4,2),(5,1)))
diceSumTable.append(((1,6),(2,5),(3,4),(4,3),(5,2),(6,1)))
diceSumTable.append(((2,6),(3,5),(4,4),(5,3),(6,2)))
diceSumTable.append(((3,6),(4,5),(5,4),(6,3)))
diceSumTable.append(((4,6),(5,5),(6,4)))
diceSumTable.append(((5,6),(6,5)))
diceSumTable.append(((6,6)))
#for illustrative purposes
for i in diceSumTable:
print i
dice()
You got 2, 3 because that's what you entered into the list in the first to places, the other numbers you put in got replaced with statements like diceSumTable[x] =.

You are entering data from 2 index (means third element in array).
diceSumTable = [2,3,4,5,6,7,8,9,10,11,12,13,14]
when you do
# Replacing third element
diceSumTable[2] = [(1,1)]
diceSumTable will be like
diceSumTable = [2,3,[(1,1)],5,6,7,8,9,10,11,12,13,14]
Slly it will replace all values.

You are confusing the value of an entry in a list with the index of a list.
diceSumTable[3] corresponds to the fourth entry in diceSumTable (since they are numbered from 0).
Your first line creates a list diceSumTable with 13 entries, numbered 0...12.
Your next set of lines fills in the 3d through 13th entries (and throws away what was there before!).
To do what you want, you have a few choices.
1/ You can do what you're doing, but ignore the first two entries. This is not very pythonic...
2/ You can create a length 11 list, holding the actual entries. In this case, the most efficient way to do it is
diceSumTable = [] ### empty list
diceSumTable.append([(1,1)])
diceSumTable.append([(1,2),(2,1)])
#### etc.
3/ You can use a dict. This is probably closest to what you want, although it's slightly inefficient in space and time, since you just want consecutive integer keys (but premature optimisation is the root of all evil):
diceSumTable = {} ### empty dict
diceSumTable[2] = [(1,1)]
diceSumTable[3] = [(1,2),(2,1)]
#### etc.
(Markdown question: is there any way to intersperse code within a bulleted or enumerated list?)

When you set diceSumTable[2] you are replacing the third value in the list (lists are zero indexed - first value is name[0]) not the value that currently holds 2.
So after the first call you have diceSumTable equal to [2,3,[(1,1)],5,6,...].
I think what you could do is, as mentioned elsewhere, use diceSumTable = [] then diceSumTable.append((1,1)) for each dice combination.
You could also use a dictionary.
diceSumTable = {}
diceSumTable[2] = [(1,1)]
diceSumTable[3] = [(1,2),(2,1)]
#etc
You could then access by value rather than position.
>>>diceSumValue[3]
[(1,2),(2,1)]
>>>

You should index the list by it's indices, not the values:
In [123]: lst = [3, 4, 5]
In [124]: lst[0] == 3 #item "3" is at index 0
Out[124]: True
In [125]: lst[3] #out of range
---------------------------------------------------------------------------
IndexError Traceback (most recent call last)
<ipython-input-125-48d1ca706e8c> in <module>()
----> 1 lst[3] #out of range
IndexError: list index out of range
In [126]: lst[1] = 10 #if I want to change "4" in the list to "10"
In [127]: lst
Out[127]: [3, 10, 5]

I am not shure of what result do you except from this code, what i understood is that you want to code to print :
[(1, 1)]
[(1, 2), (2, 1)]
[(1, 3), (2, 2), (3, 1)]
[(1, 4), (2, 3), (3, 2), (4, 1)]
[(1, 5), (2, 4), (3, 3), (4, 2), (5, 1)]
[(1, 6), (2, 5), (3, 4), (4, 3), (5, 2), (6, 1)]
[(2, 6), (3, 5), (4, 4), (5, 3), (6, 2)]
[(3, 6), (4, 5), (5, 4), (6, 3)]
[(4, 6), (5, 5), (6, 4)]
[(5, 6), (6, 5)]
[(6, 6)]
In this case, i think the error you are making is believing that the following line :
diceSumTable = [2,3,4,5,6,7,8,9,10,11,12,13,14]
Give you a list where index start from 2 and finish at 14, which is wrong because in Python every list index start at 0, and there is no way you can change that. The line you give me, actually create a list where the first index is 0 and the last index is 12 (size of the list - 1). And you list is such that diceSumTable[0] is 2 diceSumTable[1] is 3, etc.
This lead you to two options, either accept that list start at index 0, and if you want to keep using the mapping you want (i guess there is a reason for that, you surely want to associate 2 with (1,1), 3 with (1,2), (2,1)) just use diceSumTable[theNumberOfYourMapping -2]. Or as say haavee, you can use dict for that. But in this case, when you will iterate over your dict, you won't have you value is the write order. If there is no gap between you're wanted index i will go with the map.
I think it will be great if you could explain use a bit more what you want to do, why do you want 2 and not 0 to be associated to [(1,1)]. Do you want to do something else with this lis t than printing it. To sum up here is the code, i would have written, if i understood what you wanted to do :
def dice():
diceSumTable = [[(1,1)],
[(1,2),(2,1)],
[(1,3),(2,2),(3,1)],
[(1,4),(2,3),(3,2),(4,1)],
[(1,5),(2,4),(3,3),(4,2),(5,1)],
[(1,6),(2,5),(3,4),(4,3),(5,2),(6,1)],
[(2,6),(3,5),(4,4),(5,3),(6,2)],
[(3,6),(4,5),(5,4),(6,3)],
[(4,6),(5,5),(6,4)],
[(5,6),(6,5)],
[(6,6)]]
#this is like for value in diceSumTable but i will iterate to 0,1..10 in more
for (i,value) in enumerate(diceSumTable):
print str(i+2) + " is associated to " + str(value)
dice()
Moreover if you want to know more about Python list, i could read :
http://effbot.org/zone/python-list.htm
And for Python dictionary :
http://www.pythonforbeginners.com/dictionary/dictionary-manipulation-in-pythonc

Related

How to create a list of all values within a certain range from each other?

I have a list of tuples:
x = [(2, 10), (4, 5), (8, 10), (9, 11), (10, 15)]
I'm trying to compare the first values in all the tuples to see if they are within 1 from each other. If they are within 1, I want to aggregate (sum) the second value of the tuple, and take the mean of the first value.
The output list would look like this:
[(2, 10), (4, 5), (9, 36)]
Notice that the 8 and 10 have a difference of 2, but they're both only 1 away from 9, so they all 3 get aggregated.
I have been trying something along these lines, but It's not capturing the sequenced values like 8, 9, and 10. It's also still preserving the original values, even if they've been aggregated together.
tuple_list = [(2, 10), (4, 5), (8, 10), (9, 11), (10, 15)]
output_list = []
for x1,y1 in tuple_list:
for x2,y2 in tuple_list:
if x1==x2:
continue
if np.abs(x1-x2) <= 1:
output_list.append((np.mean([x1,x2]), y1+y2))
else:
output_list.append((x1,y1))
output_list = list(set(output_list))
You can do it in a list comprehension using groupby (from itertools). The grouping key will be the difference between the first value and the tuple's index in the list. When the values are 1 apart, this difference will be constant and the tuples will be part of the same group.
For example: [2, 4, 8, 9, 10] minus their indexes [0, 1, 2, 3, 4] will give [2, 3, 6, 6, 6] forming 3 groups: [2], [4] and [8 ,9, 10].
from itertools import groupby
x = [(2, 10), (4, 5), (8, 10), (9, 11), (10, 15)]
y = [ (sum(k)/len(k),sum(v)) # output tuple
for i in [enumerate(x)] # sequence iterator
for _,g in groupby(x,lambda t:t[0]-next(i)[0]) # group by sequence
for k,v in [list(zip(*g))] ] # lists of keys & values
print(y)
[(2.0, 10), (4.0, 5), (9.0, 36)]
The for k,v in [list(zip(*g))] part is a bit tricky but what it does it transform a list of tuples (in a group) into two lists (k and v) with k containing the first item of each tuple and v containing the second items.
e.g. if g is ((8,10),(9,11),(10,15)) then k will be (8,9,10) and v will be (10,11,15)
By sorting the list first, and then using itertools.pairwise to iterate over the next and previous days, this problem starts to become much easier. On sequential days, instead of adding a new item to our final list, we modify the last item added to it. Figuring out the new sum is easy enough, and figuring out the new average is actually super easy because we're averaging sequential numbers. We just need to keep track of how many sequential days have passed and we can use that to get the average.
def on_neighboring_days_sum_occurrances(tuple_list):
tuple_list.sort()
ret = []
sequential_days = 1
# We add the first item now
# And then when we start looping we begin looping on the second item
# This way the loop will always be able to modify ret[-1]
ret.append(tuple_list[0])
# Python 3.10+ only, in older versions do
# for prev, current in zip(tuple_list, tuple_list[1:]):
for prev, current in itertools.pairwise(tuple_list):
day = current[0]
prev_day = prev[0]
is_sequential_day = day - prev_day <= 1
if is_sequential_day:
sequential_days += 1
avg_day = day - sequential_days/2
summed_vals = ret[-1][1] + current[1]
ret[-1] = (avg_day, summed_vals)
else:
sequential_days = 1
ret.append(current)
return ret
You can iterate through the list and keep track of a single tuple, and iterate from the tuple next to the one that you're tracking till the penultimate tuple in the list because, when the last tuple comes into tracking there is no tuple after that and thus it is a waste iteration; and find if the difference between the 1st elements is equal to the difference in indices of the tuples, if so sum up the 2nd as well as 1st elements, when this condition breaks, divide the sum of 1st elements with the difference in indices so as to get the average of them, and append them to the result list, now to make sure the program doesn't consider the same tuples again, jump to the index where the condition broke... like this
x = [(2, 10), (4, 5), (8, 10), (9, 11), (10, 15)]
x.sort()
res, i = [], 0
while i<len(x)-1:
sum2, avg1 = x[i][1], x[i][0]
for j in range(i+1, len(x)):
if abs(x[j][0]-x[i][0]) == (j-i):
sum2 += x[j][1]
avg1 += x[j][0]
else:
res.append(x[i])
i+=1
break
else:
avg1 /= len(x)-i
res.append((int(avg1), sum2))
i = j+1
print(res)
Here the while loop iterates from the start of the list till the penultimate tuple in the list, the sum2, avg1 keeps track of the 2nd and 1st elements of the current tuple respectively. The for loop iterates through the next tuple to the current tuple till the end. The if checks the condition, and if it is met, it adds the elements of the tuple from the for loop since the variables are intialized with the elements of current tuple, else it appends the tuple from the for loop directly to the result list res, increments the while loop variable and breaks out of the iteration. When the for loop culminates without a break, it means that the condition breaks, thus it finds the average of the 1st element and appends the tuple (avg1, sum2) to res and skips to the tuple which is next to the one that broke the condition.

How to i make "rows" consiting of pairs from a list of objects that is sorted based on their attributes

I have created a class with attributes and sorted them based on their level of x, from 1-6. I then want to sort the list into pairs, where the objects with the highest level of "x" and the object with the lowest level of "x" are paired together, and the second most and second less and so on. If it was my way it would look like this, even though objects are not itereable.
for objects in sortedlist:
i = 0
row(i) = [[sortedlist[i], list[-(i)-1]]
i += 1
if i => len(sortedlist)
break
Using zip
I think the code you want is:
rows = list(zip(sortedList, reversed(sortedList)))
However, note that this would "duplicate" the elements:
>>> sortedList = [1, 2, 3, 4, 5]
>>> list(zip(sortedList, reversed(sortedList)))
[(1, 5), (2, 4), (3, 3), (4, 2), (5, 1)]
If you know that the list has an even number of elements and want to avoid duplicates, you can instead write:
rows = list(zip(sortedList[:len(sortedList)//2], reversed(sortedList[len(sortedList)//2:])))
With the following result:
>>> sortedList = [1,2,3,4,5,6]
>>> list(zip(sortedList[:len(sortedList)//2], reversed(sortedList[len(sortedList)//2:])))
[(1, 6), (2, 5), (3, 4)]
Using loops
Although I recommend using zip rather than a for-loop, here is how to fix the loop you wrote:
rows = []
for i in range(len(sortedList)):
rows.append((sortedList[i], sortedList[-i-1]))
With result:
>>> sortedList=[1,2,3,4,5]
>>> rows = []
>>> for i in range(len(sortedList)):
... rows.append((sortedList[i], sortedList[-i-1]))
...
>>> rows
[(1, 5), (2, 4), (3, 3), (4, 2), (5, 1)]

Trying to understand the double index in python

def countSmaller(self, nums):
def sort(enum):
half = len(enum) / 2
if half:
left, right = sort(enum[:half]), sort(enum[half:])
for i in range(len(enum))[::-1]:
if not right or left and left[-1][1] > right[-1][1]:
smaller[left[-1][0]] += len(right)
enum[i] = left.pop()
else:
enum[i] = right.pop()
return enum
smaller = [0] * len(nums)
sort(list(enumerate(nums)))
return smaller
I am a new python coder so this query!.. In left[-1][1] , I understood [-1] makes me think the last index but what does the second index [1] mean.
The second index does the same as the first but with the nested value.
For exemple:
a = [(1, 2), (2, 3), (3, 4)]
a[-1] # (3, 4)
a[-1][1] # 4
In your example you don't have a list with numbers but enumerate objects converted to lists
sort(list(enumerate(nums)))
It means that you have data like this:
nums = [1, 2, 3, 4, 5]
enum_list = list(enumerate(nums)) # [(0, 1), (1, 2), (2, 3), (3, 4), (4, 5)]
It seems like left is an array containing tuples. I.e. Each element of the array is a tuple.
Ex: left=[(value1oftuple1,value2oftuple1),(value1ofarray2,value2ofarray2)]
In this case left[-1][1] would reference the first value in the last element of the array (value1ofarray2).
I found this by running your code and printing the value of left just before your code calls left[-1][1]. This way you can see what data type left is.

How does `key=function` in `sorted(tuple, key=function)` work?

Given a list of non-empty tuples, return a list sorted in increasing order by the last element in each tuple.
e.g. [(1, 7), (1, 3), (3, 4, 5), (2, 2)] yields [(2, 2), (1, 3), (3, 4, 5), (1, 7)]
Hint: use a custom key= function to extract the last element form each tuple.
The solution to the problem is:
def last(a):
return a[-1]
def sort_last(tuples):
return sorted(tuples, key=last)
Can anyone help me to understand what arguments are passed to the last function? Specifically, what does a contain?
We have not passed any values or arguments while calling the last function in the sorted method.
This is what is called a "lambda".
It's passing the current element of your list to the function "last" which will then get the last element of the tuple.
So the parameter "a" is the tuple being currently processed.
Iterating through a tuple via for loop - (Sorted initial list with 2nd element of the tuple.)
Nested for loops - comparing original tuple with the sorted 2nd element list.
tuple1 = [(2, 5), (1, 2), (4, 4), (2, 3), (2, 1)]
List2 =[]
List3 =[]
for t in tuple1:
List2.append(t[1],)
List2.sort()
print(List2)
for l in List2:
for q in tuple1:
if l == int(q[1],):
List3.append(q)
print(List3)

How to select increasing elements of a list of tuples?

I have the following list of tuples:
a = [(1, 2), (2, 4), (3, 1), (4, 4), (5, 2), (6, 8), (7, -1)]
I would like to select the elements which second value in the tuple is increasing compared to the previous one. For example I would select (2, 4) because 4 is superior to 2, in the same manner I would select (4, 4) and (6, 8).
Can this be done in a more elegant way than a loop starting explicitly on the second element ?
To clarify, I want to select the tuples which second elements are superior to the second element of the prior tuple.
>>> [right for left, right in pairwise(a) if right[1] > left[1]]
[(2, 4), (4, 4), (6, 8)]
Where pairwise is an itertools recipe you can find here.
You can use a list comprehension to do this fairly easily:
a = [a[i] for i in range(1, len(a)) if a[i][1] > a[i-1][1]]
This uses range(1, len(a)) to start from the second element in the list, then compares the second value in each tuple with the second value from the preceding tuple to determine whether it should be in the new list.
Alternatively, you could use zip to generate pairs of neighbouring tuples:
a = [two for one, two in zip(a, a[1:]) if two[1] > one[1]]
You can use enumerate to derive indices and then make list comprehension:
a = [t[1] for t in enumerate(a[1:]) if t[1][1] > a[t[0]-1][1]]
You can use list comprehension
[i for i in a if (i[0] < i[1])]
Returns
[(1, 2), (2, 4), (6, 8)]
Edit: I was incorrect in my understanding of the question. The above code will return all tuples in which the second element is greater than the first. This is not the answer to the question OP asked.

Categories