Tuple and recursive list conversion - python

A recursive list is represented by a chain of pairs. The first element of each pair is an element in the list, while the second is a pair that represents the rest of the list. The second element of the final pair is None, which indicates that the list has ended. We can construct this structure using a nested tuple literal. Example:
(1, (2, (3, (4, None))))
So far, I've created a method that converts a tuple of values or the value None into a corresponding rlist. The method is called to_rlist(items). Example:
>>> to_rlist((1, (0, 2), (), 3))
(1, ((0, (2, None)), (None, (3, None))))
How do I write the inverse of to_rlist, a function that takes an rlist as input and returns the corresponding tuple? The method should be called to_tuple(parameter). Example of what should happen:
>>> x = to_rlist((1, (0, 2), (), 3))
>>> to_tuple(x)
(1, (0, 2), (), 3)
Note: The method to_rlist works as intended.
This is what I have so far:
def to_tuple(L):
if not could_be_rlist(L):
return (L,)
x, y = L
if not x is None and not type(x) is tuple and y is None:
return (x,)
elif x is None and not y is None:
return ((),) + to_tuple(y)
elif not x is None and not y is None:
return to_tuple(x) + to_tuple(y)
Which gives me the following result (which is incorrect):
>>> x = to_rlist((1, (0, 2), (), 3))
>>> to_tuple(x)
(1, 0, 2, (), 3)
How can I fix my method to return a nested tuple properly?

def to_list(x):
if x == None:
return ()
if type(x) != tuple:
return x
a, b = x
return (to_list(a),) + to_list(b)

This one worked for my HW ;)
def to_rlist(items):
r = empty_rlist
for i in items[::-1]:
if is_tuple(i): r1 = to_rlist(i)
else: r1 = i
r = make_rlist(r1,r)
return r

Related

Treverse list of tuples to compare and report min, max

My previous question was not understood, so I rephrase and post this one.
I have a list of tuple for (class, n_class_examples) like this:
my_list = (0, 126), (1, 192), (2, 330), (3, 952) ]
So I am interested in generating a function, that takes in such a list, compare each tuple against all others, and in each case reports which class has smaller number of samples (min_class), and which has the larger number of samples (max_class).
def get_min_max_class(current_list):
for tn, tn+1: # tn -> 1-tuple, tn+1 any other tuple not tn
if tn[1] < tn+1[1]
smaller_class = tn[0]
larger_class = tn+1[0]
smaller_class = tn+1[0]
larger_class = tn[0]
return # smaller, larger of the 2 compared in each case
So that:
get_min_max_class(my_list)
# would perform the comparison like so:
(0, 126) v (1, 192) -> min_class = 0, max_class = 1 # in this case
(0, 126) v (2, 330) -> min_class = 0, max_class = 2 # and in this case
(0, 126) v (3, 952) -> min_class = 0, max_class = 3 # and here ..
(1, 192) v (2, 330) -> min_class = 1, max_class = 2 # ...
(1, 192) v (3, 952) -> min_class = 1, max_class = 3
(2, 330) v (3, 952) -> min_class = 2, max_class = 3
Forgive my definition of function, but I want the function to iteratively compare those items, each time, report which is larger and which is smaller.
Iterate over the list of pairs generated by itertools.combintions, the process each pair individually using min and max.
from itertools import combinations
from operator import itemgetter
first = itemgetter(0)
second = itemgetter(1)
def get_min_max_class(current_list):
for pair in combinations(current_list, 2):
p0, p1 = pair
min_class = first(min(pair, key=second))
max_class = first(max(pair, key=second))
print(f'{p0} v {p1} -> min_class = {min_class}, max_class = {max_class}')
get_min_max_class(my_list)
If you want to return a list of results, rather than simply printing a report, you'll have to define what exactly you want to return.
Python's sorted(), min(), and max() functions take as second argument a 'key' that let's you specify how to calculate the sorting for different objects, using lambda functions. In this case, you want to sort the tuples based on the value of the second element and return the corresponding first value.
So, if I wanted the 'max' in your case I would do:
max_class = max(my_list, key=lambda x: x[1])[-1]
The lambda expression, if it's new to you, is saying "sort the item x in the list based on whatever you find at x[1]". Then, take the final element of the sorted list to get the class with the most samples, or whatever it was exactly.
I hope that helps!

Iterating n python iterators at once

I'm trying to iterate a bunch of data ranges at once, to get the combinations of all their values.
The number of ranges can differ, but I have them collected in a list.
Is there a way to iterate them using list comprehension or a similar clean, pythonic way?
This is what I mean by iterating together:
[print(i, j) for i in r1 for j in r2]
So that's a simple example with two known ranges, but what I need is more like
[print(i, j, ...) for i in r1 for j in r2 for k in r3...]
Note: i don't just need a list of number combinations, the iterators are my own iterator class which works similarly to range() but allows me to also get current state without calling next(), which would alter the state.
My iterator class sets its value back to the start on StopIteration, so it can be looped through more than once.
Here you can see the class:
#dataclass
class Range:
start: float
end: float
step: float = field(default=1)
includeEnd: bool = field(default=True)
def __post_init__(self):
self.value = self.start
def __next__(self):
v = self.value
end = v > self.end if self.includeEnd else v >= self.end
if not end:
self.value += self.step
return v
else:
self.value = self.start
raise StopIteration
def __iter__(self):
return self
But how would you get the product of n iterators using itertools.product(), when you have a list of n iterators?
itertools.product(*the_list). Nothing special about product() there. The leading * is general Python syntax for treating a list (more generally, an iterable) as a sequence of individual arguments.
>>> from itertools import product
>>> args = [range(2), range(3), (i**2 for i in [5, 9])]
>>> args
[range(0, 2), range(0, 3), <generator object <genexpr> at 0x000001E2E7A710B0>]
>>> for x in product(*args):
... print(x)
(0, 0, 25)
(0, 0, 81)
(0, 1, 25)
(0, 1, 81)
(0, 2, 25)
(0, 2, 81)
(1, 0, 25)
(1, 0, 81)
(1, 1, 25)
(1, 1, 81)
(1, 2, 25)
(1, 2, 81)

