How to change the index in a list of lists - python

I would like to change the way a list of lists in indexed.
Suppose my initial list is two lists of one list and two lists of three elements. For example:
L = [[[1, 2, 3]], [[4, 5, 6], [7, 8, 9]]]
Then let say I want to take '4' in L, I must do
L[1][0][0].
Now I'd like to create a new list such that the last indexing become the first one
Lnew = [[[1], [4, 7]], [[2], [5, 8]], [[3], [6, 9]]]
And then for taking '4' I have to do:
Lnew[0][1][0]
In a more general case, I'd like to create the list Lnew defined by:
Lnew[i][k][l] = L[k][l][i]
Is there a way to do this kind of permutation of the index without doing the following loops:
Lnew = []
for i in range(len(Payment_dates)):
L1 = []
for k in range(N+1):
L2 = []
for l in range(k+1):
L2.append(L[k][l][i])
L1.append(L2)
Lnew.append(L1)
Which is not very optimal in term of complexity.
Thanks

What you'd like to achieve, presupposes that all sublists have the same length.
If they have differing lengths, you may wish to append zeros to all sublists until they have the length of the longest sublist or (which is easier) until infinity.
The same behaviour can be achieved by using a function to access the elements of the list. You can call this function during runtime every time you need an element of the list:
def getElement(myList, i, k, l):
if k < myList.length and l < myList[k].length and i < myList[k][l].length:
return myList[k][l][i]
else:
return None # or zero, or whatever you prefer
Depending on your code structure, you might not need this as a function and you can just put the conditions inside of your code.
You can also nest the if-conditions and throw different errors or return different values depending on what level the element does not exist.
If we neglect the time complexity of outputting a multidimensional list's element, this approach should decrease your time complexity from O(n^3) to O(1).

Related

Given multiple lists, how to find the values between neighboring lists?

I have multiple lists of increasing numbers. The elements in each list are strictly greater than those in its previous neighbor. i.e. [1,2,3], [6,7,8], [10,11,12].
How to find the numbers between neighboring lists? In this case, the results would be [4,5], [9].
If there are only two lists, I can use something like
a = [1,2,3]
b = [4,5,6]
result = list(range(a[-1]+1,b[0]))
but I can't think of a simple and fast way to construct a loop to do this if I have more than two lists.
Assuming that the lists are sorted and that the elements in each list are strictly increasing you can use a loop to iterate over the indices of the lists and uses the range() function to generate a list of numbers between the last element of the current list and the first element of the next list. The results are appended to the results array:
def find_gaps(lists):
gaps = []
for i in range(len(lists) - 1):
gap = list(range(lists[i][-1] + 1, lists[i + 1][0]))
gaps.append(gap)
return gaps
lists = [[1, 2, 3], [6, 7, 8], [10, 11, 12]]
print(find_gaps(lists))
The resulting list of arrays will contain the numbers between neighboring lists: [[4, 5], [9]]. The complexity of the find_gaps() function is O(n), where n is the number of lists.
You can try the following:
a,b,c=[1,2,3], [6,7,8], [10,11,12]
c=[a,b,c]
result=[]
for i in range(len(c)-1):
result.append(list(range(c[i][-1]+1,c[i+1][0])))
print(result)

Basic question about replacing entries in list of lists according to if condition

