Generate a list from linear combination in simpy - python

I am using sympy to do some calculations, but I need to manipulate results of multiplications using rules I have to define. Let us suppose I have
a, b, c, d = symbols('a b c d', commutative=False)
res = a*b + b*c + c*d
I was wondering how can I write a function which takes res and gives a list of this kind
[[a,b],[b,c],[c,d]]
since every time I try to perform operations like list(res) python throws the exception Mul (Add) object is not iterable. Thanks in advance

There is no function for this already but you can make one like this:
def factors(expr):
result = []
for term in Add.make_args(expr):
flat_factors = []
for factor in Mul.make_args(term):
symbol, power = factor.as_base_exp()
flat_factors += [symbol] * power
result.append(flat_factors)
return result
That gives:
In [74]: factors(a*b + b*c + c*d)
Out[74]: [[a, b], [b, c], [c, d]]
In [75]: factors(a**2*b + a*b**2)
Out[75]: [[a, b, b], [a, a, b]]

This is a way:
[x.as_ordered_factors() for x in res.as_ordered_terms()]

No sure, but do you wanna do something like this?
res = 'a*b + b*c + c*d'
def list_func(res):
list1 = res.split('+')
return ([item.split('*') for item in list1])
print(list_func(res))

Related

next item of list inside dataframe

I have a dataframe that has a column where each row has a list.
I want to get the next element after the value I am looking for (in another column).
For example:
Let's say I am looking for 'b':
|lists |next_element|
|---------|------------|
|[a,b,c,d]| c | #(c is the next value after b)
|[c,b,a,e]| a | #(a is the next value after b)
|[a,e,f,b]| [] | #(empty, because there is no next value after b)
*All lists have the element. There are no lists without the value I am looking for
Thank you
Try writing a function and use apply.
value = 'b'
def get_next(x):
get_len = len(x)-1
for i in x:
if value.lower() == i.lower():
curr_idx = x.index(i)
if curr_idx == get_len:
return []
else:
return x[curr_idx+1]
df["next_element"] = df["lists"].apply(get_next)
df
Out[649]:
lists next_element
0 [a, b, c, d] c
1 [c, b, a, e] a
2 [a, e, f, b] []
First observation, since you want the next element of a list of string elements, the expected data type should be a string for that column, and not a list.
So, instead of the next_element columns as [c, a, []] its better to use [c, a, None]
Secondly, you should try avoiding apply methods directly over series and instead utilize the str methods that pandas provides for series which is a vectorized way of solving such problems super fast.
With the above in mind, let's try this completely vectorized one-liner -
element = 'b'
df['next_element'] = df.lists.str.join('').str.split(element).str[-1].str[0]
lists next_element
0 [a, b, c, d] c
1 [c, b, a, e] a
2 [a, e, f, b] NaN
First I combine each row as a single string [a,b,c,d]->'abcd`
Next I split this by 'b' to get substrings
I pick the last element from this list and finally the first element from that, for each row, using str functions which are vectorized over each row.
Read more about pandas.Series.str methods on official documentation/tutorial here
df = df.assign(next_element = "")
print(df)
for ind in df.index:
c= df["Lists"][ind]
for i,v in enumerate(c):
if v == "b":
df["next_element"][ind] = c[i+1]
print(df)
Try with this one you will get the exact output what you expected.

How can I create more than one array at the same time using a generator?

Let's assume we have 3 arrays, index, a and b. How can I create arrays c and d just passing through index once?
c = [a[i] for i in index]
d = [b[i] for i in index]
Is there a way to create these arrays with a sigle generator?
You can use the zip function:
c, d = zip(*((a[i], b[i]) for i in index))
If you want c and d to be lists you can use map:
c, d = map(list, zip(*((a[i], b[i]) for i in index)))
If you want something longer (but maybe clearer), you could build a generator:
def g(a, b, index):
for i in index:
yield a[i], b[i]
c, d = zip(*g(a, b, index))
I will use zip with tuple expansion using *
c,d = zip(*((a[i],b[i]) for i in index))
Here it expands a[i] and b[i] in pairs using zip from generator expression.

Python - double loop for element wise string concatenation

My question is very simple:
I have A = ['AA','BB'], B = ['CC','DD']
How do I get AB = ['AACC','AADD','BBCC',BBDD']?
Thank you!
You can use itertools.product:
>>> from itertools import product
>>> A = ['AA','BB']
>>> B = ['CC','DD']
>>> AB = [''.join(p) for p in product(A, B)]
>>> AB
['AACC', 'AADD', 'BBCC', 'BBDD']
This has the benefit of working with any number of iterables.
Its easier to see whats happening in a complete loop, here we are going to take i in a which will be AA and BB and j in b which will be CC and DD. On our first iteration we combine the first two AA + CC then append them to our new list , next comes AA + DD then onto BB and the process repeats.
a = ['AA','BB']
b = ['CC','DD']
res = []
for i in a:
for j in b:
x = i + j
res.append(x)
print(res)
# ['AACC', 'AADD', 'BBCC', 'BBDD']
After you understand this you can skip that process and do it with list comprehension, this is identical.
res = [i + j for i in a for j in b]
with list comprehension:
AB = [x + y for x in A for y in B]
we thus iterate over the elements in A and for each element x in A, we iterate over B, and then add x + y to the list.
Or for a variadic number of lists, and with a generator:
from itertools import product
map(''.join, product(A, B))
This can be easily extended to a variable number of elements, like:
>>> A = ['AA','BB']; B = ['CC','DD']; C = ['EE', 'FF']
>>> list(map(''.join, product(A, B, C)))
['AACCEE', 'AACCFF', 'AADDEE', 'AADDFF', 'BBCCEE', 'BBCCFF', 'BBDDEE', 'BBDDFF']

Assign values to an array using two values

I am trying to generate an array that is the sum of two previous arrays. e.g
c = [A + B for A in a and B in b]
Here, get the error message
NameError: name 'B' is not defined
where
len(a) = len(b) = len(c)
Please can you let me know what I am doing wrong. Thanks.
The boolean and operator does not wire iterables together, it evaluates the truthiness (or falsiness) of its two operands.
What you're looking for is zip:
c = [A + B for A, B in zip(a, b)]
Items from the two iterables are successively assigned to A to B until one of the two is exhausted. B is now defined!
It should be
c = [A + B for A in a for B in b]
for instead of and. You might want to consider using numpy, where you can add 2 matrices directly, and more efficient.
'for' does not work the way you want it to work.
You could use zip().
A = [1,2,3]
B = [4,5,6]
c = [ a + b for a,b in zip(A,B)]
zip iterates through A & B and produces tuples.
To see what this looks like try:
[ x for x in zip(A,B)]

Using itertools.permutation where r > n

I am trying to generate all permuations of a set of items and the my R needs to be larger than the size of the set of items
Here is an example :
itertools.permutations ("ABC", 4)
this always returns 0 items as R > N.
I want this
[A, A, A, A]
[A, A, A, B]
[A, A, B, A]
[A, B, A, A]
...
How can I achieve this?
You don't seem to want the permutation, but the Cartesian product:
itertools.product("ABC", repeat=4)
https://docs.python.org/3/library/itertools.html#itertools.product

Categories