while-loop problem for acess a list element - python

I want to append each element of [1,2] to [[1], [2], [3]] and as
a consequence, the final array that I want is [[1,1], [1,2], [2,1], [2,2], [3,1], [3,2]]
But my code has a mistake I couldn't recognize it yet, and the result of the python code below is [[1, 1, 2], [1, 1, 2], [2, 1, 2], [2, 1, 2], [3, 1, 2], [3, 1, 2]]
The python code:
tor=[]
arr=[1,2]
arz=[[1], [2], [3]]
each=0
while each<len(arz):
eleman=arz[each]
index=0
while index < len(arr):
k=arr[index]
eleman=arz[each]
eleman.append(k)
tor.append(eleman)
index = index+1
each=each+1

it would be eleman=arz[each].copy() as lists are mutable so every time you change an element in the original list it will get reflected in the resultant array

In this example it would be more useful to use a for loop. You can use it to loop through both lists, and append in pairs.
arr = [1, 2]
arz = [[1], [2], [3]]
tor = []
for i in arz:
for j in (arr):
tor.append([i[0], j])
print(tor)

You can use Python list comprehension to achieve this in one line:
a1 = [1, 2]
a2 = [[1], [2], [3]]
result = [[i[0], j] for i in a2 for j in a1]
print(result)
For this kind of operations don't use While loop but the for loop. It is much cleaner and simpler to use with iterables.

Related

Why does value appended to list change dynamically?

Python Version: 3.9.5
My Code:
cells = [[i, i] for i in range(3)]
steps = []
for t in range(2):
for i in range(len(cells)):
xrand = random.randint(-5, 5)
yrand = random.randint(-5, 5)
cells[i][0] += xrand
cells[i][1] += yrand
steps.append(list(cells))
print(steps)
Actual Output:
[[[3, 3], [2, 3], [6, 3]]]
[[[4, 7], [-3, 2], [8, 3]], [[4, 7], [-3, 2], [8, 3]]]
Expected Output:
[[[3, 3], [2, 3], [6, 3]]]
[[[3, 3], [2, 3], [6, 3]], [[4, 7], [-3, 2], [8, 3]]]
I am changing the value of the list cells every time in the outer loop and appending the modified list to the steps list. As you can see in the output, in the second iteration, the first element of cells is also modified even though I am only appending an element to the list.
The first element of steps is the cells list from the previous iteration. But in the second iteration, both elements dynamically change even though I am only appending an element and so the first element shouldn't be affected.
Why does this happen and how do you overcome this?
list(cells) creates a shallow copy, thus modifying the mutable content of cells will also mutate the contents of the shallow copy - or rather, the content of the shallow copy is really the same object as the content of cells.
What you want is a deep copy:
import copy
cells = [[i, i] for i in range(3)]
steps = []
for t in range(2):
for i in range(len(cells)):
xrand = 1
yrand = 1
cells[i][0] += xrand
cells[i][1] += yrand
steps.append(copy.deepcopy(cells))
print(steps)
Output:
[[[1, 1], [2, 2], [3, 3]]]
[[[1, 1], [2, 2], [3, 3]], [[2, 2], [3, 3], [4, 4]]]
I have replaced the random.randint parts with static values, so the demonstrated output is less ... random.
Lists are manipulated in place
This is because the list is manipulated with a reference to the real object (storage location) on the disk. See the following example:
l = [2]
x = [l, l]
print(x)
>>> [[2], [2]]
l[0] = 4
print(x)
>>> [[4], [4]]
You can overcome this by using either a new variable or a copy.deepcopy()
>>> import copy
>>> l = [2]
>>> x = [copy.deepcopy(l), l]
>>> print(x)
[[2], [2]]
>>> l[0] = 4
>>> print(x)
[[2], [4]]

get the first and the 2nd and the last element in array python

hi my question is very simple
i have array like this
list=[3,2,4,1]
i want to get the first element which is --> 3
and the second element which is --> 2
and the last or fourth element which is --> 1
output = [3,2,1]
also i want the
output =[3,4,1]
without using this method
output=[ list[0],list[2],list[3] ]
and without using any function or library, just python
i mean using slicing and for loop
also the indices are varying to print all the possible combination
see the code below
nums_1=[3,2,4,1]
results=[]
steps=1
s=0
limit=pow(2, len(nums_1))
while steps<=len(nums_1) and s<limit :
for i in range (0,len(nums_1)+1):
for j in range (0,len(nums_1)+1):
if nums_1[i:j:steps] not in results:
results.append(sorted(nums_1[i:j:steps]))
print(i,j,steps,'--------------->',nums_1[i:j:steps])
steps+=1
s+=1
output is
[[], [3], [2, 3], [2, 3, 4], [1, 2, 3, 4], [2], [2, 4], [1, 2, 4], [4], [1, 4], [1], [3, 4], [1, 2], [1, 3]]
[1,2,3] and [1,3,4] not in the result
Assuming you know which indexes you want to find, you can insert these in a list. Then use list comprehension to generate a new list with the found elements.
lst=[3,2,4,1]
indexes = [0,1,-1]
found = [lst[ind] for ind in indexes]

