Code Monkey home page Code Monkey logo

skompiler's Introduction

SKompiler: Translate trained SKLearn models to executable code in other languages

Build Status

The package provides a tool for transforming trained SKLearn models into other forms, such as SQL queries, Excel formulas, Portable Format for Analytics (PFA) files or Sympy expressions (which, in turn, can be translated to code in a variety of languages, such as C, Javascript, Rust, Julia, etc).

Requirements

  • Python 3.5 or later

Installation

The simplest way to install the package is via pip:

$ pip install SKompiler[full]

Note that the [full] option includes the installations of sympy, sqlalchemy and astor, which are necessary if you plan to convert SKompiler's expressions to sympy expressions (which, in turn, can be compiled to many other languages) or to SQLAlchemy expressions (which can be further translated to different SQL dialects) or to Python source code. If you do not need this functionality (say, you only need the raw SKompiler expressions or perhaps only the SQL conversions without the sympy ones), you may avoid the forced installation of all optional dependencies by simply writing

$ pip install SKompiler

(you are free to install any of the required extra dependencies, via separate calls to pip install, of course)

Usage

Introductory example

Let us start by walking through an introductory example. We begin by training a model on a small dataset:

from sklearn.datasets import load_iris
from sklearn.ensemble import RandomForestClassifier
X, y = load_iris(return_X_y=True)
m = RandomForestClassifier(n_estimators=3, max_depth=3).fit(X, y)

Suppose we need to express the logic of m.predict in SQLite. Here is how we can achieve that:

from skompiler import skompile
expr = skompile(m.predict)
sql = expr.to('sqlalchemy/sqlite')

Voila, the value of the sql variable is a query, which would compute the value of m.predict in pure SQL:

WITH _tmp1 AS
(SELECT .... FROM data)
_tmp2 AS
( ... )
SELECT ... from _tmp2 ...

Let us import the data into an in-memory SQLite database to test the generated query:

import sqlalchemy as sa
import pandas as pd
conn = sa.create_engine('sqlite://').connect()
df = pd.DataFrame(X, columns=['x1', 'x2', 'x3', 'x4']).reset_index()
df.to_sql('data', conn)

Our database now contains the table named data with the primary key index. We need to provide this information to SKompiler to have it generate the correct query:

sql = expr.to('sqlalchemy/sqlite', key_column='index', from_obj='data')

We can now query the data:

results = pd.read_sql(sql, conn)

and verify that the results match:

assert (results.values.ravel() == m.predict(X).ravel()).all()

Note that the generated SQL expression uses names x1, x2, x3 and x4 to refer to the input variables. We could have chosen different input variable names by writing:

expr = skompile(m.predict, ['a', 'b', 'c', 'd'])

Single-shot computation

Note that the generated SQL code splits the computation into sequential steps using with expressions. In some cases you might want to have the whole computation "inlined" into a single expression. You can achieve this by specifying multistage=False:

sql = expr.to('sqlalchemy/sqlite', multistage=False)

Note that in this case the resulting expression would typically be several times longer than the multistage version:

len(expr.to('sqlalchemy/sqlite'))
> 2262
len(expr.to('sqlalchemy/sqlite', multistage=False))
> 12973

Why so? Because, for a typical classifier (including the one used in this example)

predict(x) = argmax(predict_proba(x))

There is, however, no single argmax function in SQL, hence it has to be faked using the following logic:

predict(x) = if predict_proba(x)[0] == max(predict_proba(x)) then 0
                else if predict_proba(x)[1] == max(predict_proba(x)) then 1
                else 2

If SKompiler is not alowed to use a separate step to store the intermediate predict_proba outputs, it is forced to inline the same computation verbatim multiple times. To summarize, you should probably avoid the use of multistage=False in most cases.

Other formats

