3

I'm beginner in python and I need to translate some code in R to Python.

I need to find one root per row in a dataset based in a dynamic function, the code in R is:

library(rootSolve
library(dplyr)
library(plyr)

 dataset = data.frame(A = c(10,20,30),B=c(20,10,40), FX = c("A+B-x","A-B+x","A*B-x"))

 sol<- adply(dataset,1, summarize,
               solution_0= uniroot.all(function(x)(eval(parse(text=as.character(FX),dataset))),lower = -10000, upper = 10000, tol = 0.00001))

This code return [30,-10,1200] as a solution for each row.

In python I read a documentation of optimize of sciPy package but i don't found a code that's work for me:

I tried a solutions like that below, but without sucess:

import pandas as pd
from scipy.optimize import fsolve as fs

data = {'A': [10,20,30],
        'B': [20,10,40],
        'FX': ["A+B-x","A-B+x","A*B-x"]}
df = pd.DataFrame(data)

def func(FX):
    return(exec(FX))

fs(func(df.FX),x0=0,args=df) 

Someone have idea how to solve this?

Very Thanks.

1 Answers1

2

SymPy is a symbolic math library for Python. Your question can be solved as:

import pandas as pd
from sympy import Symbol, solve
from sympy.parsing.sympy_parser import parse_expr

data = {'A': [10,20,30],
        'B': [20,10,40],
        'FX': ["A+B-x","A-B+x","A*B-x"]}

df = pd.DataFrame(data)

x = Symbol("x", real=True)

for index, row in df.iterrows():
    F = parse_expr(row['FX'], local_dict={'A': row['A'], 'B': row['B'], 'x':x})
    print (row['A'], row['B'], row['FX'], "-->", F, "-->", solve(F, x))

This outputs:

10 20 A+B-x --> 30 - x --> [30]
20 10 A-B+x --> x + 10 --> [-10]
30 40 A*B-x --> 1200 - x --> [1200]

Note that SymPy's solve returns a list of solutions. If you are sure there is always exactly one solution, just use solve(F, x)[0]. (Remember that unlike R, Python always starts indexing with 0.)

With list comprehension, you could write the solution as:

sol = [ solve(parse_expr(row['FX'], local_dict={'A': row['A'], 'B': row['B'], 'x':x}),
              x)[0] for _, row in df.iterrows() ]

If you have many columns, you can also create the dictionary with a loop: dict({c:row[c] for c in df.columns}, **{'x':x}) ). The weird ** syntax is needed if you want to combine the dictionaries inside the list comprehension. See this post about the union of dictionaries.

cols = df.columns # change this if you won't need all columns
sol = [ solve(parse_expr(row['FX'],
                         local_dict=dict({c:row[c] for c in cols}, **{'x':x}) ),
              x)[0].evalf() for _, row in df.iterrows() ]

PS: SymPy normally keeps the solutions in a symbolic form because it prefers exact expressions. When there are e.g. fractions or square roots, they are not evaluated immediately. To get the evaluated form, use evalf() as in solve(F, x)[0].evalf().

JohanC
  • 71,591
  • 8
  • 33
  • 66
  • Thanks, another question, is it necessary to create the dictionary for all columns? can i do something like R's "Attached" function? because my dataset has hundreds of columns and this dictionary would be quite large. Thank you again. – Adauto.Almeida Nov 12 '19 at 00:49
  • As each row needs its own values for the variables, you'll need to create (or update) the dictionary for each of the rows. If you know some rows only use a subset of the columns, you might restrict the dictionary to only these columns. Anyway, creating the dictionary is not the slowest part of the solution. – JohanC Nov 12 '19 at 07:49