Extract the n-th element from a list of list of list

I have a list of list of list and I would like to extract the n-th element from each sub-sub list. For example, given the following list:
my_list = [[[0, 0], [1, 1]], [[2, 0], [3, 2]], [[2, 0], [3, 3]], [[4, 0], [5, 4]], [[6, 0], [7, 5]]]
I want to extract all the first elements keeping the structure of the original list (list of list of list), like:
all_first_elements = [[[0],[1]], [[2],[3]], [[2],[3]], [[4],[5]], [[6],[7]]]
The problem is similar to this one but with one additional nested list.
I have tried all_first_elements = [item[0] for item in my_list] but it returns the first elements of the list of list (and not the first elements of list of list of list).
You can use a double for loop within a list-comprehension
all_first_elements = [[[item_1[0]] for item_1 in item_0] for item_0 in my_list]
The output will be
[[[0], [1]], [[2], [3]], [[2], [3]], [[4], [5]], [[6], [7]]]
If you are open to using numpy, you can slice and index using:
import numpy as np
np.array(my_list)[:, :, [0]]
[out]
array([[[0],
[1]],
[[2],
[3]],
[[2],
[3]],
[[4],
[5]],
[[6],
[7]]])
If you need the result as a list, just chain on the .tolist method:
np.array(my_list)[:, :, [0]].tolist()
[out]
[[[0], [1]], [[2], [3]], [[2], [3]], [[4], [5]], [[6], [7]]]
The enumerate() function adds a counter to an iterable.
So for each element in a cursor, a tuple is produced with (counter, element).
my_list = [[[0, 0], [1, 1]], [[2, 0], [3, 2]], [[2, 0], [3, 3]], [[4, 0], [5, 4]], [[6, 0], [7, 5]]]
for sub_list in my_list:
for index,elm in enumerate(sub_list):
sub_list[index] = [elm[0]]
print(my_list)
O/P:
[[[0], [1]], [[2], [3]], [[2], [3]], [[4], [5]], [[6], [7]]]
Try this :
all_first_elements = [[[i[0][0]],[i[1][0]]] for i in my_list]
Output :
[[[0], [1]], [[2], [3]], [[2], [3]], [[4], [5]], [[6], [7]]]
Here is a generic solution that you will not have to update if the nesting level changes:
def extract(data, level, index):
result = []
if level == 0:
return data[index]
for elem in data:
result.append(extract(elem, level-1, index))
return result
You will call it like this for the example provided in the original question:
print extract(my_list, level=2, index=0)
Please be aware that I have excluded data validation checks for the sake of clarity and because I was not sure how your data may vary. For example, the lists at the n-th level of depth may or may not be empty.

Partition N items into K bins in Python lazily

Give an algorithm (or straight Python code) that yields all partitions of a collection of N items into K bins such that each bin has at least one item. I need this in both the case where order matters and where order does not matter.
Example where order matters
>>> list(partition_n_in_k_bins_ordered((1,2,3,4), 2))
[([1], [2,3,4]), ([1,2], [3,4]), ([1,2,3], [4])]
>>> list(partition_n_in_k_bins_ordered((1,2,3,4), 3))
[([1], [2], [3,4]), ([1], [2,3], [4]), ([1,2], [3], [4])]
>>> list(partition_n_in_k_bins_ordered((1,2,3,4), 4))
[([1], [2], [3], [4])]
Example where order does not matter
>>> list(partition_n_in_k_bins_unordered({1,2,3,4}, 2))
[{{1}, {2,3,4}}, {{2}, {1,3,4}}, {{3}, {1,2,4}}, {{4}, {1,2,3}},
{{1,2}, {3,4}}, {{1,3}, {2,4}}, {{1,4}, {2,3}}]
These functions should produce lazy iterators/generators, not lists. Ideally they would use primitives found in itertools. I suspect that there is a clever solution that is eluding me.
While I've asked for this in Python I'm also willing to translate a clear algorithm.
you need a recursive function to solve this kind of problem: you take the list, take a subportion of it of increasing length and apply the same procedure to the remaining tail of the list in n-1 pieces.
here is my take to the ordered combination
def partition(lista,bins):
if len(lista)==1 or bins==1:
yield [lista]
elif len(lista)>1 and bins>1:
for i in range(1,len(lista)):
for part in partition(lista[i:],bins-1):
if len([lista[:i]]+part)==bins:
yield [lista[:i]]+part
for i in partition(range(1,5),1):
print i
#[[1, 2, 3, 4]]
for i in partition(range(1,5),2):
print i
#[[1], [2, 3, 4]]
#[[1, 2], [3, 4]]
#[[1, 2, 3], [4]]
for i in partition(range(1,5),3):
print i
#[[1], [2], [3, 4]]
#[[1], [2, 3], [4]]
#[[1, 2], [3], [4]]
for i in partition(range(1,5),4):
print i
#[[1], [2], [3], [4]]
Enrico's algorithm, Knuth's, and only my glue are needed to paste together something that returns the list of lists or set of sets (returned as lists of lists in case elements are not hashable).
def kbin(l, k, ordered=True):
"""
Return sequence ``l`` partitioned into ``k`` bins.
Examples
========
The default is to give the items in the same order, but grouped
into k partitions:
>>> for p in kbin(range(5), 2):
... print p
...
[[0], [1, 2, 3, 4]]
[[0, 1], [2, 3, 4]]
[[0, 1, 2], [3, 4]]
[[0, 1, 2, 3], [4]]
Setting ``ordered`` to None means that the order of the elements in
the bins is irrelevant and the order of the bins is irrelevant. Though
they are returned in a canonical order as lists of lists, all lists
can be thought of as sets.
>>> for p in kbin(range(3), 2, ordered=None):
... print p
...
[[0, 1], [2]]
[[0], [1, 2]]
[[0, 2], [1]]
"""
from sympy.utilities.iterables import (
permutations, multiset_partitions, partitions)
def partition(lista, bins):
# EnricoGiampieri's partition generator from
# http://stackoverflow.com/questions/13131491/
# partition-n-items-into-k-bins-in-python-lazily
if len(lista) == 1 or bins == 1:
yield [lista]
elif len(lista) > 1 and bins > 1:
for i in range(1, len(lista)):
for part in partition(lista[i:], bins - 1):
if len([lista[:i]] + part) == bins:
yield [lista[:i]] + part
if ordered:
for p in partition(l, k):
yield p
else:
for p in multiset_partitions(l, k):
yield p