By changing the first parameter of the .to() call you may produce output in a variety of other formats besides SQLite:

  • sqlalchemy: raw SQLAlchemy expression (which is a dialect-independent way of representing SQL). Jokes aside, SQL is sometimes a totally valid choice for deploying models into production.

    Note that generated SQL may (depending on the chosen model and method) include functions exp, log and sqrt, which are not supported out of the box in SQLite. If you work with SQLite, you will need to add them separately via create_function. You can find an example of how this can be done in tests/evaluators.py in the SKompiler's source code.

  • sqlalchemy/<dialect>: SQL string in any of the SQLAlchemy-supported dialects (firebird, mssql, mysql, oracle, postgresql, sqlite, sybase). This is a convenience feature for those who are lazy to figure out how to compile raw SQLAlchemy to actual SQL.

  • excel: Excel formula. Ever tried dragging a random forest equation down along the table? Fun! Check out this short screencast to see how it can be done.

    NB: The screencast was recorded using a previous version, where multistage=False was the default option.

  • pfa: A dict with PFA code.

  • pfa/json or pfa/yaml: PFA code as a JSON or YAML string for those who are lazy to write json.dumps or yaml.dump. PyYAML should be installed in the latter case, of course.

  • sympy: A SymPy expression. Ever wanted to take a derivative of your model symbolically?

  • sympy/<lang>: Code in the language <lang>, generated via SymPy. Supported values for <lang> are c, cxx, rust, fortran, js, r, julia, mathematica, octave. Note that the quality of the generated code varies depending on the model, language and the value of the assign_to parameter. Again, this is just a convenience feature, you will get more control by dealing with sympy code printers manually.

    NB: Sympy translation does not support multistage mode at the moment, hence the resulting code will have repeated subexpressions (which can be extracted by means of Sympy itself, however).

  • python: Python syntax tree (the same you'd get via ast.parse). This (and the following three options) are mostly useful for debugging and testing.

  • python/code: Python source code. The generated code will contain references to custom functions, such as __argmax__, __sigmoid__, etc. To execute the code you will need to provide these in the locals dictionary. See skompiler.fromskast.python._eval_vars.

  • python/lambda: Python callable function (primarily useful for debugging and testing). Equivalent to calling expr.lambdify().

  • string: string, equivalent to str(expr).

Other models

So far this has been a fun two-weekends-long project, hence translation is implemented for a limited number of models. The most basic ones (linear models, decision trees, forests, gradient boosting, PCA, KMeans, MLP, Pipeline and a couple of preprocessors) are covered, however, and this is already sufficient to compile nontrivial constructions. For example:

m = Pipeline([('scale', StandardScaler()),
              ('dim_reduce', PCA(6)),
              ('cluster', KMeans(10)),
              ('classify', MLPClassifier([5, 4], 'tanh'))])

Even though this particular example probably does not make much sense from a machine learning perspective, it would happily compile both to Excel and SQL forms none the less.

How it works

The skompile procedure translates a given method into an intermediate syntactic representation (called SKompiler AST or SKAST). This representation uses a limited number of operations so it is reasonably simple to translate it into other forms.

In principle, SKAST's utility is not limited to sklearn models. Anything you translate into SKAST becomes automatically compileable to whatever output backends are implemented in SKompiler. Generating raw SKAST is quite straightforward:

from skompiler.dsl import ident, const
expr = const([[1,2],[3,4]]) @ ident('x', 2) + 12
expr.to('sqlalchemy/sqlite', 'result')
> SELECT 1 * x1 + 2 * x2 + 12 AS result1, 3 * x1 + 4 * x2 + 12 AS result2 
> FROM data

You can use repr(expr) on any SKAST expression to dump its unformatted internal representation for examination or str(expr) to get a somewhat-formatted view of it.

It is important to note, that for larger models (say, a random forest or a gradient boosted model with 500+ trees) the resulting SKAST expression tree may become deeper than Python's default recursion limit of 1000. As a result some translators may produce a RecursionError when processing such expressions. This can be solved by raising the system recursion limit to sufficiently high value:

import sys
sys.setrecursionlimit(10000)

Development

If you plan to develop or debug the package, consider installing it by running:

$ pip install -e .[dev]

from within the source distribution. This will install the package in "development mode" and include extra dependencies, useful for development.

You can then run the tests by typing

$ py.test

at the root of the source distribution.

Contributing

Feel free to contribute or report issues via Github:

Copyright & License

Copyright: 2018, Konstantin Tretyakov. License: MIT

skompiler's People

Contributors

konstantint avatar mepland avatar animator avatar darleybarreto avatar

Stargazers

Dave Bunten avatar George Pearse avatar  avatar simon-nx-ren avatar Jakub Ziarno avatar  avatar Jatin Jindal avatar Bulat Suleymanov avatar veteran ludologist avatar  avatar  avatar More Z. avatar Liam Clancy avatar Ian Herve avatar  avatar  avatar JF Binvignat avatar Rémi Adon avatar Dhulkifli Hussein avatar Cameron Irmas avatar kinnkinnzenn avatar Eric Ferreira avatar  avatar  avatar Ryan avatar Mattia Setzu avatar Jon Chun avatar Chandan Singh avatar Nuno Campos avatar  avatar Romain Lesur avatar Mohammed Hamdy avatar Khuyen Tran avatar  avatar Shashank Pachava avatar Bhav Sardana avatar Alexandr Zahatski avatar Michael Pedersen avatar Rezha Julio avatar Kyle Anthony Williams avatar Alejandro Giacometti avatar Alastair McKinley avatar  avatar Guillaume Gautier avatar Alcides Fonseca avatar Daniel van Strien avatar Yunfeng Wang avatar Rhet Turnbull avatar Chris Zubak-Skees avatar Simon Willison avatar Colin Dellow avatar geosmart avatar Abhishek Bishoyi avatar  avatar Ayush Kumar avatar  avatar  avatar Vidcentum avatar Anton Lastochkin avatar  avatar Anthony C Melson avatar Ted Harris avatar  avatar Jon Nordby avatar Ibrahim Sherif avatar Yu Qiu avatar Chris avatar Elena Treskova avatar welsonlee avatar  avatar  avatar Andrey  avatar Curtis Neiderer avatar  avatar Misa Ogura avatar Furkan Karadaş avatar Avinash Sooriyarachchi avatar comsaint avatar Rafael Bianchi avatar  avatar  avatar Pablo Cordero avatar Marcus Adair avatar Arnaldo Vera avatar Bryan Kaplan avatar  avatar Martin avatar David Rivkin avatar Ricardo Ander-Egg avatar Simone Francia avatar  avatar Vitor Baptista avatar Martin Valgur avatar  avatar Pietro Giuffrida avatar Sylvain Rousseau avatar Dmitry Ivankov avatar  avatar João avatar Paulo S. Costa avatar

Watchers

 avatar James Cloos avatar  avatar Ted Harris avatar Andrey  avatar  avatar  avatar  avatar Ryan Gosiaco avatar

skompiler's Issues

Export weights of a Random Forest

With this library, would be possible extract the weights of a Random Fores as for example in suppor vector machine or PLS or Neural Network? In this case, could you provide me some code, for after load the weights and training your model.

Thanks!
Pablo

SQLAlchemy Integration Broken with SQLAlchemy v2.0

SQLAlchemy has changed some of their function calls in newer versions which break SKompiler's SQLAlchemy methods. I did my testing with SQLAlchemy==2.0.21.

One simple fix is in the _iif() function return where

return sa.case([(cond, iftrue)], else_=iffalse)

should now be

return sa.case((cond, iftrue), else_=iffalse)

as the whens parameter is now a series of tuple arguments, not a list.

However, there is still an issue with the sa.select() calls. For example in translate() you can alter this line from

    result = sa.select(result.cols, from_obj=result.from_obj)

to

    result = sa.select(result.cols[0])

and get an output without errors, but the parameters are no longer replaced. The SQL output will be something like:

SELECT CASE WHEN ("Your Column Name Here" <= :param_1) THEN :param_2 ...

The from_obj parameter has been deprecated - along with all kwargs. After a quick look I don't see how to bring the values in from result.from_obj. This is also probably an issue with this sa.select() call in _make_cte().

There maybe other issues with the SQLAlchemy integration, these are just the two I found in my testing.

CC @konstantint

RecursionError: maximum recursion depth exceeded

Hi,

I am trying to use the expr.to('excel') utility of skompile, and I am getting following error:

RecursionError: maximum recursion depth exceeded

The error stack is as below:

---------------------------------------------------------------------------
RecursionError                            Traceback (most recent call last)
<ipython-input-150-7dcc4a57e7a5> in <module>
      1 expr = skompile(final_model.predict)
      2 print(expr)
----> 3 expr.to('excel')
      4 #expr.to('sqlalchemy/sqlite')

/remote/vghawk1/nayaka/soft/anaconda3/envs/tf2_pytorch/lib/python3.7/site-packages/skompiler/ast.py in to(self, target, *args, **kw)
    266             mod = import_module(module)
    267             translator = getattr(mod, callable)
--> 268         return translator(self, *dialect, *args, **kw)
    269 #endregion
    270 

/remote/vghawk1/nayaka/soft/anaconda3/envs/tf2_pytorch/lib/python3.7/site-packages/skompiler/fromskast/excel.py in translate(node, component, multistage, assign_to, multistage_subexpression_min_length, _max_subexpression_length)
     62                          multistage_subexpression_min_length=multistage_subexpression_min_length,
     63                          _max_subexpression_length=_max_subexpression_length)
