how print_parts() function work in composing program chapter2.3 - python

in book composing program chapter 2.3, there is a function print_part() makes me confused (whole code here):
>>> def print_parts(tree, partition=[]):
if is_leaf(tree):
if root(tree):
print(' + '.join(partition))
else:
left, right = branches(tree)
m = str(root(tree))
print_parts(left, partition + [m])
print_parts(right, partition)
>>> print_parts(partition_tree(6, 4))
4 + 2
4 + 1 + 1
3 + 3
3 + 2 + 1
3 + 1 + 1 + 1
2 + 2 + 2
2 + 2 + 1 + 1
2 + 1 + 1 + 1 + 1
1 + 1 + 1 + 1 + 1 + 1
this function print all the ways using parts up to 4 to partition 6. I've understand how the partition algorithm works in partition_tree() and have no problem to understand a partition tree:
4
_________________________
| |
4 3
_______ _____
| | | |
.. ... ... ...
but I still don't know how to print partition ways from a partition tree. especially these lines:
print(' + '.join(partition))
print_parts(left, partition + [m])
print_parts(right, partition)
# why call twice? and the recursion here didn't looks like the
# way the print_parts() called in the beginning.
update:
recursion here didn't looks like the way the print_parts() called in the beginning.
take an simpler args as example to illustrate my confusion:
>>> print_parts(partition_tree(3, 2))
2 + 1
1 + 1 + 1
the partition tree is:
2
--------------------------------
2 1
---------------- ------------------------
F 1 1 F
------ ------------
T F 1 F
-------
T F
or
[2, [2, [False], [1, [True], [False]]], [1, [1, [1, [True], [False]],[False]],[False]]]
the above list is first pass into the func print_parts() as trees's value.
when going to this line:
print_parts(left, partition + [m])
the left value is
[[2, [False], [1, [True], [False]]], [1, [1, [1, [True], [False]],[False]],[False]]]
it's no longer a tree because in the definition the tree should have a structure like: [node, [branch],[branch]]. if so, the recursion cannot works.

It sounds like you understand the algorithm but maybe not the Python code.
print(' + '.join(partition)) is just formatting a list into the output format that you see. For example, it turns the list [3, 2, 1] into the string '3 + 2 + 1'.
Then the reason the way print_parts can be called in a way that "doesn't look the same" as the original call is that the method definition allows for an optional second argument. If the second option is not explicitly provided, it is by default set to an empty list. That's what partition=[] in the function definition does.
As for why it's being called "twice", it's just that at each step of the recursion, we're branching into two branches (left and right), and then recursively processing both the left side and the right side.
Update (reply to your update):
The confusion stems from this line where Python can assign multiple variables at once:
left, right = branches(tree)
Since branches(tree) is a list of length 2, the 2 elements get assigned to left and right respectively.
So it is not true what you said:
the left value is
[[2, [False], [1, [True], [False]]], [1, [1, [1, [True], [False]],[False]],[False]]]
But instead:
left, right = branches(partition_tree(3, 2))
print left
> [2, [False], [1, [True], [False]]]
print right
> [1, [1, [1, [True], [False]], [False]], [False]]

Related

How to determine trend in series?