Algorithm to generate (not quite) spanning set in Python

This follows on from this question:
Algorithm to generate spanning set
Given this input: [1,2,3,4]
I'd like to generate this set of sets in python:
[1] [2] [3] [4]
[1] [2] [3,4]
[1] [2, 3, 4]
[1] [2,3] [4]
[1,2] [3] [4]
[1,2] [3,4]
[1,2,3] [4]
[1,2,3,4]
So unlike the previous question, the order of the list is retained.
Ideally the code would work for n items in the list
Thanks very much
EDIT 2: Could anyone advise me on how to do this if the original input is a string rather than a list (where each word in the string becomes an item in a list). Thanks!
EDIT: added [1] [2, 3, 4] Sorry for the mistake
You might also enjoy a recursive solution:
def span(lst):
yield [lst]
for i in range(1, len(lst)):
for x in span(lst[i:]):
yield [lst[:i]] + x
Explanation
We exploit recursion here to break the problem down. The approach is the following:
For every list, the whole list is a valid spanning: [1,2,3,4] => [[1,2,3,4]].
For every list that is longer than size 1, we can use the first item as a group and then apply the same algorithm on the remaining list to get all the combined results:
[1,2,3] =>
[[1]] + [[2], [3]] # => [[1], [2], [3]]
[[1]] + [[2,3]] # => [[1], [2,3]]
For every list that is longer than size 2, we can just as well use the first two items as a group and then apply the same algorithm on the remaining list and combine the results:
[1,2,3,4,5] =>
[[1,2]] + [[3], [4], [5]] # => [[1,2], [3], [4], [5]]
[[1,2]] + [[3,4], [5]] # => [[1,2], [3,4], [5]]
[[1,2]] + [[3], [4,5]] # => [[1,2], [3], [4,5]]
[[1,2]] + [[3,4,5]] # => [[1,2], [3,4,5]]
We can see that the possible combinations on the right side are indeed all possible groupings of the remainder of the list, [3,4,5].
For every list that is longer than ... etc. Thus, the final algorithm is the following:
yield the whole list (it is always a valid spanning, see above)
For every possible splitting of the list, yield the left-hand part of the list combined with all possible spannings of the right-hand part of the list.
yield is a special keyword in Python that make the function a generator, which means that it returns a iterable object that can be used to enumerate all results found. You can transform the result into a list using the list constructor function: list(span([1,2,3,4])).
Adjusting one of the solution from Python: show all possible groupings of a list:
from itertools import combinations
def cut(lst, indexes):
last = 0
for i in indexes:
yield lst[last:i]
last = i
yield lst[last:]
def generate(lst, n):
for indexes in combinations(list(range(1,len(lst))), n - 1):
yield list(cut(lst, indexes))
data = [1,2,3,4]
for i in range(1, len(data)+1): # the only difference is here
for g in generate(data, i):
print(g)
"""
[[1, 2, 3, 4]]
[[1], [2, 3, 4]]
[[1, 2], [3, 4]]
[[1, 2, 3], [4]]
[[1], [2], [3, 4]]
[[1], [2, 3], [4]]
[[1, 2], [3], [4]]
[[1], [2], [3], [4]]
"""
import itertools
a = [1, 2, 3, 4]
n = len(a)
for num_splits in range(n):
for splits in itertools.combinations(range(1, n), num_splits):
splices = zip([0] + list(splits), list(splits) + [n])
print([a[i:j] for i, j in splices])
prints
[[1, 2, 3, 4]]
[[1], [2, 3, 4]]
[[1, 2], [3, 4]]
[[1, 2, 3], [4]]
[[1], [2], [3, 4]]
[[1], [2, 3], [4]]
[[1, 2], [3], [4]]
[[1], [2], [3], [4]]

Categories

Resources