---> 64     result = writer(node)
     65     if component is not None:
     66         result = result[component]

/remote/vghawk1/nayaka/soft/anaconda3/envs/tf2_pytorch/lib/python3.7/site-packages/skompiler/fromskast/_common.py in __call__(self, node, **kw)
     31 
     32     def __call__(self, node, **kw):
---> 33         return getattr(self, node.__class__.__name__)(node, **kw)
     34 
     35 

/remote/vghawk1/nayaka/soft/anaconda3/envs/tf2_pytorch/lib/python3.7/site-packages/skompiler/fromskast/_common.py in LFold(self, node, **kw)
     80         if not node.elems:
     81             raise ValueError("LFold expects at least one element")
---> 82         return self(reduce(lambda x, y: BinOp(node.op, x, y), node.elems), **kw)
     83 
     84     def Let(self, node, **kw):

/remote/vghawk1/nayaka/soft/anaconda3/envs/tf2_pytorch/lib/python3.7/site-packages/skompiler/fromskast/_common.py in __call__(self, node, **kw)
     31 
     32     def __call__(self, node, **kw):
---> 33         return getattr(self, node.__class__.__name__)(node, **kw)
     34 
     35 

/remote/vghawk1/nayaka/soft/anaconda3/envs/tf2_pytorch/lib/python3.7/site-packages/skompiler/fromskast/_common.py in BinOp(self, node, **kw)
     64         the operation elementwise and returns a list."""
     65 
---> 66         left = self(node.left, **kw)
     67         right = self(node.right, **kw)
     68         op = self(node.op, **kw)

... last 2 frames repeated, from the frame below ...

/remote/vghawk1/nayaka/soft/anaconda3/envs/tf2_pytorch/lib/python3.7/site-packages/skompiler/fromskast/_common.py in __call__(self, node, **kw)
     31 
     32     def __call__(self, node, **kw):
---> 33         return getattr(self, node.__class__.__name__)(node, **kw)
     34 
     35 

RecursionError: maximum recursion depth exceeded

Please help in resolving this issue.

Regards
Keki

Translation to another language is broken in Sympy 1.10

Hi, I tried the following code with Sympy 1.10

from skompiler import skompile
from sklearn.datasets import load_iris
from sklearn.ensemble import GradientBoostingRegressor

X, y = load_iris(return_X_y=True)

regr = GradientBoostingRegressor(random_state=1, max_depth=3, n_estimators=3)
res = regr.fit(X, y)
expr = skompile(res.predict)
rust = expr.to('sympy/cxx')
print(rust)

Which gives this error

envs/komp/lib/python3.9/site-packages/sklearn/utils/deprecation.py:103: FutureWarning: Attribute `n_features_` was deprecated in version 1.0 and will be removed in 1.2. Use `n_features_in_` instead.
  warnings.warn(msg, category=FutureWarning)
Traceback (most recent call last):
  File "test.py", line 11, in <module>
    rust = expr.to('sympy/cxx')
  File "SKompiler/skompiler/ast.py", line 268, in to
    return translator(self, *dialect, *args, **kw)
  File "SKompiler/skompiler/fromskast/sympy.py", line 67, in translate
    return to_code(syexpr, dialect, assign_to=assign_to, **kw)
  File "SKompiler/skompiler/fromskast/sympy.py", line 236, in to_code
    return _code_printers[dialect](syexpr, assign_to=assign_to, **kw)
  File "SKompiler/skompiler/fromskast/sympy.py", line 204, in <lambda>
    'cxx': lambda expr, **kw: sp.cxxcode(expr, user_functions=_ufns, **kw),
  File "envs/komp/lib/python3.9/site-packages/sympy/printing/codeprinter.py", line 865, in cxxcode
    return cxx_code_printers[standard.lower()](settings).doprint(expr, assign_to)
  File "envs/komp/lib/python3.9/site-packages/sympy/printing/codeprinter.py", line 150, in doprint
    lines = self._print(expr).splitlines()
  File "envs/komp/lib/python3.9/site-packages/sympy/printing/printer.py", line 331, in _print
    return printmethod(expr, **kwargs)
  File "envs/komp/lib/python3.9/site-packages/sympy/printing/codeprinter.py", line 375, in _print_Assignment
    return self._doprint_loops(rhs, lhs)
  File "envs/komp/lib/python3.9/site-packages/sympy/printing/codeprinter.py", line 184, in _doprint_loops
    dummies = get_contraction_structure(expr)
  File "envs/komp/lib/python3.9/site-packages/sympy/tensor/index_methods.py", line 443, in get_contraction_structure
    result[key] |= d[key]
TypeError: unsupported operand type(s) for |=: 'set' and 'Piecewise'

Issues using skompiler

I'm trying to make your skompiler to work with the former examples:

I tested this code using different environments:

from sklearn.datasets import load_iris
from sklearn.ensemble import RandomForestClassifier
X, y = load_iris(True)
m = RandomForestClassifier(n_estimators=3, max_depth=3).fit(X, y)
from skompiler import skompile
expr = skompile(m.predict)
sql = expr.to('python/code')

I get the following error using Python 3.5 with skompiler 0.5.1:

Traceback (most recent call last):
  File "test.py", line 6, in <module>
    expr = skompile(m.predict)
  File "/home/mario/.virtualenvs/skompiler2/lib/python3.5/site-packages/skompiler/api.py", line 76, in skompile
    return _translate(model, inputs, method)
  File "/home/mario/.virtualenvs/skompiler2/lib/python3.5/site-packages/skompiler/api.py", line 86, in _translate
    from .toskast.sklearn import translate as from_sklearn
  File "/home/mario/.virtualenvs/skompiler2/lib/python3.5/site-packages/skompiler/toskast/sklearn/__init__.py", line 22, in <module>
    from skompiler.dsl import ident, vector
  File "/home/mario/.virtualenvs/skompiler2/lib/python3.5/site-packages/skompiler/dsl.py", line 6, in <module>
    from . import ast
  File "/home/mario/.virtualenvs/skompiler2/lib/python3.5/site-packages/skompiler/ast.py", line 73, in <module>
    class ASTNode(object, metaclass=ASTNodeCreator, fields=None):
TypeError: type.__init__() takes no keyword arguments

I get a different error using Python 3.7 on a different machine:

Traceback (most recent call last):
  File "test.py", line 6, in <module>
    expr = skompile(m.predict)
  File "/home/mario/.virtualenvs/skompiler/lib/python3.7/site-packages/skompiler/api.py", line 76, in skompile
    return _translate(model, inputs, method)
  File "/home/mario/.virtualenvs/skompiler/lib/python3.7/site-packages/skompiler/api.py", line 86, in _translate
    from .toskast.sklearn import translate as from_sklearn
  File "/home/mario/.virtualenvs/skompiler/lib/python3.7/site-packages/skompiler/toskast/sklearn/__init__.py", line 87, in <module>
    @register(SVC, ['decision_function', 'predict'])
  File "/home/mario/.virtualenvs/skompiler/lib/python3.7/site-packages/skompiler/toskast/sklearn/__init__.py", line 64, in decorator
    def new_fn(model, inputs='x', method='predict'):
  File "/home/mario/.virtualenvs/skompiler/lib/python3.7/site-packages/singledispatch.py", line 202, in <lambda>
    return lambda f: register(cls, f)
  File "/home/mario/.virtualenvs/skompiler/lib/python3.7/site-packages/singledispatch.py", line 205, in register
    ns.cache_token = get_cache_token()
  File "/home/mario/.virtualenvs/skompiler/lib/python3.7/site-packages/singledispatch_helpers.py", line 159, in get_cache_token
    return ABCMeta._abc_invalidation_counter
AttributeError: type object 'ABCMeta' has no attribute '_abc_invalidation_counter'

Support XGBoost

Considering XGBoost is a favourite of Kaggle’s winners and widely used its support is highly wanted.
Can we expect such support in a near future?

Can you please write worked code example to convert to python ? E.g.on same random forest model to python

...
expr = skompile(estimator.predict, random_forest_pars)
func_python_2 = expr.to('python')
print(func_python_2)

Result:
<_ast.Call object at 0x0000024E39D1BE20>
=======================..
expr = skompile(estimator.predict, random_forest_pars, True/False)

Traceback (most recent call last):
File "C:\Users\irina\anaconda3\lib\site-packages\IPython\core\interactiveshell.py", line 3418, in run_code
exec(code_obj, self.user_global_ns, self.user_ns)
File "", line 1, in
runfile('C:/Users/irina/PycharmProjects/pythonProject/test_models.py', wdir='C:/Users/irina/PycharmProjects/pythonProject')
File "C:\Program Files\JetBrains\PyCharm Community Edition 2021.2.1\plugins\python-ce\helpers\pydev_pydev_bundle\pydev_umd.py", line 198, in runfile
pydev_imports.execfile(filename, global_vars, local_vars) # execute the script
File "C:\Program Files\JetBrains\PyCharm Community Edition 2021.2.1\plugins\python-ce\helpers\pydev_pydev_imps_pydev_execfile.py", line 18, in execfile
exec(compile(contents+"\n", file, 'exec'), glob, loc)
File "C:/Users/irina/PycharmProjects/pythonProject/test_models.py", line 62, in
expr = skompile(estimator.predict, random_forest_pars, False)
File "C:\Users\irina\anaconda3\lib\site-packages\skompiler\api.py", line 76, in skompile

SQL code for single object not need table

Can you please change SQL for single object ?
There we not need join by id and copy input to table "data", only params to parametrized query (e.g., :x1, :x2, etc.) or placeholders to substitution (e.g., #x1#, #x2#, etc.). Such query is simpler and works very fast (especially parametrized).

export to languages not working!

Hi!
I'm trying to export RandomForestRegressor to C/C++ (also tried for js).
Unfortunately got this exception:
unsupported operand type(s) for |=: 'Piecewise' and 'Piecewise'

Can you please help me?

Support GradientBoostingClassifier

Hello,
Now I'm struggling to export my GradientBoostingClassifier’s “predict_proba” method into c code. And in many examples, I saw it works for GradientBoostingRegressor model. But it seems not to work with GradientBoostingClassifier model.
Instead using GradientBoostingRegressor model, is there another way for GradientBoostingClassifier model?

I got an error message like this,
"ValueError: Method predict_proba is not supported (or not implemented yet) for GradientBoostingClassifier".

In addition, I also tried using “decision_function” method. But it didn’t work still with “AttributeError: ‘LogOddsEstimator’ object has no attribute ‘priors’” message.

Only getting first node of decision tree when converting to SQL

Model type is sklearn DecisionTreeClassifier

expr = skompile(m.predict)

The result is as expected...

IfThenElse(test=BinOp(op=LtEq(), left=IndexedIdentifier(id='x', index=11, size=12), right=NumberConstant(value=0.3329164981842041)), iftrue=IfThenElse(test=BinOp(op=LtEq(), left=IndexedIdentifier(id='x', index=11, size=12), right=NumberConstant(value=0.19435399770736694)), iftrue=IfThenElse(test=BinOp(op=LtEq(), left=IndexedIdentifier(id='x', index=11, size=12), right=NumberConstant(value=0.08358149975538254)), iftrue=NumberConstant(value=0), iffalse=NumberConstant(value=0)), iffalse=IfThenElse(test=BinOp(op=LtEq(), left=IndexedIdentifier(id='x', index=11, size=12), right=NumberConstant(value=0.2531224936246872)), iftrue=NumberConstant(value=0), iffalse=NumberConstant(value=0))), iffalse=IfThenElse(test=BinOp(op=LtEq(), left=IndexedIdentifier(id='x', index=11, size=12), right=NumberConstant(value=0.6303065121173859)), iftrue=IfThenElse(test=BinOp(op=LtEq(), left=IndexedIdentifier(id='x', index=10, size=12), right=NumberConstant(value=0.9569794833660126)), iftrue=NumberConstant(value=1), iffalse=NumberConstant(value=1)), iffalse=IfThenElse(test=BinOp(op=LtEq(), left=IndexedIdentifier(id='x', index=10, size=12), right=NumberConstant(value=0.9592015147209167)), iftrue=NumberConstant(value=1), iffalse=NumberConstant(value=1))))

However, when converting to SQL only the first node is converted and rather than nest in subsequent nodes the first node results are inserted.

sql = expr.to('sqlalchemy/mssql',multistage=True)

SELECT CASE WHEN (x12 <= 0.3329164981842041) THEN 0 ELSE 1 END AS y \nFROM data

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.