1
2
3
4
Combined
Series
0.5
5
10
Nan
0.5, 5, 10
Increaseing
1
2
3
4
1, 2, 3, 4
Increasing
8
5
3
-1
8, 5, 3, -1
Decreasing
4
8
5
3
4, 8, 5, 3
neither
I have a table with the above column [1,2,3,4,Combined]
How can i try to automate the series determination of the combined column in python?
def test(combine):
return "Increasing." if all(combine[i] < combine[i + 1] for i in range(len(combine) - 1))
else
"Decreasing." if all(combine[i + 1] < combine[i] for i in range(len(combine) - 1))
else
"neither!"
But this give me error with outcome '0'
Your function works fine, provided that:
It is written with proper indentations (and line-continuations),
The column Combined contains lists of numbers (not e.g. strings such as '[0.5, 5, 10]', or lists of strings, etc).
First, let's make sure the column contains lists of floats and/or ints:
assert df['Combined'].apply(lambda x: isinstance(x, list) and all(isinstance(xi, (int, float)) for xi in x)).all()
If that is not the case, then correct it:
from pandas.api.types import is_numeric_dtype
# test that the 4 first columns are numeric
assert df.iloc[:, :4].apply(is_numeric_dtype).all()
df['Combined'] = df.iloc[:, :4].apply(lambda s: s.dropna().tolist(), axis=1)
Then, make sure the syntax of your function is correct (indentation and line-continuation):
def test(combine):
return "Increasing." if all(combine[i] < combine[i + 1] for i in range(len(combine) - 1)) \
else "Decreasing." if all(combine[i + 1] < combine[i] for i in range(len(combine) - 1)) \
else "neither!"
Then:
>>> df['Combined'].apply(test)
0 Increasing.
1 Increasing.
2 Decreasing.
3 neither!
You could make the function a bit more concise:
def test(x):
return "Increasing." if all(a < b for a, b in zip(x, x[1:])) \
else "Decreasing." if all(a > b for a, b in zip(x, x[1:])) \
else "neither!"
Or you could use Pandas Series built-in monotonic properties:
def trend_comment(a):
s = pd.Series(a)
return 'increasing' if s.is_monotonic_increasing \
else 'decreasing' if s.is_monotonic_decreasing \
else 'neither'
>>> df['Combined'].apply(trend_comment)
0 increasing
1 increasing
2 decreasing
3 neither
(Note that the definition is slightly different than your tests: Pandas' is_monotonic_increasing really means "monotonic non-decreasing", in which consecutive values are either equal or increasing).

Why does python replace every object of a column, when only referring to one, if all lines are identical? [duplicate]

This question already has answers here:
List of lists changes reflected across sublists unexpectedly
(17 answers)
Closed 3 months ago.
When trying to change one value in a matrix, python will change all items of that column with the desired value, despite the fact I am only trying to change one. But this only happens when all rows are identical.
Example:
def print_matrix(matrix: list[list], dlm: str) -> None:
for row in matrix:
for col in row:
print(col, end = dlm)
print()
one_row = list(range(4))
test_matrix = []
for i in range(5):
test_matrix.append(one_row)
test_matrix[0][0] = 5
sec_matrix =[
[0,1,2,3],
[0,1,2,3],
[0,1,2,3],
[0,1,2,4]
]
sec_matrix[0][0]=5
print_matrix(test_matrix, ' ')
print()
print_matrix(sec_matrix, ' ')
In the first matrix every 0 gets replaced with a 5, despite only referencing the first item of the first list.
In the second one it works the way I want it to, because the last list is slightly different.
Why is there a difference in the way test_matrix and sec_matrix are treated? Is this a bug, or intended?
Does python just think they are the same list because they look the same?
Or are they even the same to increase performance? Either way I don't think it should happen.
I tried to update a matrix item on certain coordinates.
I expected only the desired item to be altered, instead every single one of that column got changed. Problem is fixed by not having identical rows.
The reason is when you write test_matrix.append(one_row) you are appending actually [0,1,2,3] 5 times to test_matrix, essentially, i.e the list will look like [[0, 1, 2, 3], [0, 1, 2, 3], [0, 1, 2, 3], [0, 1, 2, 3], [0, 1, 2, 3]]. Here each list element is a list with [0,1,2,3] references to the same [0,1,2,3]. When you then modify this single [0,1,2,3] it is visible via all references to it. For debugging purposes, you can check it,
print(id(test_matrix[0]))
print(id(test_matrix[1]))
So you will see all are the same id, if you want to do it then you can do it like below- where test_matrix = [ list(range(4)) for n in range(5) ] will re-generate value each time
def print_matrix(matrix, dlm):
for row in matrix:
for col in row:
print(col, end = dlm)
print()
test_matrix = []
test_matrix = [ list(range(4)) for n in range(5) ] # re-generate and appending
test_matrix[0][0] = 7
sec_matrix =[
[0,1,2,3],
[0,1,2,3],
[0,1,2,3],
[0,1,2,4]
]
sec_matrix[0][0]=5
print_matrix(test_matrix, ' ')
print()
print_matrix(sec_matrix, ' ')
Output:
7 1 2 3
0 1 2 3
0 1 2 3
0 1 2 3
0 1 2 3
5 1 2 3
0 1 2 3
0 1 2 3
0 1 2 4

How do I go back and forth in a loop?

So I have a sequence of items at these indexes 1 2 3 4 5 6 7 8 9 10. I need to add middle two elements at first:
output = i[5]+i[6]
and for every next iteration, output must be add with left element and then right element.
output = output + i[4]
next step,
output = output + i[7]
I don't know how do that in loop or any other structure. Could anyone recommend anything to me? I'm working with python, but I just need pseudocode.
To get an element from either left or either right for each iteration I'd use a while statement in combination with a pop() on the list.
Code:
l = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
total = 0
while len(l):
total += l.pop(len(l) // 2)
Output:
print(total)
55
The total progression is:
0 + 6 = 6
6 + 5 = 11
11 + 7 = 18
18 + 4 = 22
22 + 8 = 30
30 + 3 = 33
33 + 9 = 42
42 + 2 = 44
44 + 10 = 54
54 + 1 = 55
newlist=[1,2,3,4,5,6,7,8,9,10]
while(len(newlist)>0):
lengthi=(len(newlist)//2)-1
lengthj=(len(newlist)//2)
sum=(newlist[lengthi]+newlist[lengthj])
del newlist[lengthi:(lengthj)+1]
print(sum)
This works for the list I have tried it, however this will change according to the length of your list. This works for even, if the length changes the code gives errors because there will be a non matched element at the end. Also you can do this by not deleting the list by copying the variable into another variable and then deleting the variables from it is probably better.
The common item here is that the two subscripts have to add up to 11.
output = 0
for left in range(5, 0, -1):
right = 11 - left
output += i[left]
output += i[right]
I left the solution longer than necessary, since you seem to have other steps in between.
Well you can start from middle of the list and then as you go forward, add left and right elements together.
m = len(a)//2 # middle of the list
output = 0
for i in range(m):
output += a[m-i-1] + a[m+i]
I'm trusting here that length of the list is an even number. Although odd length won't be a problem, just that the last element of list will be ignored.

Organizing data to access information based on either variable depending on what is required

I am given a list of elements with their respective lists of nodes, and I want to switch how I look them up. I'd like to have a list of nodes with lists of their respective elements.
Example:
Have
E N
1 1 2 3
2 2 3 4
3 1 4
Desired
N E
1 1 3
2 1 2
3 1 2
4 2 3
I have a nested for loop solution.
# known data
elements = [
[], # No element 0
[1, 2, 3],
[2, 3, 4],
[1, 4],
]
max_element = 3
max_node = 4
# truth
truth_nodes = [
[],
[1, 3],
[1, 2],
[1, 2],
[2, 3],
]
# current algorithm
nodes = [[] for n in range(max_node+1)]
for node in range(1, max_node + 1):
for element in range(1, max_element + 1):
if node in elements[element]:
nodes[node].append(element)
Is there another tool in Python, either with NumPy, Pandas, or something else that might speed this up for over 300,000 elements? If this is a common algorithm, what is its name and/or how would I find it?
Edit: Is this a graph algorithm?
I can imagine vertices for my nodes above as well as my elements and using NetworkX to make an undirected graph. Then would I use a connected component algorithm?
I ended up using a pandas DataFrame with columns node and element and a row for every pair of a single node belonging to a single element.
Node Element
1 1
2 1
3 1
2 2
3 2
4 2
1 3
4 3
I believe this follows Tidy Data by Hadley Wickham. Then I could call a given node by a boolean index df[df.node==1], and likewise for an element, df[df.element==3].
I am still not sure if this is a graph algorithm, but organizing my data this way allowed me to move forward with the rest of the problem.

Slicing a list from the end

Say, I have a list of values:
>>> a = [1, 2, 3, 4]
How can I make it include the end value through slicing? I expected:
>>> a[4:]
[4]
instead of:
>>> a[4:]
[]
Slicing indices start from zero
So if you have:
>>> xs = [1, 2, 3, 4]
| | | |
V V V V
0 1 2 3 <-- index in xs
And you slice from 4 onwards you get:
>>> xs[4:]
[]
Four is is the length of ``xs`, not the last index!
However if you slice from 3 onwards (the last index of the list):
>>> xs[3:]
[4]
See: Data Structures
Many many common computer programmming langauges and software systems are in fact zero-based so please have a read of Zero-based Numbering
Python indexes are zero based. The last element is at index 3, not 4:
>>> a = [1,2,3,4]
>>> a[3:]
[4]
a = [1,2,3,4]
a[-1:]
In python you can iterate values from beginning to end ending to beginning
1, 2, 3, 4
| | | |
0 1 2 3 (or)
-4 -3 -2 -1
So If you want last element of the list you can use either a[len(a)-1:] or a[-1:]

Categories

Resources