How can I change this code so the I replace the elements for a different if condition?
In the code below I change replace the 3 with 5.
But what if the wanted to replace the entry to 5 if any sub-list entry is larger than 30?
Example: If L[i][j] > 30 I get [[3,3],[5,5],[5,1], [5,8]]
L = [[3,3],[444,1111],[45,1], [90,8]]
for i, x in enumerate(L):
for j, a in enumerate(x):
if 3 in a:
L[i][j] = a.replace(3, 5)
****** Disclaimer *******:
The example was taken from this enclosed question for the sake of convenience. My aim in this question is to understand how to use the indexes so it fits a different type of if condition.
Replace elements in a list of lists python
Quick and dirty answer:
L = [[3,3],[444,1111],[45,1], [90,8]]
L = [[5 if value < 30 else 5 for value in list ] for list in L]
print(L)
out:
[[3, 3], [5, 5], [5, 1], [5, 8]]
Better way using a function
If you want to replace the values then it's better to create a simple function as shown below. You seem to have copied the code from somewhere else, so I will try my best to explain to you what is going on below.
Our original list is shown below:
L = [[3,3],[444,1111],[45,1], [90,8]]
The function below is defined to accept 3 values, the first value is the list to be manipulated, the second is the condition, as in replace if number is > condition. The third value is the new value to replace the values that satisfy the condition.
def replace_greater_than(List,condition = 30,new_value = 5):
L = [[value if value < condition else new_value for value in list ] for list in List]
Here we are using a concept know as nested list comprehension. Start by reading it from right to left. We are looping through every list in List (notice the case), please note that "list" in the code can be replaced with any variable name that you want, but i just called it "list" to improve readability.
So now we are looping through our initial list and we are retrieving the inner lists with each iteration.Then we perform list comprehension again on the inner lists that we just retrieved, so now we are looping on the values in the list themselves.
Finally in the inner list comprehension we set a condition, which is passed on by the user, if the value is less than the set condition or threshold then return the same value else return the specified new value.
How to use the function:
Example: here we place the condition as 30 and the value as 5
replace_greater_than(List = L, condition = 30, value = 5)
out:
[[3, 3], [5, 5], [5, 1], [5, 8]]
Example: We can also call the function without passing any values to the parameters because we already set the default values of our function earlier; condition = 30, new_value = 5.
replace_greater_than(List = L)
out:
[[3, 3], [5, 5], [5, 1], [5, 8]]
Example: Finally, we can also pass custom values to our function. The function below will replace all the values greater than 100 with the new_value of 23
replace_greater_than(List = L, condition = 100, new_value = 23)
out:
[[3, 3], [23, 23], [45, 1], [90, 8]]
Here are some sources to help you get started with learning python:
Great free Course covering all the basics by Microsoft
List Comprehension
Functions in python
Some channels on Youtube that I recommend:
Real Python
Corey Schafer
DataCamp
Basically you have a loop through all elements, then you have condition and action when condition is met.
for i, x in enumerate(L): #loop
for j, a in enumerate(x): #loop
if 3 in a: #condition
L[i][j] = a.replace(3, 5) #action
You want to change condition to if L[i][j] > 30 (already in your question), and you want to change action to replace or reassign entry, which is straightforward L[i][j] = 5.
In above code a is L[i][j], so you can write condition a bit differently.
I will let you modify code yourself and test. You don't need to use list comprehension at this point.
Thea easy way to do it is to iterate over each item in each list and change items via an 'if' statement.
Create a statement which will return either the item it is given, or 3 if the number is equal to 5.
replace = lambda i: 3 if i==5 else i
Next, iterate through the nested lists. This can be written out in two ways, via list comprehension or as nested for statements.
l = [[replace(l2)for l2 in l1] for l1 in l]
for l1 in l:
for l2 in l1:
l2 = replace(l2)
When we run the code we get:
print (l)
>>> [[3, 3], [444, 1111], [45, 1], [90, 8]]
To make the code change any items <30 we can change the if statement we made at the start.
replace = lambda i: 3 if i<30 else i
Printing the new code gives:
print (l)
>>> [[3, 3], [444, 1111], [45, 3], [90, 3]]
Note, using x.replace() works for strings not integers, you can play with int() and str() if you need to use .replace().

Python. Greedy algorithm, 2 for loops iterating over lists