List not appending properly when passed to function

I have a problem with list of lists, that the list loses sub-lists' values.
When I pass the list to function, the function operates on the list and then the list is returned but sub-lists of the list are empty.
def solve_n_queens(size):
possible_places = []
solve_n_queens_helper(0, size, [], possible_places)
return possible_places
def solve_n_queens_helper(row, size, curr_placings, possible_places):
if row == size:
possible_places.append(curr_placings)
print(possible_places) # here it prints list of sublists correctly
for col in range(size):
cell = (row, col)
if is_valid_with_previous(cell, curr_placings):
curr_placings.append(cell)
solve_n_queens_helper(row + 1, size, curr_placings, possible_places)
curr_placings.pop()
def is_valid_with_previous(queen_position, prev_queen_positions):
for prev_queen_position in prev_queen_positions:
if prev_queen_position[1] == queen_position[1]:
return False
row_distance = abs(prev_queen_position[0] - queen_position[0])
col_distance = abs(prev_queen_position[1] - queen_position[1])
if row_distance == 0 or row_distance == col_distance:
return False
return True
if __name__ == '__main__':
print(solve_n_queens(4))
It should return [[(0, 2), (1, 0), (2, 3), (3, 1)], [(0, 2), (1, 0), (2, 3), (3, 1)]] but it returns [[], []]. When I print possible_placings while the algorithm is running it prints the list correctly, the values are in the sub-lists, but when algorithm terminates, the values disappear.
The problem is that you add curr_placings to the result list, but then pop each of the elements from those lists. Create a copy when adding the results:
if row == size:
possible_places.append(list(curr_placings)) # <-- copy list!
print(possible_places)
Alternatively, curr_placings[:] or curr_placings.copy() would also work. The same would also be necessary if you used yield or return instead of collecting the solutions in a list.

pyspark redueByKey modify single results

I have a dataset that looks like this in pyspark:
samp = sc.parallelize([(1,'TAGA'), (1, 'TGGA'), (1, 'ATGA'), (1, 'GTGT'), (2, 'GTAT'), (2, 'ATGT'), (3, 'TAAT'), (4, 'TAGC')])
I have a function that I'm using to combine the strings:
def combine_strings(x,y):
if (isinstance(x,list) and isinstance(y, list)):
z = x + y
return z
if (isinstance(x, list) and isinstance(y, str)):
x.append(y)
return x
if (isinstance(x, str) and isinstance(y, list)):
y.append(x)
return y
return [x,y]
The result I get is:
samp.reduceByKey(lambda x,y : combine_strings(x,y)).collect()
[(1, ['TAGA', 'TGGA', 'ATGA', 'GTGT']), (2, ['GTAT', 'ATGT']), (3, 'TAAT'), (4, 'TAGC')]
What I want is:
[(1, ['TAGA', 'TGGA', 'ATGA', 'GTGT']), (2, ['GTAT', 'ATGT']), (3, ['TAAT']), (4, ['TAGC'])]
Where everything is an array. I can't tell if pyspark is calling combine_strings on a result where there's 1 entry or if I can tell reduceByKey to do something with singleton results? How do I modify the reduceByKey() or the combine_strings function to produce what I'd like?
You could first map the values into lists and then only combine those lists:
samp.mapValues(lambda x : [x]).reduceByKey(lambda x,y : x + y).collect()
The problem here is that those singletons are not affected by reduceByKey. Here is another example:
samp = sc.parallelize([(1,1),(2,2),(2,2),(3,3)])
>>> samp.reduceByKey(lambda x, y : x + y + 1).collect()
[(3, 3), (1, 1), (2, 5)]

python operations with sets

I use python only occasionally, sorry for a seemingly trivial question
>>> a = set(((1,1),(1,6),(6,1),(6,6)))
>>> a
set([(6, 1), (1, 6), (1, 1), (6, 6)])
>>> a - set(((1,1)))
set([(6, 1), (1, 6), (1, 1), (6, 6)])
>>> a.remove((1,1))
>>> a
set([(6, 1), (1, 6), (6, 6)])
why '-' operator didn't delete the element but the remove did ?
Because you missed a comma:
>>> set(((1,1)))
set([1])
should be:
>>> set(((1,1),))
set([(1, 1)])
or, to be more readable:
set([(1,1)])
or even (Py2.7+):
{(1,1)}
You missed a comma when trying to specify a tuple of one element. Syntax for tuples is indeed somewhat tricky...
A tuple is built using commas, not parenthesis
sometimes it is mandatory to add parenthesis around it
an empty tuple is however represented by an empty pair of parenthesis
not always a pair of parenthesis wrapping zero or more expression separated by commas is a tuple
Some examples
w = 1, 2, 3 # creates a tuple, no parenthesis needed
w2 = (1, 2, 3) # works too, like x+y is the same as (x+y)
x, y, z = w # unpacks a tuple
k0 = () # creates an empty tuple
k1 = (1,) # a tuple with one element (note the comma)
k = (1) # just a number, NOT a tuple
foo(1, 2, 3) # call passing three numbers, not a tuple
bar((1, 2, 3)) # call passing a tuple
if x in 1, 2: # syntax error, parenthesis are needed
pass
for x in 1, 2: # ok here
pass
gen = (x for x in 1, 2) # error, parenthesis needed here around (1, 2)

Categories