I am trying to write a greedy algorithm where I have a constraint on how many items I can fit into a box and I have to fit as many items in a box as possible before moving on to the next one (ie maximizing the weight of each box).
I've been trying to solve this by creating two identical lists, let's say a_list and b_list.
a_list = [9, 8, 6, 4, 3, 2, 2, 2]
b_list = [9, 8, 6, 4, 3, 2, 2, 2]
The constraint on each box is 10 here, so for example I can only fit the first item (9) into one before moving onto the next box. The following box should contain 8 + 2.
Each box is a list within the main list ie
list_ = [[9], [8,2],[6,4].....]
I can only move on to next box once the current one cannot have further items fitted into it.
When I am trying iterate through the two lists I don't know how to delete items to avoid them appearing multiple times in list_.
I'm close but I have a couple of items coming up twice while one doesn't come up at all.
It is also the case that despite my sorting the lists in descending order, not all my boxes are optimal, one of them only has one item with value '2' in it. I know it's to do with the loop but I don't understand why it's not going through the items in descending order.
limit = 10
list_ = [[]]
for i in a_list:
for j in b_list:
if sum(l[-1]) + i + j <= limit:
l[-1].append(i)
l[-1].append(j)
b_list.remove(j)
elif sum(l[-1]) + j <= limit:
l[-1].append(j)
b_list.remove(j)
else:
l.append([])
The only reason I think that you're using an a_list and a b_list is that you assume you need to pick two items per box, which need not be the case.
I think you should use a single list, and use a list index based approach to track which items are added.
You also will have issues with deletes, so try setting items that are added to -1 and filter them out after each pass, to avoid confusions with deletes while looping.
I'm resisting sharing the solution code here, but ping me if you need it.
Changing a list as you iterate over it is always a challenge. Here is one solution that uses a while loop, which I generally don't endorse, but it is a simple enough algorithms that it should work here with no issues.
The while loop checks if there are any elements left in the initial list. It then pops (removes and saves) the first element of the list and iterates over the rest of the list looking for additional elements that satisfy the condition of the summing to less than the constraint. If an element is found it is appended to sub-list and its index is recorded. At the end of the for loop the sub list is append to the output list and then recorded indices are removed in the reverse order.
a_list = [9, 8, 6, 4, 3, 2, 2, 2]
constraint = 10
out = []
while a_list:
# grab first element of a_list and reset the list of
# to pop from a_list to pop from a_list
sub_out = [a_list.pop(0)]
pop_list = []
for i,a in enumerate(a_list):
if a+sum(sub_out) <= constraint:
sub_out.append(a)
pop_list.append(i)
# append the sub_list to the output list
out.append(sub_out)
# remove each item in the pop_list in the reverse order
for i in reversed(pop_list):
a_list.pop(i)
#output:
>>> out
[[9], [8, 2], [6, 4], [3, 2, 2]]

Creating a tuple with n entries for n lists

If I have a function that creates a random number of lists and I want the final result to return a tuple containing those lists - is it at all possible to do this?
Since tuples are immutable and the number of elements in a tuple cannot be changed once it has been created, then how can one be automatically generated which contains n entries for n lists?
Unlike lists, expanding the entries of a tuple can't simply be changed with a command like .append().
EDIT: It just dawned on me that I could create a tuple with a really large number of entries e.g. tuple = (a,b,c,d,e,f,g,h, ... to infinity) to cover n lists and then pass on the values of the tuple, which contain a list, to a new tuple with the exact number of n entries but this seems like a really inefficient method.
Create a list of lists, then pass that list to the built in tuple() function:
my_lists = [[1, 2], [5, 7], [8, 9]]
my_tuple = tuple(my_lists)
print(my_tuple)
Output
([1, 2], [5, 7], [8, 9])

How to pick the smallest value in a list when an iterative process is applied to the list

I need to find the smallest value in a series of lists. I understand the code for the smallest value in just one list:
>>> x = [1, 2, 3, 4, 5]
>>> print (min(x))
1
Simple enough. However, I would like to know if there is a way to write code that finds the smallest value for each list I have without stopping and adjusting the code (whether by an iterative process or some other means). Any help would be greatly appreciated. Thanks in advance!
First, make a list of lists out of your separate lists. For example, if you have lists A, B and C, the list of lists would be [A,B,C].
Now, to get a list of all the minimum values for each list in a list of lists lst:
[min(x) for x in lst]
To get the global minimum:
min(x for sublist in lst for x in sublist)
Demo:
>>> lst
[[1, 2, 3], [4, 5, 6], [7, 8, 9]]
>>> [min(x) for x in lst]
[1, 4, 7]
>>> min(x for sublist in lst for x in sublist)
1
Edit in reaction to OP's comment:
I just want the minimum value for each list. I don't need to compile all of the minimum values together into a new list
If you just want to print the minimum values for each list A, B, C, ... , you can do:
for lst in (A,B,C): # put as many lists as you like into the parentheses
print(min(lst))
Edit in reaction to
I am only accessing one list (in this case, the values are well depths) at a time. [..] What I would like to know is how to write a code that finds the smallest value in a list given that the iterator essentially changes the values in said list.
Just print(min(lst)) each time lst has changed.
Assuming you've got a list of lists, this should do it:
minimums = [min(l) for l in lists]

Categories