From db42d622582e0b2028636030c9826b14c5382cc2 Mon Sep 17 00:00:00 2001 From: ZhengRyan Date: Sat, 10 Jun 2023 21:50:11 +0800 Subject: [PATCH] =?UTF-8?q?=E5=88=9D=E6=AC=A1=E6=8F=90=E4=BA=A4=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .editorconfig | 21 + .github/ISSUE_TEMPLATE.md | 15 + .gitignore | 106 + .travis.yml | 28 + AUTHORS.rst | 13 + CONTRIBUTING.rst | 128 ++ HISTORY.rst | 8 + LICENSE | 22 + MANIFEST.in | 11 + Makefile | 87 + README.md | 492 ++++ docs/Makefile | 20 + docs/authors.rst | 1 + docs/conf.py | 162 ++ docs/contributing.rst | 1 + docs/history.rst | 1 + docs/index.rst | 20 + docs/installation.rst | 51 + docs/make.bat | 36 + docs/readme.rst | 1 + docs/usage.rst | 7 + examples/tutorial_code.ipynb | 1974 +++++++++++++++++ images/RyanZheng.png | Bin 0 -> 41888 bytes ...6\345\271\262\351\245\255\344\272\272.png" | Bin 0 -> 27731 bytes requirements.txt | 3 + setup.cfg | 20 + setup.py | 49 + tests/__init__.py | 1 + tests/test_xgboost2sql.py | 32 + xgboost2sql/__init__.py | 7 + xgboost2sql/xgboost2sql.py | 245 ++ 31 files changed, 3562 insertions(+) create mode 100644 .editorconfig create mode 100644 .github/ISSUE_TEMPLATE.md create mode 100644 .gitignore create mode 100644 .travis.yml create mode 100644 AUTHORS.rst create mode 100644 CONTRIBUTING.rst create mode 100644 HISTORY.rst create mode 100644 LICENSE create mode 100644 MANIFEST.in create mode 100644 Makefile create mode 100644 README.md create mode 100644 docs/Makefile create mode 100644 docs/authors.rst create mode 100755 docs/conf.py create mode 100644 docs/contributing.rst create mode 100644 docs/history.rst create mode 100644 docs/index.rst create mode 100644 docs/installation.rst create mode 100644 docs/make.bat create mode 100644 docs/readme.rst create mode 100644 docs/usage.rst create mode 100644 examples/tutorial_code.ipynb create mode 100644 images/RyanZheng.png create mode 100644 "images/\351\255\224\351\203\275\346\225\260\346\215\256\345\271\262\351\245\255\344\272\272.png" create mode 100644 requirements.txt create mode 100644 setup.cfg create mode 100644 setup.py create mode 100644 tests/__init__.py create mode 100644 tests/test_xgboost2sql.py create mode 100644 xgboost2sql/__init__.py create mode 100644 xgboost2sql/xgboost2sql.py diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..d4a2c44 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,21 @@ +# http://editorconfig.org + +root = true + +[*] +indent_style = space +indent_size = 4 +trim_trailing_whitespace = true +insert_final_newline = true +charset = utf-8 +end_of_line = lf + +[*.bat] +indent_style = tab +end_of_line = crlf + +[LICENSE] +insert_final_newline = false + +[Makefile] +indent_style = tab diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md new file mode 100644 index 0000000..f712034 --- /dev/null +++ b/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,15 @@ +* xgboost2sql version: +* Python version: +* Operating System: + +### Description + +Describe what you were trying to get done. +Tell us what happened, what went wrong, and what you expected to happen. + +### What I Did + +``` +Paste the command(s) you ran and the output. +If there was a crash, please include the traceback here. +``` diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4c915d1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,106 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +env/ +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +*.egg-info/ +.installed.cfg +*.egg + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +.hypothesis/ +.pytest_cache/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# pyenv +.python-version + +# celery beat schedule file +celerybeat-schedule + +# SageMath parsed files +*.sage.py + +# dotenv +.env + +# virtualenv +.venv +venv/ +ENV/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ + +# IDE settings +.vscode/ +.idea/ diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..043f5dc --- /dev/null +++ b/.travis.yml @@ -0,0 +1,28 @@ +# Config file for automatic testing at travis-ci.com + +language: python +python: + - 3.8 + - 3.7 + - 3.6 + +# Command to install dependencies, e.g. pip install -r requirements.txt --use-mirrors +install: pip install -U tox-travis + +# Command to run tests, e.g. python setup.py test +script: tox + +# Assuming you have installed the travis-ci CLI tool, after you +# create the Github repo and add it to Travis, run the +# following command to finish PyPI deployment setup: +# $ travis encrypt --add deploy.password +deploy: + provider: pypi + distributions: sdist bdist_wheel + user: RyanZheng + password: + secure: PLEASE_REPLACE_ME + on: + tags: true + repo: ZhengRyan/xgboost2sql + python: 3.8 diff --git a/AUTHORS.rst b/AUTHORS.rst new file mode 100644 index 0000000..c4a54fa --- /dev/null +++ b/AUTHORS.rst @@ -0,0 +1,13 @@ +======= +Credits +======= + +Development Lead +---------------- + +* RyanZheng + +Contributors +------------ + +None yet. Why not be the first? diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst new file mode 100644 index 0000000..c9c0b86 --- /dev/null +++ b/CONTRIBUTING.rst @@ -0,0 +1,128 @@ +.. highlight:: shell + +============ +Contributing +============ + +Contributions are welcome, and they are greatly appreciated! Every little bit +helps, and credit will always be given. + +You can contribute in many ways: + +Types of Contributions +---------------------- + +Report Bugs +~~~~~~~~~~~ + +Report bugs at https://github.com/ZhengRyan/xgboost2sql/issues. + +If you are reporting a bug, please include: + +* Your operating system name and version. +* Any details about your local setup that might be helpful in troubleshooting. +* Detailed steps to reproduce the bug. + +Fix Bugs +~~~~~~~~ + +Look through the GitHub issues for bugs. Anything tagged with "bug" and "help +wanted" is open to whoever wants to implement it. + +Implement Features +~~~~~~~~~~~~~~~~~~ + +Look through the GitHub issues for features. Anything tagged with "enhancement" +and "help wanted" is open to whoever wants to implement it. + +Write Documentation +~~~~~~~~~~~~~~~~~~~ + +xgboost2sql could always use more documentation, whether as part of the +official xgboost2sql docs, in docstrings, or even on the web in blog posts, +articles, and such. + +Submit Feedback +~~~~~~~~~~~~~~~ + +The best way to send feedback is to file an issue at https://github.com/ZhengRyan/xgboost2sql/issues. + +If you are proposing a feature: + +* Explain in detail how it would work. +* Keep the scope as narrow as possible, to make it easier to implement. +* Remember that this is a volunteer-driven project, and that contributions + are welcome :) + +Get Started! +------------ + +Ready to contribute? Here's how to set up `xgboost2sql` for local development. + +1. Fork the `xgboost2sql` repo on GitHub. +2. Clone your fork locally:: + + $ git clone git@github.com:your_name_here/xgboost2sql.git + +3. Install your local copy into a virtualenv. Assuming you have virtualenvwrapper installed, this is how you set up your fork for local development:: + + $ mkvirtualenv xgboost2sql + $ cd xgboost2sql/ + $ python setup.py develop + +4. Create a branch for local development:: + + $ git checkout -b name-of-your-bugfix-or-feature + + Now you can make your changes locally. + +5. When you're done making changes, check that your changes pass flake8 and the + tests, including testing other Python versions with tox:: + + $ flake8 xgboost2sql tests + $ python setup.py test or pytest + $ tox + + To get flake8 and tox, just pip install them into your virtualenv. + +6. Commit your changes and push your branch to GitHub:: + + $ git add . + $ git commit -m "Your detailed description of your changes." + $ git push origin name-of-your-bugfix-or-feature + +7. Submit a pull request through the GitHub website. + +Pull Request Guidelines +----------------------- + +Before you submit a pull request, check that it meets these guidelines: + +1. The pull request should include tests. +2. If the pull request adds functionality, the docs should be updated. Put + your new functionality into a function with a docstring, and add the + feature to the list in README.rst. +3. The pull request should work for Python 3.5, 3.6, 3.7 and 3.8, and for PyPy. Check + https://travis-ci.com/ZhengRyan/xgboost2sql/pull_requests + and make sure that the tests pass for all supported Python versions. + +Tips +---- + +To run a subset of tests:: + +$ pytest tests.test_xgboost2sql + + +Deploying +--------- + +A reminder for the maintainers on how to deploy. +Make sure all your changes are committed (including an entry in HISTORY.rst). +Then run:: + +$ bump2version patch # possible: major / minor / patch +$ git push +$ git push --tags + +Travis will then deploy to PyPI if tests pass. diff --git a/HISTORY.rst b/HISTORY.rst new file mode 100644 index 0000000..4a6d9ae --- /dev/null +++ b/HISTORY.rst @@ -0,0 +1,8 @@ +======= +History +======= + +0.1.0 (2023-06-04) +------------------ + +* First release on PyPI. diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..dda4589 --- /dev/null +++ b/LICENSE @@ -0,0 +1,22 @@ +MIT License + +Copyright (c) 2023, RyanZheng + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 0000000..965b2dd --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1,11 @@ +include AUTHORS.rst +include CONTRIBUTING.rst +include HISTORY.rst +include LICENSE +include README.rst + +recursive-include tests * +recursive-exclude * __pycache__ +recursive-exclude * *.py[co] + +recursive-include docs *.rst conf.py Makefile make.bat *.jpg *.png *.gif diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..7106847 --- /dev/null +++ b/Makefile @@ -0,0 +1,87 @@ +.PHONY: clean clean-build clean-pyc clean-test coverage dist docs help install lint lint/flake8 +.DEFAULT_GOAL := help + +define BROWSER_PYSCRIPT +import os, webbrowser, sys + +from urllib.request import pathname2url + +webbrowser.open("file://" + pathname2url(os.path.abspath(sys.argv[1]))) +endef +export BROWSER_PYSCRIPT + +define PRINT_HELP_PYSCRIPT +import re, sys + +for line in sys.stdin: + match = re.match(r'^([a-zA-Z_-]+):.*?## (.*)$$', line) + if match: + target, help = match.groups() + print("%-20s %s" % (target, help)) +endef +export PRINT_HELP_PYSCRIPT + +BROWSER := python -c "$$BROWSER_PYSCRIPT" + +help: + @python -c "$$PRINT_HELP_PYSCRIPT" < $(MAKEFILE_LIST) + +clean: clean-build clean-pyc clean-test ## remove all build, test, coverage and Python artifacts + +clean-build: ## remove build artifacts + rm -fr build/ + rm -fr dist/ + rm -fr .eggs/ + find . -name '*.egg-info' -exec rm -fr {} + + find . -name '*.egg' -exec rm -f {} + + +clean-pyc: ## remove Python file artifacts + find . -name '*.pyc' -exec rm -f {} + + find . -name '*.pyo' -exec rm -f {} + + find . -name '*~' -exec rm -f {} + + find . -name '__pycache__' -exec rm -fr {} + + +clean-test: ## remove test and coverage artifacts + rm -fr .tox/ + rm -f .coverage + rm -fr htmlcov/ + rm -fr .pytest_cache + +lint/flake8: ## check style with flake8 + flake8 xgboost2sql tests + +lint: lint/flake8 ## check style + +test: ## run tests quickly with the default Python + pytest + +test-all: ## run tests on every Python version with tox + tox + +coverage: ## check code coverage quickly with the default Python + coverage run --source xgboost2sql -m pytest + coverage report -m + coverage html + $(BROWSER) htmlcov/index.html + +docs: ## generate Sphinx HTML documentation, including API docs + rm -f docs/xgboost2sql.rst + rm -f docs/modules.rst + sphinx-apidoc -o docs/ xgboost2sql + $(MAKE) -C docs clean + $(MAKE) -C docs html + $(BROWSER) docs/_build/html/index.html + +servedocs: docs ## compile the docs watching for changes + watchmedo shell-command -p '*.rst' -c '$(MAKE) -C docs html' -R -D . + +release: dist ## package and upload a release + twine upload dist/* + +dist: clean ## builds source and wheel package + python setup.py sdist + python setup.py bdist_wheel + ls -l dist + +install: clean ## install the package to the active Python's site-packages + python setup.py install diff --git a/README.md b/README.md new file mode 100644 index 0000000..9bae7d1 --- /dev/null +++ b/README.md @@ -0,0 +1,492 @@ +# XGBoost模型转sql语句工具包 +现在是大数据量的时代,我们开发的模型要应用在特别大的待预测集上,使用单机的python,需要预测2、3天,甚至更久,中途很有可能中断。因此需要通过分布式的方式来预测。这个工具包就是实现了将训练好的python模型,转换成sql语句。将生成的sql语句可以放到大数据环境中进行分布式执行预测,能比单机的python预测快好几个量级 + + +## 思想碰撞 + +| 微信 | 微信公众号 | +| :---: | :----: | +| RyanZheng.png | 魔都数据干饭人.png | +| RyanZheng | 魔都数据干饭人 | + + +> 仓库地址:https://github.com/ZhengRyan/xgboost2sql +> +> 微信公众号文章: +> +> pipy包:https://pypi.org/project/xgboost2sql/ + + + +## 环境准备 +可以不用单独创建虚拟环境,因为对包的依赖没有版本要求 + +### `xgboost2sql` 安装 +pip install(pip安装) + +```bash +pip install xgboost2sql # to install +pip install -U xgboost2sql # to upgrade +``` + +Source code install(源码安装) + +```bash +python setup.py install +``` + +### 运行样例 +####【注意:::核验对比python模型预测出来的结果和sql语句预测出来的结果是否一致请查看"https://github.com/ZhengRyan/xgboost2sql/examples/tutorial_code.ipynb"教程代码】 + ++ 导入相关依赖 +```python +import xgboost as xgb +from sklearn.datasets import make_classification +from sklearn.model_selection import train_test_split +from xgboost2sql import XGBoost2Sql +``` + ++ 训练1个xgboost二分类模型 +```python +X, y = make_classification(n_samples=10000, + n_features=10, + n_informative=3, + n_redundant=2, + n_repeated=0, + n_classes=2, + weights=[0.7, 0.3], + flip_y=0.1, + random_state=1024) + +X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=1024) + +###训练模型 +model = xgb.XGBClassifier(n_estimators=3) +model.fit(X_train, y_train) +xgb.to_graphviz(model) +``` ++ 使用xgboost2sql工具包将模型转换成的sql语句 +```python +xgb2sql = XGBoost2Sql() +sql_str = xgb2sql.transform(model) +``` + ++ 将sql语句保存 +```python +xgb2sql.save() +``` + ++ 将sql语句打印出来 +```python +print(sql_str) +``` + +```sql +select key,1 / (1 + exp(-(tree_1_score + tree_2_score + tree_3_score)+(-0.0))) as score + from ( + select key, + --tree1 + case when (f9<-1.64164519 or f9 is null) then + case when (f3<-4.19117069 or f3 is null) then + case when (f2<-1.31743848 or f2 is null) then + -0.150000006 + else + -0.544186056 + end + else + case when (f3<1.23432565 or f3 is null) then + case when (f7<-2.55682254 or f7 is null) then + -0.200000018 + else + case when (f5<0.154983491 or f5 is null) then + 0.544721723 + else + case when (f3<0.697217584 or f3 is null) then + -0.150000006 + else + 0.333333373 + end + end + end + else + case when (f5<-1.0218116 or f5 is null) then + case when (f0<-0.60882163 or f0 is null) then + case when (f2<0.26019755 or f2 is null) then + 0.0666666701 + else + -0.300000012 + end + else + -0.520000041 + end + else + 0.333333373 + end + end + end + else + case when (f9<1.60392439 or f9 is null) then + case when (f3<0.572542191 or f3 is null) then + case when (f5<0.653370142 or f5 is null) then + case when (f7<-0.765973091 or f7 is null) then + case when (f3<-0.432390809 or f3 is null) then + 0.204000011 + else + -0.485454559 + end + else + case when (f3<-1.20459461 or f3 is null) then + -0.5104478 + else + 0.441509455 + end + end + else + case when (f7<0.133017987 or f7 is null) then + case when (f8<0.320554674 or f8 is null) then + -0.290322572 + else + 0.368339777 + end + else + case when (f8<-0.211985052 or f8 is null) then + 0.504000008 + else + -0.525648415 + end + end + end + else + case when (f7<2.22314501 or f7 is null) then + case when (f8<-0.00532855326 or f8 is null) then + case when (f8<-0.204920739 or f8 is null) then + -0.533991575 + else + -0.200000018 + end + else + 0.428571463 + end + else + case when (f3<1.33772755 or f3 is null) then + case when (f0<-0.975171864 or f0 is null) then + 0.163636371 + else + 0.51818186 + end + else + -0 + end + end + end + else + case when (f3<1.77943277 or f3 is null) then + case when (f7<-0.469875157 or f7 is null) then + case when (f3<-0.536645889 or f3 is null) then + case when (f9<1.89841866 or f9 is null) then + -0 + else + 0.333333373 + end + else + case when (f4<-2.43660188 or f4 is null) then + 0.150000006 + else + 0.551020443 + end + end + else + case when (f1<-0.0788691565 or f1 is null) then + 0.150000006 + else + -0.375 + end + end + else + case when (f4<-1.73232496 or f4 is null) then + -0.150000006 + else + case when (f6<-1.6080606 or f6 is null) then + -0.150000006 + else + case when (f7<-0.259483218 or f7 is null) then + -0.558620751 + else + -0.300000012 + end + end + end + end + end + end + as tree_1_score, +--tree2 + case when (f9<-1.64164519 or f9 is null) then + case when (f3<-4.19117069 or f3 is null) then + case when (f0<0.942570388 or f0 is null) then + -0.432453066 + else + -0.128291652 + end + else + case when (f3<1.23432565 or f3 is null) then + case when (f7<-2.55682254 or f7 is null) then + -0.167702854 + else + case when (f5<0.154983491 or f5 is null) then + case when (f1<2.19985676 or f1 is null) then + 0.41752997 + else + 0.115944751 + end + else + 0.115584135 + end + end + else + case when (f5<-1.0218116 or f5 is null) then + case when (f0<-0.60882163 or f0 is null) then + -0.119530827 + else + -0.410788596 + end + else + 0.28256765 + end + end + end + else + case when (f9<1.60392439 or f9 is null) then + case when (f3<0.460727394 or f3 is null) then + case when (f5<0.653370142 or f5 is null) then + case when (f7<-0.933565617 or f7 is null) then + case when (f3<-0.572475374 or f3 is null) then + 0.182491601 + else + -0.377898693 + end + else + case when (f3<-1.20459461 or f3 is null) then + -0.392539263 + else + 0.352721155 + end + end + else + case when (f7<0.207098693 or f7 is null) then + case when (f8<0.498489976 or f8 is null) then + -0.193351224 + else + 0.29298231 + end + else + case when (f8<-0.117464997 or f8 is null) then + 0.400667101 + else + -0.402199954 + end + end + end + else + case when (f7<1.98268723 or f7 is null) then + case when (f8<-0.00532855326 or f8 is null) then + case when (f7<1.36281848 or f7 is null) then + -0.408002198 + else + -0.236123681 + end + else + case when (f5<1.14038813 or f5 is null) then + 0.404326111 + else + -0.110877581 + end + end + else + case when (f3<1.56952488 or f3 is null) then + case when (f5<2.14646816 or f5 is null) then + 0.409404457 + else + 0.0696995854 + end + else + -0.32059738 + end + end + end + else + case when (f3<1.77943277 or f3 is null) then + case when (f7<-0.469875157 or f7 is null) then + case when (f3<-0.536645889 or f3 is null) then + case when (f9<1.89841866 or f9 is null) then + -0 + else + 0.28256765 + end + else + 0.419863999 + end + else + case when (f3<0.444227457 or f3 is null) then + -0.34664312 + else + 0.0693304539 + end + end + else + case when (f4<-1.10089087 or f4 is null) then + case when (f3<2.3550868 or f3 is null) then + 0.0147894565 + else + -0.331404865 + end + else + -0.421277165 + end + end + end + end + as tree_2_score, +--tree3 + case when (f9<-1.64164519 or f9 is null) then + case when (f3<-4.19117069 or f3 is null) then + case when (f4<-1.30126143 or f4 is null) then + -0.0772174299 + else + -0.374165356 + end + else + case when (f3<1.23432565 or f3 is null) then + case when (f7<-2.55682254 or f7 is null) then + -0.142005175 + else + case when (f5<0.154983491 or f5 is null) then + case when (f7<3.59379435 or f7 is null) then + 0.352122813 + else + 0.132789165 + end + else + 0.0924336985 + end + end + else + case when (f5<-1.0218116 or f5 is null) then + case when (f0<-0.60882163 or f0 is null) then + -0.0954768136 + else + -0.351594836 + end + else + 0.245992288 + end + end + end + else + case when (f9<1.60392439 or f9 is null) then + case when (f3<0.347133756 or f3 is null) then + case when (f5<0.661561131 or f5 is null) then + case when (f7<-0.933565617 or f7 is null) then + case when (f3<-0.472413659 or f3 is null) then + 0.116336405 + else + -0.313245147 + end + else + case when (f3<-1.5402329 or f3 is null) then + -0.352897167 + else + 0.311400592 + end + end + else + case when (f7<0.275665522 or f7 is null) then + case when (f8<0.403402805 or f8 is null) then + -0.292606086 + else + 0.220064178 + end + else + case when (f8<-0.0442957953 or f8 is null) then + 0.350784421 + else + -0.336107522 + end + end + end + else + case when (f7<1.77503061 or f7 is null) then + case when (f8<0.196157426 or f8 is null) then + case when (f7<1.36281848 or f7 is null) then + -0.3376683 + else + -0.0711223111 + end + else + case when (f7<-0.661211252 or f7 is null) then + 0.434363276 + else + -0.219307661 + end + end + else + case when (f3<1.37940335 or f3 is null) then + case when (f6<1.34894884 or f6 is null) then + 0.367155522 + else + 0.124757253 + end + else + -0.293739736 + end + end + end + else + case when (f3<1.77943277 or f3 is null) then + case when (f7<-0.469875157 or f7 is null) then + case when (f3<-0.536645889 or f3 is null) then + case when (f9<1.89841866 or f9 is null) then + -0 + else + 0.245992288 + end + else + case when (f0<1.60565615 or f0 is null) then + 0.357973605 + else + 0.193993196 + end + end + else + case when (f9<1.89456153 or f9 is null) then + -0.276471078 + else + 0.111896731 + end + end + else + case when (f1<1.35706067 or f1 is null) then + case when (f4<-1.10089087 or f4 is null) then + case when (f3<2.3550868 or f3 is null) then + 0.0119848112 + else + -0.284813672 + end + else + -0.376859784 + end + else + case when (f2<-0.25748384 or f2 is null) then + 0.0723158419 + else + -0.253415495 + end + end + end + end + end + as tree_3_score + from data_table) +``` + + diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 0000000..4786e82 --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,20 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = python -msphinx +SPHINXPROJ = xgboost2sql +SOURCEDIR = . +BUILDDIR = _build + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/docs/authors.rst b/docs/authors.rst new file mode 100644 index 0000000..e122f91 --- /dev/null +++ b/docs/authors.rst @@ -0,0 +1 @@ +.. include:: ../AUTHORS.rst diff --git a/docs/conf.py b/docs/conf.py new file mode 100755 index 0000000..29c03e4 --- /dev/null +++ b/docs/conf.py @@ -0,0 +1,162 @@ +#!/usr/bin/env python +# +# xgboost2sql documentation build configuration file, created by +# sphinx-quickstart on Fri Jun 9 13:47:02 2017. +# +# This file is execfile()d with the current directory set to its +# containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +# If extensions (or modules to document with autodoc) are in another +# directory, add these directories to sys.path here. If the directory is +# relative to the documentation root, use os.path.abspath to make it +# absolute, like shown here. +# +import os +import sys +sys.path.insert(0, os.path.abspath('..')) + +import xgboost2sql + +# -- General configuration --------------------------------------------- + +# If your documentation needs a minimal Sphinx version, state it here. +# +# needs_sphinx = '1.0' + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones. +extensions = ['sphinx.ext.autodoc', 'sphinx.ext.viewcode'] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix(es) of source filenames. +# You can specify multiple suffix as a list of string: +# +# source_suffix = ['.rst', '.md'] +source_suffix = '.rst' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = 'xgboost2sql' +copyright = "2023, RyanZheng" +author = "RyanZheng" + +# The version info for the project you're documenting, acts as replacement +# for |version| and |release|, also used in various other places throughout +# the built documents. +# +# The short X.Y version. +version = xgboost2sql.__version__ +# The full version, including alpha/beta/rc tags. +release = xgboost2sql.__version__ + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +# +# This is also used if you do content translation via gettext catalogs. +# Usually you set "language" from the command line for these cases. +language = None + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +# This patterns also effect to html_static_path and html_extra_path +exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +# If true, `todo` and `todoList` produce output, else they produce nothing. +todo_include_todos = False + + +# -- Options for HTML output ------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +# +html_theme = 'alabaster' + +# Theme options are theme-specific and customize the look and feel of a +# theme further. For a list of options available for each theme, see the +# documentation. +# +# html_theme_options = {} + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] + + +# -- Options for HTMLHelp output --------------------------------------- + +# Output file base name for HTML help builder. +htmlhelp_basename = 'xgboost2sqldoc' + + +# -- Options for LaTeX output ------------------------------------------ + +latex_elements = { + # The paper size ('letterpaper' or 'a4paper'). + # + # 'papersize': 'letterpaper', + + # The font size ('10pt', '11pt' or '12pt'). + # + # 'pointsize': '10pt', + + # Additional stuff for the LaTeX preamble. + # + # 'preamble': '', + + # Latex figure (float) alignment + # + # 'figure_align': 'htbp', +} + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, author, documentclass +# [howto, manual, or own class]). +latex_documents = [ + (master_doc, 'xgboost2sql.tex', + 'xgboost2sql Documentation', + 'RyanZheng', 'manual'), +] + + +# -- Options for manual page output ------------------------------------ + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + (master_doc, 'xgboost2sql', + 'xgboost2sql Documentation', + [author], 1) +] + + +# -- Options for Texinfo output ---------------------------------------- + +# Grouping the document tree into Texinfo files. List of tuples +# (source start file, target name, title, author, +# dir menu entry, description, category) +texinfo_documents = [ + (master_doc, 'xgboost2sql', + 'xgboost2sql Documentation', + author, + 'xgboost2sql', + 'One line description of project.', + 'Miscellaneous'), +] + + + diff --git a/docs/contributing.rst b/docs/contributing.rst new file mode 100644 index 0000000..e582053 --- /dev/null +++ b/docs/contributing.rst @@ -0,0 +1 @@ +.. include:: ../CONTRIBUTING.rst diff --git a/docs/history.rst b/docs/history.rst new file mode 100644 index 0000000..2506499 --- /dev/null +++ b/docs/history.rst @@ -0,0 +1 @@ +.. include:: ../HISTORY.rst diff --git a/docs/index.rst b/docs/index.rst new file mode 100644 index 0000000..9e75919 --- /dev/null +++ b/docs/index.rst @@ -0,0 +1,20 @@ +Welcome to xgboost2sql's documentation! +====================================== + +.. toctree:: + :maxdepth: 2 + :caption: Contents: + + readme + installation + usage + modules + contributing + authors + history + +Indices and tables +================== +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` diff --git a/docs/installation.rst b/docs/installation.rst new file mode 100644 index 0000000..5601a84 --- /dev/null +++ b/docs/installation.rst @@ -0,0 +1,51 @@ +.. highlight:: shell + +============ +Installation +============ + + +Stable release +-------------- + +To install xgboost2sql, run this command in your terminal: + +.. code-block:: console + + $ pip install xgboost2sql + +This is the preferred method to install xgboost2sql, as it will always install the most recent stable release. + +If you don't have `pip`_ installed, this `Python installation guide`_ can guide +you through the process. + +.. _pip: https://pip.pypa.io +.. _Python installation guide: http://docs.python-guide.org/en/latest/starting/installation/ + + +From sources +------------ + +The sources for xgboost2sql can be downloaded from the `Github repo`_. + +You can either clone the public repository: + +.. code-block:: console + + $ git clone git://github.com/ZhengRyan/xgboost2sql + +Or download the `tarball`_: + +.. code-block:: console + + $ curl -OJL https://github.com/ZhengRyan/xgboost2sql/tarball/master + +Once you have a copy of the source, you can install it with: + +.. code-block:: console + + $ python setup.py install + + +.. _Github repo: https://github.com/ZhengRyan/xgboost2sql +.. _tarball: https://github.com/ZhengRyan/xgboost2sql/tarball/master diff --git a/docs/make.bat b/docs/make.bat new file mode 100644 index 0000000..b688a60 --- /dev/null +++ b/docs/make.bat @@ -0,0 +1,36 @@ +@ECHO OFF + +pushd %~dp0 + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=python -msphinx +) +set SOURCEDIR=. +set BUILDDIR=_build +set SPHINXPROJ=xgboost2sql + +if "%1" == "" goto help + +%SPHINXBUILD% >NUL 2>NUL +if errorlevel 9009 ( + echo. + echo.The Sphinx module was not found. Make sure you have Sphinx installed, + echo.then set the SPHINXBUILD environment variable to point to the full + echo.path of the 'sphinx-build' executable. Alternatively you may add the + echo.Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.http://sphinx-doc.org/ + exit /b 1 +) + +%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% +goto end + +:help +%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% + +:end +popd diff --git a/docs/readme.rst b/docs/readme.rst new file mode 100644 index 0000000..72a3355 --- /dev/null +++ b/docs/readme.rst @@ -0,0 +1 @@ +.. include:: ../README.rst diff --git a/docs/usage.rst b/docs/usage.rst new file mode 100644 index 0000000..0b70e11 --- /dev/null +++ b/docs/usage.rst @@ -0,0 +1,7 @@ +===== +Usage +===== + +To use xgboost2sql in a project:: + + import xgboost2sql diff --git a/examples/tutorial_code.ipynb b/examples/tutorial_code.ipynb new file mode 100644 index 0000000..89cefb3 --- /dev/null +++ b/examples/tutorial_code.ipynb @@ -0,0 +1,1974 @@ +{ + "cells": [ + { + "cell_type": "raw", + "metadata": {}, + "source": [ + "一、训练1个xgboost二分类模型\n", + "二、使用模型对测试数据集进行预测\n", + "三、使用xgboost2sql包将模型转换成的sql语句\n", + "四、使用模型转换成的sql语句对测试数据集进行预测\n", + "五、对比python模型预测出来的结果和sql语句预测出来的结果是否一致(很重要,一定要认真核对)\n", + "六、将sql保存成文件" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "pip install xgboost2sql" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "f9<-1.64164519\n", + "\n", + "\n", + "\n", + "1\n", + "\n", + "f3<-4.19117069\n", + "\n", + "\n", + "\n", + "0->1\n", + "\n", + "\n", + "yes, missing\n", + "\n", + "\n", + "\n", + "2\n", + "\n", + "f9<1.60392439\n", + "\n", + "\n", + "\n", + "0->2\n", + "\n", + "\n", + "no\n", + "\n", + "\n", + "\n", + "3\n", + "\n", + "f2<-1.31743848\n", + "\n", + "\n", + "\n", + "1->3\n", + "\n", + "\n", + "yes, missing\n", + "\n", + "\n", + "\n", + "4\n", + "\n", + "f3<1.23432565\n", + "\n", + "\n", + "\n", + "1->4\n", + "\n", + "\n", + "no\n", + "\n", + "\n", + "\n", + "5\n", + "\n", + "f3<0.572542191\n", + "\n", + "\n", + "\n", + "2->5\n", + "\n", + "\n", + "yes, missing\n", + "\n", + "\n", + "\n", + "6\n", + "\n", + "f3<1.77943277\n", + "\n", + "\n", + "\n", + "2->6\n", + "\n", + "\n", + "no\n", + "\n", + "\n", + "\n", + "7\n", + "\n", + "leaf=-0.150000006\n", + "\n", + "\n", + "\n", + "3->7\n", + "\n", + "\n", + "yes, missing\n", + "\n", + "\n", + "\n", + "8\n", + "\n", + "leaf=-0.544186056\n", + "\n", + "\n", + "\n", + "3->8\n", + "\n", + "\n", + "no\n", + "\n", + "\n", + "\n", + "9\n", + "\n", + "f7<-2.55682254\n", + "\n", + "\n", + "\n", + "4->9\n", + "\n", + "\n", + "yes, missing\n", + "\n", + "\n", + "\n", + "10\n", + "\n", + "f5<-1.0218116\n", + "\n", + "\n", + "\n", + "4->10\n", + "\n", + "\n", + "no\n", + "\n", + "\n", + "\n", + "15\n", + "\n", + "leaf=-0.200000018\n", + "\n", + "\n", + "\n", + "9->15\n", + "\n", + "\n", + "yes, missing\n", + "\n", + "\n", + "\n", + "16\n", + "\n", + "f5<0.154983491\n", + "\n", + "\n", + "\n", + "9->16\n", + "\n", + "\n", + "no\n", + "\n", + "\n", + "\n", + "17\n", + "\n", + "f0<-0.60882163\n", + "\n", + "\n", + "\n", + "10->17\n", + "\n", + "\n", + "yes, missing\n", + "\n", + "\n", + "\n", + "18\n", + "\n", + "leaf=0.333333373\n", + "\n", + "\n", + "\n", + "10->18\n", + "\n", + "\n", + "no\n", + "\n", + "\n", + "\n", + "27\n", + "\n", + "leaf=0.544721723\n", + "\n", + "\n", + "\n", + "16->27\n", + "\n", + "\n", + "yes, missing\n", + "\n", + "\n", + "\n", + "28\n", + "\n", + "f3<0.697217584\n", + "\n", + "\n", + "\n", + "16->28\n", + "\n", + "\n", + "no\n", + "\n", + "\n", + "\n", + "45\n", + "\n", + "leaf=-0.150000006\n", + "\n", + "\n", + "\n", + "28->45\n", + "\n", + "\n", + "yes, missing\n", + "\n", + "\n", + "\n", + "46\n", + "\n", + "leaf=0.333333373\n", + "\n", + "\n", + "\n", + "28->46\n", + "\n", + "\n", + "no\n", + "\n", + "\n", + "\n", + "29\n", + "\n", + "f2<0.26019755\n", + "\n", + "\n", + "\n", + "17->29\n", + "\n", + "\n", + "yes, missing\n", + "\n", + "\n", + "\n", + "30\n", + "\n", + "leaf=-0.520000041\n", + "\n", + "\n", + "\n", + "17->30\n", + "\n", + "\n", + "no\n", + "\n", + "\n", + "\n", + "47\n", + "\n", + "leaf=0.0666666701\n", + "\n", + "\n", + "\n", + "29->47\n", + "\n", + "\n", + "yes, missing\n", + "\n", + "\n", + "\n", + "48\n", + "\n", + "leaf=-0.300000012\n", + "\n", + "\n", + "\n", + "29->48\n", + "\n", + "\n", + "no\n", + "\n", + "\n", + "\n", + "11\n", + "\n", + "f5<0.653370142\n", + "\n", + "\n", + "\n", + "5->11\n", + "\n", + "\n", + "yes, missing\n", + "\n", + "\n", + "\n", + "12\n", + "\n", + "f7<2.22314501\n", + "\n", + "\n", + "\n", + "5->12\n", + "\n", + "\n", + "no\n", + "\n", + "\n", + "\n", + "13\n", + "\n", + "f7<-0.469875157\n", + "\n", + "\n", + "\n", + "6->13\n", + "\n", + "\n", + "yes, missing\n", + "\n", + "\n", + "\n", + "14\n", + "\n", + "f4<-1.73232496\n", + "\n", + "\n", + "\n", + "6->14\n", + "\n", + "\n", + "no\n", + "\n", + "\n", + "\n", + "19\n", + "\n", + "f7<-0.765973091\n", + "\n", + "\n", + "\n", + "11->19\n", + "\n", + "\n", + "yes, missing\n", + "\n", + "\n", + "\n", + "20\n", + "\n", + "f7<0.133017987\n", + "\n", + "\n", + "\n", + "11->20\n", + "\n", + "\n", + "no\n", + "\n", + "\n", + "\n", + "21\n", + "\n", + "f8<-0.00532855326\n", + "\n", + "\n", + "\n", + "12->21\n", + "\n", + "\n", + "yes, missing\n", + "\n", + "\n", + "\n", + "22\n", + "\n", + "f3<1.33772755\n", + "\n", + "\n", + "\n", + "12->22\n", + "\n", + "\n", + "no\n", + "\n", + "\n", + "\n", + "31\n", + "\n", + "f3<-0.432390809\n", + "\n", + "\n", + "\n", + "19->31\n", + "\n", + "\n", + "yes, missing\n", + "\n", + "\n", + "\n", + "32\n", + "\n", + "f3<-1.20459461\n", + "\n", + "\n", + "\n", + "19->32\n", + "\n", + "\n", + "no\n", + "\n", + "\n", + "\n", + "33\n", + "\n", + "f8<0.320554674\n", + "\n", + "\n", + "\n", + "20->33\n", + "\n", + "\n", + "yes, missing\n", + "\n", + "\n", + "\n", + "34\n", + "\n", + "f8<-0.211985052\n", + "\n", + "\n", + "\n", + "20->34\n", + "\n", + "\n", + "no\n", + "\n", + "\n", + "\n", + "49\n", + "\n", + "leaf=0.204000011\n", + "\n", + "\n", + "\n", + "31->49\n", + "\n", + "\n", + "yes, missing\n", + "\n", + "\n", + "\n", + "50\n", + "\n", + "leaf=-0.485454559\n", + "\n", + "\n", + "\n", + "31->50\n", + "\n", + "\n", + "no\n", + "\n", + "\n", + "\n", + "51\n", + "\n", + "leaf=-0.5104478\n", + "\n", + "\n", + "\n", + "32->51\n", + "\n", + "\n", + "yes, missing\n", + "\n", + "\n", + "\n", + "52\n", + "\n", + "leaf=0.441509455\n", + "\n", + "\n", + "\n", + "32->52\n", + "\n", + "\n", + "no\n", + "\n", + "\n", + "\n", + "53\n", + "\n", + "leaf=-0.290322572\n", + "\n", + "\n", + "\n", + "33->53\n", + "\n", + "\n", + "yes, missing\n", + "\n", + "\n", + "\n", + "54\n", + "\n", + "leaf=0.368339777\n", + "\n", + "\n", + "\n", + "33->54\n", + "\n", + "\n", + "no\n", + "\n", + "\n", + "\n", + "55\n", + "\n", + "leaf=0.504000008\n", + "\n", + "\n", + "\n", + "34->55\n", + "\n", + "\n", + "yes, missing\n", + "\n", + "\n", + "\n", + "56\n", + "\n", + "leaf=-0.525648415\n", + "\n", + "\n", + "\n", + "34->56\n", + "\n", + "\n", + "no\n", + "\n", + "\n", + "\n", + "35\n", + "\n", + "f8<-0.204920739\n", + "\n", + "\n", + "\n", + "21->35\n", + "\n", + "\n", + "yes, missing\n", + "\n", + "\n", + "\n", + "36\n", + "\n", + "leaf=0.428571463\n", + "\n", + "\n", + "\n", + "21->36\n", + "\n", + "\n", + "no\n", + "\n", + "\n", + "\n", + "37\n", + "\n", + "f0<-0.975171864\n", + "\n", + "\n", + "\n", + "22->37\n", + "\n", + "\n", + "yes, missing\n", + "\n", + "\n", + "\n", + "38\n", + "\n", + "leaf=-0\n", + "\n", + "\n", + "\n", + "22->38\n", + "\n", + "\n", + "no\n", + "\n", + "\n", + "\n", + "57\n", + "\n", + "leaf=-0.533991575\n", + "\n", + "\n", + "\n", + "35->57\n", + "\n", + "\n", + "yes, missing\n", + "\n", + "\n", + "\n", + "58\n", + "\n", + "leaf=-0.200000018\n", + "\n", + "\n", + "\n", + "35->58\n", + "\n", + "\n", + "no\n", + "\n", + "\n", + "\n", + "59\n", + "\n", + "leaf=0.163636371\n", + "\n", + "\n", + "\n", + "37->59\n", + "\n", + "\n", + "yes, missing\n", + "\n", + "\n", + "\n", + "60\n", + "\n", + "leaf=0.51818186\n", + "\n", + "\n", + "\n", + "37->60\n", + "\n", + "\n", + "no\n", + "\n", + "\n", + "\n", + "23\n", + "\n", + "f3<-0.536645889\n", + "\n", + "\n", + "\n", + "13->23\n", + "\n", + "\n", + "yes, missing\n", + "\n", + "\n", + "\n", + "24\n", + "\n", + "f1<-0.0788691565\n", + "\n", + "\n", + "\n", + "13->24\n", + "\n", + "\n", + "no\n", + "\n", + "\n", + "\n", + "25\n", + "\n", + "leaf=-0.150000006\n", + "\n", + "\n", + "\n", + "14->25\n", + "\n", + "\n", + "yes, missing\n", + "\n", + "\n", + "\n", + "26\n", + "\n", + "f6<-1.6080606\n", + "\n", + "\n", + "\n", + "14->26\n", + "\n", + "\n", + "no\n", + "\n", + "\n", + "\n", + "39\n", + "\n", + "f9<1.89841866\n", + "\n", + "\n", + "\n", + "23->39\n", + "\n", + "\n", + "yes, missing\n", + "\n", + "\n", + "\n", + "40\n", + "\n", + "f4<-2.43660188\n", + "\n", + "\n", + "\n", + "23->40\n", + "\n", + "\n", + "no\n", + "\n", + "\n", + "\n", + "41\n", + "\n", + "leaf=0.150000006\n", + "\n", + "\n", + "\n", + "24->41\n", + "\n", + "\n", + "yes, missing\n", + "\n", + "\n", + "\n", + "42\n", + "\n", + "leaf=-0.375\n", + "\n", + "\n", + "\n", + "24->42\n", + "\n", + "\n", + "no\n", + "\n", + "\n", + "\n", + "61\n", + "\n", + "leaf=-0\n", + "\n", + "\n", + "\n", + "39->61\n", + "\n", + "\n", + "yes, missing\n", + "\n", + "\n", + "\n", + "62\n", + "\n", + "leaf=0.333333373\n", + "\n", + "\n", + "\n", + "39->62\n", + "\n", + "\n", + "no\n", + "\n", + "\n", + "\n", + "63\n", + "\n", + "leaf=0.150000006\n", + "\n", + "\n", + "\n", + "40->63\n", + "\n", + "\n", + "yes, missing\n", + "\n", + "\n", + "\n", + "64\n", + "\n", + "leaf=0.551020443\n", + "\n", + "\n", + "\n", + "40->64\n", + "\n", + "\n", + "no\n", + "\n", + "\n", + "\n", + "43\n", + "\n", + "leaf=-0.150000006\n", + "\n", + "\n", + "\n", + "26->43\n", + "\n", + "\n", + "yes, missing\n", + "\n", + "\n", + "\n", + "44\n", + "\n", + "f7<-0.259483218\n", + "\n", + "\n", + "\n", + "26->44\n", + "\n", + "\n", + "no\n", + "\n", + "\n", + "\n", + "65\n", + "\n", + "leaf=-0.558620751\n", + "\n", + "\n", + "\n", + "44->65\n", + "\n", + "\n", + "yes, missing\n", + "\n", + "\n", + "\n", + "66\n", + "\n", + "leaf=-0.300000012\n", + "\n", + "\n", + "\n", + "44->66\n", + "\n", + "\n", + "no\n", + "\n", + "\n", + "\n" + ], + "text/plain": [ + "" + ] + }, + "execution_count": 1, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "###训练1个xgboost二分类模型\n", + "import xgboost as xgb\n", + "from sklearn.datasets import make_classification\n", + "from sklearn.model_selection import train_test_split\n", + "\n", + "X, y = make_classification(n_samples=10000,\n", + " n_features=10,\n", + " n_informative=3,\n", + " n_redundant=2,\n", + " n_repeated=0,\n", + " n_classes=2,\n", + " weights=[0.7, 0.3],\n", + " flip_y=0.1,\n", + " random_state=1024)\n", + "\n", + "X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=1024)\n", + "\n", + "###训练模型\n", + "model = xgb.XGBClassifier(n_estimators=3)\n", + "model.fit(X_train, y_train)\n", + "xgb.to_graphviz(model)" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " key python_pred_res\n", + "0 0 0.788244247\n", + "1 1 0.220293656\n", + "2 2 0.220293656\n", + "3 3 0.790651679\n", + "4 4 0.220293656\n", + "... ... ...\n", + "2495 2495 0.220293656\n", + "2496 2496 0.220293656\n", + "2497 2497 0.217607751\n", + "2498 2498 0.220293656\n", + "2499 2499 0.762217879\n", + "\n", + "[2500 rows x 2 columns]\n" + ] + } + ], + "source": [ + "import pandas as pd\n", + "pd.set_option('display.float_format',lambda x : '%.9f' % x)\n", + "###使用模型对测试数据集进行预测\n", + "test_pred = model.predict_proba(X_test)[:, 1]\n", + "test_pred = pd.DataFrame(test_pred,columns=['python_pred_res'])\n", + "test_pred.reset_index(inplace=True)\n", + "test_pred.rename(columns={'index':'key'}, inplace=True)\n", + "print(test_pred)" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + " select key,1 / (1 + exp(-((tree_1_score + tree_2_score + tree_3_score)+(-0.0)))) as score\n", + " from (\n", + " select key,\n", + " --tree1\n", + "\t\tcase when (f9<-1.64164519 or f9 is null) then\n", + "\t\t\tcase when (f3<-4.19117069 or f3 is null) then\n", + "\t\t\t\tcase when (f2<-1.31743848 or f2 is null) then\n", + "\t\t\t\t\t-0.150000006\n", + "\t\t\t\telse\n", + "\t\t\t\t\t-0.544186056\n", + "\t\t\t\tend\n", + "\t\t\telse\n", + "\t\t\t\tcase when (f3<1.23432565 or f3 is null) then\n", + "\t\t\t\t\tcase when (f7<-2.55682254 or f7 is null) then\n", + "\t\t\t\t\t\t-0.200000018\n", + "\t\t\t\t\telse\n", + "\t\t\t\t\t\tcase when (f5<0.154983491 or f5 is null) then\n", + "\t\t\t\t\t\t\t0.544721723\n", + "\t\t\t\t\t\telse\n", + "\t\t\t\t\t\t\tcase when (f3<0.697217584 or f3 is null) then\n", + "\t\t\t\t\t\t\t\t-0.150000006\n", + "\t\t\t\t\t\t\telse\n", + "\t\t\t\t\t\t\t\t0.333333373\n", + "\t\t\t\t\t\t\tend\n", + "\t\t\t\t\t\tend\n", + "\t\t\t\t\tend\n", + "\t\t\t\telse\n", + "\t\t\t\t\tcase when (f5<-1.0218116 or f5 is null) then\n", + "\t\t\t\t\t\tcase when (f0<-0.60882163 or f0 is null) then\n", + "\t\t\t\t\t\t\tcase when (f2<0.26019755 or f2 is null) then\n", + "\t\t\t\t\t\t\t\t0.0666666701\n", + "\t\t\t\t\t\t\telse\n", + "\t\t\t\t\t\t\t\t-0.300000012\n", + "\t\t\t\t\t\t\tend\n", + "\t\t\t\t\t\telse\n", + "\t\t\t\t\t\t\t-0.520000041\n", + "\t\t\t\t\t\tend\n", + "\t\t\t\t\telse\n", + "\t\t\t\t\t\t0.333333373\n", + "\t\t\t\t\tend\n", + "\t\t\t\tend\n", + "\t\t\tend\n", + "\t\telse\n", + "\t\t\tcase when (f9<1.60392439 or f9 is null) then\n", + "\t\t\t\tcase when (f3<0.572542191 or f3 is null) then\n", + "\t\t\t\t\tcase when (f5<0.653370142 or f5 is null) then\n", + "\t\t\t\t\t\tcase when (f7<-0.765973091 or f7 is null) then\n", + "\t\t\t\t\t\t\tcase when (f3<-0.432390809 or f3 is null) then\n", + "\t\t\t\t\t\t\t\t0.204000011\n", + "\t\t\t\t\t\t\telse\n", + "\t\t\t\t\t\t\t\t-0.485454559\n", + "\t\t\t\t\t\t\tend\n", + "\t\t\t\t\t\telse\n", + "\t\t\t\t\t\t\tcase when (f3<-1.20459461 or f3 is null) then\n", + "\t\t\t\t\t\t\t\t-0.5104478\n", + "\t\t\t\t\t\t\telse\n", + "\t\t\t\t\t\t\t\t0.441509455\n", + "\t\t\t\t\t\t\tend\n", + "\t\t\t\t\t\tend\n", + "\t\t\t\t\telse\n", + "\t\t\t\t\t\tcase when (f7<0.133017987 or f7 is null) then\n", + "\t\t\t\t\t\t\tcase when (f8<0.320554674 or f8 is null) then\n", + "\t\t\t\t\t\t\t\t-0.290322572\n", + "\t\t\t\t\t\t\telse\n", + "\t\t\t\t\t\t\t\t0.368339777\n", + "\t\t\t\t\t\t\tend\n", + "\t\t\t\t\t\telse\n", + "\t\t\t\t\t\t\tcase when (f8<-0.211985052 or f8 is null) then\n", + "\t\t\t\t\t\t\t\t0.504000008\n", + "\t\t\t\t\t\t\telse\n", + "\t\t\t\t\t\t\t\t-0.525648415\n", + "\t\t\t\t\t\t\tend\n", + "\t\t\t\t\t\tend\n", + "\t\t\t\t\tend\n", + "\t\t\t\telse\n", + "\t\t\t\t\tcase when (f7<2.22314501 or f7 is null) then\n", + "\t\t\t\t\t\tcase when (f8<-0.00532855326 or f8 is null) then\n", + "\t\t\t\t\t\t\tcase when (f8<-0.204920739 or f8 is null) then\n", + "\t\t\t\t\t\t\t\t-0.533991575\n", + "\t\t\t\t\t\t\telse\n", + "\t\t\t\t\t\t\t\t-0.200000018\n", + "\t\t\t\t\t\t\tend\n", + "\t\t\t\t\t\telse\n", + "\t\t\t\t\t\t\t0.428571463\n", + "\t\t\t\t\t\tend\n", + "\t\t\t\t\telse\n", + "\t\t\t\t\t\tcase when (f3<1.33772755 or f3 is null) then\n", + "\t\t\t\t\t\t\tcase when (f0<-0.975171864 or f0 is null) then\n", + "\t\t\t\t\t\t\t\t0.163636371\n", + "\t\t\t\t\t\t\telse\n", + "\t\t\t\t\t\t\t\t0.51818186\n", + "\t\t\t\t\t\t\tend\n", + "\t\t\t\t\t\telse\n", + "\t\t\t\t\t\t\t-0\n", + "\t\t\t\t\t\tend\n", + "\t\t\t\t\tend\n", + "\t\t\t\tend\n", + "\t\t\telse\n", + "\t\t\t\tcase when (f3<1.77943277 or f3 is null) then\n", + "\t\t\t\t\tcase when (f7<-0.469875157 or f7 is null) then\n", + "\t\t\t\t\t\tcase when (f3<-0.536645889 or f3 is null) then\n", + "\t\t\t\t\t\t\tcase when (f9<1.89841866 or f9 is null) then\n", + "\t\t\t\t\t\t\t\t-0\n", + "\t\t\t\t\t\t\telse\n", + "\t\t\t\t\t\t\t\t0.333333373\n", + "\t\t\t\t\t\t\tend\n", + "\t\t\t\t\t\telse\n", + "\t\t\t\t\t\t\tcase when (f4<-2.43660188 or f4 is null) then\n", + "\t\t\t\t\t\t\t\t0.150000006\n", + "\t\t\t\t\t\t\telse\n", + "\t\t\t\t\t\t\t\t0.551020443\n", + "\t\t\t\t\t\t\tend\n", + "\t\t\t\t\t\tend\n", + "\t\t\t\t\telse\n", + "\t\t\t\t\t\tcase when (f1<-0.0788691565 or f1 is null) then\n", + "\t\t\t\t\t\t\t0.150000006\n", + "\t\t\t\t\t\telse\n", + "\t\t\t\t\t\t\t-0.375\n", + "\t\t\t\t\t\tend\n", + "\t\t\t\t\tend\n", + "\t\t\t\telse\n", + "\t\t\t\t\tcase when (f4<-1.73232496 or f4 is null) then\n", + "\t\t\t\t\t\t-0.150000006\n", + "\t\t\t\t\telse\n", + "\t\t\t\t\t\tcase when (f6<-1.6080606 or f6 is null) then\n", + "\t\t\t\t\t\t\t-0.150000006\n", + "\t\t\t\t\t\telse\n", + "\t\t\t\t\t\t\tcase when (f7<-0.259483218 or f7 is null) then\n", + "\t\t\t\t\t\t\t\t-0.558620751\n", + "\t\t\t\t\t\t\telse\n", + "\t\t\t\t\t\t\t\t-0.300000012\n", + "\t\t\t\t\t\t\tend\n", + "\t\t\t\t\t\tend\n", + "\t\t\t\t\tend\n", + "\t\t\t\tend\n", + "\t\t\tend\n", + "\t\tend\n", + "\t\tas tree_1_score,\n", + "--tree2\n", + "\t\tcase when (f9<-1.64164519 or f9 is null) then\n", + "\t\t\tcase when (f3<-4.19117069 or f3 is null) then\n", + "\t\t\t\tcase when (f0<0.942570388 or f0 is null) then\n", + "\t\t\t\t\t-0.432453066\n", + "\t\t\t\telse\n", + "\t\t\t\t\t-0.128291652\n", + "\t\t\t\tend\n", + "\t\t\telse\n", + "\t\t\t\tcase when (f3<1.23432565 or f3 is null) then\n", + "\t\t\t\t\tcase when (f7<-2.55682254 or f7 is null) then\n", + "\t\t\t\t\t\t-0.167702854\n", + "\t\t\t\t\telse\n", + "\t\t\t\t\t\tcase when (f5<0.154983491 or f5 is null) then\n", + "\t\t\t\t\t\t\tcase when (f1<2.19985676 or f1 is null) then\n", + "\t\t\t\t\t\t\t\t0.41752997\n", + "\t\t\t\t\t\t\telse\n", + "\t\t\t\t\t\t\t\t0.115944751\n", + "\t\t\t\t\t\t\tend\n", + "\t\t\t\t\t\telse\n", + "\t\t\t\t\t\t\t0.115584135\n", + "\t\t\t\t\t\tend\n", + "\t\t\t\t\tend\n", + "\t\t\t\telse\n", + "\t\t\t\t\tcase when (f5<-1.0218116 or f5 is null) then\n", + "\t\t\t\t\t\tcase when (f0<-0.60882163 or f0 is null) then\n", + "\t\t\t\t\t\t\t-0.119530827\n", + "\t\t\t\t\t\telse\n", + "\t\t\t\t\t\t\t-0.410788596\n", + "\t\t\t\t\t\tend\n", + "\t\t\t\t\telse\n", + "\t\t\t\t\t\t0.28256765\n", + "\t\t\t\t\tend\n", + "\t\t\t\tend\n", + "\t\t\tend\n", + "\t\telse\n", + "\t\t\tcase when (f9<1.60392439 or f9 is null) then\n", + "\t\t\t\tcase when (f3<0.460727394 or f3 is null) then\n", + "\t\t\t\t\tcase when (f5<0.653370142 or f5 is null) then\n", + "\t\t\t\t\t\tcase when (f7<-0.933565617 or f7 is null) then\n", + "\t\t\t\t\t\t\tcase when (f3<-0.572475374 or f3 is null) then\n", + "\t\t\t\t\t\t\t\t0.182491601\n", + "\t\t\t\t\t\t\telse\n", + "\t\t\t\t\t\t\t\t-0.377898693\n", + "\t\t\t\t\t\t\tend\n", + "\t\t\t\t\t\telse\n", + "\t\t\t\t\t\t\tcase when (f3<-1.20459461 or f3 is null) then\n", + "\t\t\t\t\t\t\t\t-0.392539263\n", + "\t\t\t\t\t\t\telse\n", + "\t\t\t\t\t\t\t\t0.352721155\n", + "\t\t\t\t\t\t\tend\n", + "\t\t\t\t\t\tend\n", + "\t\t\t\t\telse\n", + "\t\t\t\t\t\tcase when (f7<0.207098693 or f7 is null) then\n", + "\t\t\t\t\t\t\tcase when (f8<0.498489976 or f8 is null) then\n", + "\t\t\t\t\t\t\t\t-0.193351224\n", + "\t\t\t\t\t\t\telse\n", + "\t\t\t\t\t\t\t\t0.29298231\n", + "\t\t\t\t\t\t\tend\n", + "\t\t\t\t\t\telse\n", + "\t\t\t\t\t\t\tcase when (f8<-0.117464997 or f8 is null) then\n", + "\t\t\t\t\t\t\t\t0.400667101\n", + "\t\t\t\t\t\t\telse\n", + "\t\t\t\t\t\t\t\t-0.402199954\n", + "\t\t\t\t\t\t\tend\n", + "\t\t\t\t\t\tend\n", + "\t\t\t\t\tend\n", + "\t\t\t\telse\n", + "\t\t\t\t\tcase when (f7<1.98268723 or f7 is null) then\n", + "\t\t\t\t\t\tcase when (f8<-0.00532855326 or f8 is null) then\n", + "\t\t\t\t\t\t\tcase when (f7<1.36281848 or f7 is null) then\n", + "\t\t\t\t\t\t\t\t-0.408002198\n", + "\t\t\t\t\t\t\telse\n", + "\t\t\t\t\t\t\t\t-0.236123681\n", + "\t\t\t\t\t\t\tend\n", + "\t\t\t\t\t\telse\n", + "\t\t\t\t\t\t\tcase when (f5<1.14038813 or f5 is null) then\n", + "\t\t\t\t\t\t\t\t0.404326111\n", + "\t\t\t\t\t\t\telse\n", + "\t\t\t\t\t\t\t\t-0.110877581\n", + "\t\t\t\t\t\t\tend\n", + "\t\t\t\t\t\tend\n", + "\t\t\t\t\telse\n", + "\t\t\t\t\t\tcase when (f3<1.56952488 or f3 is null) then\n", + "\t\t\t\t\t\t\tcase when (f5<2.14646816 or f5 is null) then\n", + "\t\t\t\t\t\t\t\t0.409404457\n", + "\t\t\t\t\t\t\telse\n", + "\t\t\t\t\t\t\t\t0.0696995854\n", + "\t\t\t\t\t\t\tend\n", + "\t\t\t\t\t\telse\n", + "\t\t\t\t\t\t\t-0.32059738\n", + "\t\t\t\t\t\tend\n", + "\t\t\t\t\tend\n", + "\t\t\t\tend\n", + "\t\t\telse\n", + "\t\t\t\tcase when (f3<1.77943277 or f3 is null) then\n", + "\t\t\t\t\tcase when (f7<-0.469875157 or f7 is null) then\n", + "\t\t\t\t\t\tcase when (f3<-0.536645889 or f3 is null) then\n", + "\t\t\t\t\t\t\tcase when (f9<1.89841866 or f9 is null) then\n", + "\t\t\t\t\t\t\t\t-0\n", + "\t\t\t\t\t\t\telse\n", + "\t\t\t\t\t\t\t\t0.28256765\n", + "\t\t\t\t\t\t\tend\n", + "\t\t\t\t\t\telse\n", + "\t\t\t\t\t\t\t0.419863999\n", + "\t\t\t\t\t\tend\n", + "\t\t\t\t\telse\n", + "\t\t\t\t\t\tcase when (f3<0.444227457 or f3 is null) then\n", + "\t\t\t\t\t\t\t-0.34664312\n", + "\t\t\t\t\t\telse\n", + "\t\t\t\t\t\t\t0.0693304539\n", + "\t\t\t\t\t\tend\n", + "\t\t\t\t\tend\n", + "\t\t\t\telse\n", + "\t\t\t\t\tcase when (f4<-1.10089087 or f4 is null) then\n", + "\t\t\t\t\t\tcase when (f3<2.3550868 or f3 is null) then\n", + "\t\t\t\t\t\t\t0.0147894565\n", + "\t\t\t\t\t\telse\n", + "\t\t\t\t\t\t\t-0.331404865\n", + "\t\t\t\t\t\tend\n", + "\t\t\t\t\telse\n", + "\t\t\t\t\t\t-0.421277165\n", + "\t\t\t\t\tend\n", + "\t\t\t\tend\n", + "\t\t\tend\n", + "\t\tend\n", + "\t\tas tree_2_score,\n", + "--tree3\n", + "\t\tcase when (f9<-1.64164519 or f9 is null) then\n", + "\t\t\tcase when (f3<-4.19117069 or f3 is null) then\n", + "\t\t\t\tcase when (f4<-1.30126143 or f4 is null) then\n", + "\t\t\t\t\t-0.0772174299\n", + "\t\t\t\telse\n", + "\t\t\t\t\t-0.374165356\n", + "\t\t\t\tend\n", + "\t\t\telse\n", + "\t\t\t\tcase when (f3<1.23432565 or f3 is null) then\n", + "\t\t\t\t\tcase when (f7<-2.55682254 or f7 is null) then\n", + "\t\t\t\t\t\t-0.142005175\n", + "\t\t\t\t\telse\n", + "\t\t\t\t\t\tcase when (f5<0.154983491 or f5 is null) then\n", + "\t\t\t\t\t\t\tcase when (f7<3.59379435 or f7 is null) then\n", + "\t\t\t\t\t\t\t\t0.352122813\n", + "\t\t\t\t\t\t\telse\n", + "\t\t\t\t\t\t\t\t0.132789165\n", + "\t\t\t\t\t\t\tend\n", + "\t\t\t\t\t\telse\n", + "\t\t\t\t\t\t\t0.0924336985\n", + "\t\t\t\t\t\tend\n", + "\t\t\t\t\tend\n", + "\t\t\t\telse\n", + "\t\t\t\t\tcase when (f5<-1.0218116 or f5 is null) then\n", + "\t\t\t\t\t\tcase when (f0<-0.60882163 or f0 is null) then\n", + "\t\t\t\t\t\t\t-0.0954768136\n", + "\t\t\t\t\t\telse\n", + "\t\t\t\t\t\t\t-0.351594836\n", + "\t\t\t\t\t\tend\n", + "\t\t\t\t\telse\n", + "\t\t\t\t\t\t0.245992288\n", + "\t\t\t\t\tend\n", + "\t\t\t\tend\n", + "\t\t\tend\n", + "\t\telse\n", + "\t\t\tcase when (f9<1.60392439 or f9 is null) then\n", + "\t\t\t\tcase when (f3<0.347133756 or f3 is null) then\n", + "\t\t\t\t\tcase when (f5<0.661561131 or f5 is null) then\n", + "\t\t\t\t\t\tcase when (f7<-0.933565617 or f7 is null) then\n", + "\t\t\t\t\t\t\tcase when (f3<-0.472413659 or f3 is null) then\n", + "\t\t\t\t\t\t\t\t0.116336405\n", + "\t\t\t\t\t\t\telse\n", + "\t\t\t\t\t\t\t\t-0.313245147\n", + "\t\t\t\t\t\t\tend\n", + "\t\t\t\t\t\telse\n", + "\t\t\t\t\t\t\tcase when (f3<-1.5402329 or f3 is null) then\n", + "\t\t\t\t\t\t\t\t-0.352897167\n", + "\t\t\t\t\t\t\telse\n", + "\t\t\t\t\t\t\t\t0.311400592\n", + "\t\t\t\t\t\t\tend\n", + "\t\t\t\t\t\tend\n", + "\t\t\t\t\telse\n", + "\t\t\t\t\t\tcase when (f7<0.275665522 or f7 is null) then\n", + "\t\t\t\t\t\t\tcase when (f8<0.403402805 or f8 is null) then\n", + "\t\t\t\t\t\t\t\t-0.292606086\n", + "\t\t\t\t\t\t\telse\n", + "\t\t\t\t\t\t\t\t0.220064178\n", + "\t\t\t\t\t\t\tend\n", + "\t\t\t\t\t\telse\n", + "\t\t\t\t\t\t\tcase when (f8<-0.0442957953 or f8 is null) then\n", + "\t\t\t\t\t\t\t\t0.350784421\n", + "\t\t\t\t\t\t\telse\n", + "\t\t\t\t\t\t\t\t-0.336107522\n", + "\t\t\t\t\t\t\tend\n", + "\t\t\t\t\t\tend\n", + "\t\t\t\t\tend\n", + "\t\t\t\telse\n", + "\t\t\t\t\tcase when (f7<1.77503061 or f7 is null) then\n", + "\t\t\t\t\t\tcase when (f8<0.196157426 or f8 is null) then\n", + "\t\t\t\t\t\t\tcase when (f7<1.36281848 or f7 is null) then\n", + "\t\t\t\t\t\t\t\t-0.3376683\n", + "\t\t\t\t\t\t\telse\n", + "\t\t\t\t\t\t\t\t-0.0711223111\n", + "\t\t\t\t\t\t\tend\n", + "\t\t\t\t\t\telse\n", + "\t\t\t\t\t\t\tcase when (f7<-0.661211252 or f7 is null) then\n", + "\t\t\t\t\t\t\t\t0.434363276\n", + "\t\t\t\t\t\t\telse\n", + "\t\t\t\t\t\t\t\t-0.219307661\n", + "\t\t\t\t\t\t\tend\n", + "\t\t\t\t\t\tend\n", + "\t\t\t\t\telse\n", + "\t\t\t\t\t\tcase when (f3<1.37940335 or f3 is null) then\n", + "\t\t\t\t\t\t\tcase when (f6<1.34894884 or f6 is null) then\n", + "\t\t\t\t\t\t\t\t0.367155522\n", + "\t\t\t\t\t\t\telse\n", + "\t\t\t\t\t\t\t\t0.124757253\n", + "\t\t\t\t\t\t\tend\n", + "\t\t\t\t\t\telse\n", + "\t\t\t\t\t\t\t-0.293739736\n", + "\t\t\t\t\t\tend\n", + "\t\t\t\t\tend\n", + "\t\t\t\tend\n", + "\t\t\telse\n", + "\t\t\t\tcase when (f3<1.77943277 or f3 is null) then\n", + "\t\t\t\t\tcase when (f7<-0.469875157 or f7 is null) then\n", + "\t\t\t\t\t\tcase when (f3<-0.536645889 or f3 is null) then\n", + "\t\t\t\t\t\t\tcase when (f9<1.89841866 or f9 is null) then\n", + "\t\t\t\t\t\t\t\t-0\n", + "\t\t\t\t\t\t\telse\n", + "\t\t\t\t\t\t\t\t0.245992288\n", + "\t\t\t\t\t\t\tend\n", + "\t\t\t\t\t\telse\n", + "\t\t\t\t\t\t\tcase when (f0<1.60565615 or f0 is null) then\n", + "\t\t\t\t\t\t\t\t0.357973605\n", + "\t\t\t\t\t\t\telse\n", + "\t\t\t\t\t\t\t\t0.193993196\n", + "\t\t\t\t\t\t\tend\n", + "\t\t\t\t\t\tend\n", + "\t\t\t\t\telse\n", + "\t\t\t\t\t\tcase when (f9<1.89456153 or f9 is null) then\n", + "\t\t\t\t\t\t\t-0.276471078\n", + "\t\t\t\t\t\telse\n", + "\t\t\t\t\t\t\t0.111896731\n", + "\t\t\t\t\t\tend\n", + "\t\t\t\t\tend\n", + "\t\t\t\telse\n", + "\t\t\t\t\tcase when (f1<1.35706067 or f1 is null) then\n", + "\t\t\t\t\t\tcase when (f4<-1.10089087 or f4 is null) then\n", + "\t\t\t\t\t\t\tcase when (f3<2.3550868 or f3 is null) then\n", + "\t\t\t\t\t\t\t\t0.0119848112\n", + "\t\t\t\t\t\t\telse\n", + "\t\t\t\t\t\t\t\t-0.284813672\n", + "\t\t\t\t\t\t\tend\n", + "\t\t\t\t\t\telse\n", + "\t\t\t\t\t\t\t-0.376859784\n", + "\t\t\t\t\t\tend\n", + "\t\t\t\t\telse\n", + "\t\t\t\t\t\tcase when (f2<-0.25748384 or f2 is null) then\n", + "\t\t\t\t\t\t\t0.0723158419\n", + "\t\t\t\t\t\telse\n", + "\t\t\t\t\t\t\t-0.253415495\n", + "\t\t\t\t\t\tend\n", + "\t\t\t\t\tend\n", + "\t\t\t\tend\n", + "\t\t\tend\n", + "\t\tend\n", + "\t\tas tree_3_score\n", + " from data_table)\n", + " \n" + ] + } + ], + "source": [ + "from xgboost2sql import XGBoost2Sql\n", + "###使用xgboost2sql包将模型转换成的sql语句\n", + "xgb2sql = XGBoost2Sql()\n", + "sql_str = xgb2sql.transform(model)\n", + "print(sql_str)" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
keyscore
00.0000000000.788244248
11.0000000000.220293659
\n", + "
" + ], + "text/plain": [ + " key score\n", + "0 0.000000000 0.788244248\n", + "1 1.000000000 0.220293659" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "###使用模型转换成的sql语句对测试数据集进行预测\n", + "from pyspark.sql import SparkSession\n", + "spark = SparkSession.builder.getOrCreate()\n", + "\n", + "X_test_df = pd.DataFrame(X_test)\n", + "X_test_df.columns = X_test_df.columns.map(lambda x: \"f\"+str(x))\n", + "X_test_df.reset_index(inplace=True)\n", + "X_test_df.rename(columns={'index':'key'}, inplace=True)\n", + "values = X_test_df.values.tolist()\n", + "columns = X_test_df.columns.tolist()\n", + "spark_df = spark.createDataFrame(values, columns)\n", + "spark_df.createOrReplaceTempView('data_table')\n", + "sql_pred_pysdf = spark.sql(sql_str)\n", + "sql_pred_df = sql_pred_pysdf.toPandas()\n", + "sql_pred_df.head(2)" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "count 2500.000000000\n", + "mean -0.000000003\n", + "std 0.000000011\n", + "min -0.000000068\n", + "25% -0.000000003\n", + "50% -0.000000001\n", + "75% -0.000000000\n", + "max 0.000000056\n", + "Name: diff, dtype: float64" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "###对比python模型预测出来的结果和sql语句预测出来的结果是否一致\n", + "test_pred_sql_pred_df = test_pred.merge(sql_pred_df, on='key')\n", + "test_pred_sql_pred_df['diff'] = test_pred_sql_pred_df['python_pred_res'] - test_pred_sql_pred_df['score']\n", + "test_pred_sql_pred_df['diff'].describe()" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + " select key,1 / (1 + exp(-((tree_1_score + tree_2_score + tree_3_score)+(-0.0)))) as score\n", + " from (\n", + " select key,\n", + " --tree1\n", + "\t\tcase when (f9<-1.64164519 or f9 is null) then\n", + "\t\t\tcase when (f3<-4.19117069 or f3 is null) then\n", + "\t\t\t\tcase when (f2<-1.31743848 or f2 is null) then\n", + "\t\t\t\t\t-0.150000006\n", + "\t\t\t\telse\n", + "\t\t\t\t\t-0.544186056\n", + "\t\t\t\tend\n", + "\t\t\telse\n", + "\t\t\t\tcase when (f3<1.23432565 or f3 is null) then\n", + "\t\t\t\t\tcase when (f7<-2.55682254 or f7 is null) then\n", + "\t\t\t\t\t\t-0.200000018\n", + "\t\t\t\t\telse\n", + "\t\t\t\t\t\tcase when (f5<0.154983491 or f5 is null) then\n", + "\t\t\t\t\t\t\t0.544721723\n", + "\t\t\t\t\t\telse\n", + "\t\t\t\t\t\t\tcase when (f3<0.697217584 or f3 is null) then\n", + "\t\t\t\t\t\t\t\t-0.150000006\n", + "\t\t\t\t\t\t\telse\n", + "\t\t\t\t\t\t\t\t0.333333373\n", + "\t\t\t\t\t\t\tend\n", + "\t\t\t\t\t\tend\n", + "\t\t\t\t\tend\n", + "\t\t\t\telse\n", + "\t\t\t\t\tcase when (f5<-1.0218116 or f5 is null) then\n", + "\t\t\t\t\t\tcase when (f0<-0.60882163 or f0 is null) then\n", + "\t\t\t\t\t\t\tcase when (f2<0.26019755 or f2 is null) then\n", + "\t\t\t\t\t\t\t\t0.0666666701\n", + "\t\t\t\t\t\t\telse\n", + "\t\t\t\t\t\t\t\t-0.300000012\n", + "\t\t\t\t\t\t\tend\n", + "\t\t\t\t\t\telse\n", + "\t\t\t\t\t\t\t-0.520000041\n", + "\t\t\t\t\t\tend\n", + "\t\t\t\t\telse\n", + "\t\t\t\t\t\t0.333333373\n", + "\t\t\t\t\tend\n", + "\t\t\t\tend\n", + "\t\t\tend\n", + "\t\telse\n", + "\t\t\tcase when (f9<1.60392439 or f9 is null) then\n", + "\t\t\t\tcase when (f3<0.572542191 or f3 is null) then\n", + "\t\t\t\t\tcase when (f5<0.653370142 or f5 is null) then\n", + "\t\t\t\t\t\tcase when (f7<-0.765973091 or f7 is null) then\n", + "\t\t\t\t\t\t\tcase when (f3<-0.432390809 or f3 is null) then\n", + "\t\t\t\t\t\t\t\t0.204000011\n", + "\t\t\t\t\t\t\telse\n", + "\t\t\t\t\t\t\t\t-0.485454559\n", + "\t\t\t\t\t\t\tend\n", + "\t\t\t\t\t\telse\n", + "\t\t\t\t\t\t\tcase when (f3<-1.20459461 or f3 is null) then\n", + "\t\t\t\t\t\t\t\t-0.5104478\n", + "\t\t\t\t\t\t\telse\n", + "\t\t\t\t\t\t\t\t0.441509455\n", + "\t\t\t\t\t\t\tend\n", + "\t\t\t\t\t\tend\n", + "\t\t\t\t\telse\n", + "\t\t\t\t\t\tcase when (f7<0.133017987 or f7 is null) then\n", + "\t\t\t\t\t\t\tcase when (f8<0.320554674 or f8 is null) then\n", + "\t\t\t\t\t\t\t\t-0.290322572\n", + "\t\t\t\t\t\t\telse\n", + "\t\t\t\t\t\t\t\t0.368339777\n", + "\t\t\t\t\t\t\tend\n", + "\t\t\t\t\t\telse\n", + "\t\t\t\t\t\t\tcase when (f8<-0.211985052 or f8 is null) then\n", + "\t\t\t\t\t\t\t\t0.504000008\n", + "\t\t\t\t\t\t\telse\n", + "\t\t\t\t\t\t\t\t-0.525648415\n", + "\t\t\t\t\t\t\tend\n", + "\t\t\t\t\t\tend\n", + "\t\t\t\t\tend\n", + "\t\t\t\telse\n", + "\t\t\t\t\tcase when (f7<2.22314501 or f7 is null) then\n", + "\t\t\t\t\t\tcase when (f8<-0.00532855326 or f8 is null) then\n", + "\t\t\t\t\t\t\tcase when (f8<-0.204920739 or f8 is null) then\n", + "\t\t\t\t\t\t\t\t-0.533991575\n", + "\t\t\t\t\t\t\telse\n", + "\t\t\t\t\t\t\t\t-0.200000018\n", + "\t\t\t\t\t\t\tend\n", + "\t\t\t\t\t\telse\n", + "\t\t\t\t\t\t\t0.428571463\n", + "\t\t\t\t\t\tend\n", + "\t\t\t\t\telse\n", + "\t\t\t\t\t\tcase when (f3<1.33772755 or f3 is null) then\n", + "\t\t\t\t\t\t\tcase when (f0<-0.975171864 or f0 is null) then\n", + "\t\t\t\t\t\t\t\t0.163636371\n", + "\t\t\t\t\t\t\telse\n", + "\t\t\t\t\t\t\t\t0.51818186\n", + "\t\t\t\t\t\t\tend\n", + "\t\t\t\t\t\telse\n", + "\t\t\t\t\t\t\t-0\n", + "\t\t\t\t\t\tend\n", + "\t\t\t\t\tend\n", + "\t\t\t\tend\n", + "\t\t\telse\n", + "\t\t\t\tcase when (f3<1.77943277 or f3 is null) then\n", + "\t\t\t\t\tcase when (f7<-0.469875157 or f7 is null) then\n", + "\t\t\t\t\t\tcase when (f3<-0.536645889 or f3 is null) then\n", + "\t\t\t\t\t\t\tcase when (f9<1.89841866 or f9 is null) then\n", + "\t\t\t\t\t\t\t\t-0\n", + "\t\t\t\t\t\t\telse\n", + "\t\t\t\t\t\t\t\t0.333333373\n", + "\t\t\t\t\t\t\tend\n", + "\t\t\t\t\t\telse\n", + "\t\t\t\t\t\t\tcase when (f4<-2.43660188 or f4 is null) then\n", + "\t\t\t\t\t\t\t\t0.150000006\n", + "\t\t\t\t\t\t\telse\n", + "\t\t\t\t\t\t\t\t0.551020443\n", + "\t\t\t\t\t\t\tend\n", + "\t\t\t\t\t\tend\n", + "\t\t\t\t\telse\n", + "\t\t\t\t\t\tcase when (f1<-0.0788691565 or f1 is null) then\n", + "\t\t\t\t\t\t\t0.150000006\n", + "\t\t\t\t\t\telse\n", + "\t\t\t\t\t\t\t-0.375\n", + "\t\t\t\t\t\tend\n", + "\t\t\t\t\tend\n", + "\t\t\t\telse\n", + "\t\t\t\t\tcase when (f4<-1.73232496 or f4 is null) then\n", + "\t\t\t\t\t\t-0.150000006\n", + "\t\t\t\t\telse\n", + "\t\t\t\t\t\tcase when (f6<-1.6080606 or f6 is null) then\n", + "\t\t\t\t\t\t\t-0.150000006\n", + "\t\t\t\t\t\telse\n", + "\t\t\t\t\t\t\tcase when (f7<-0.259483218 or f7 is null) then\n", + "\t\t\t\t\t\t\t\t-0.558620751\n", + "\t\t\t\t\t\t\telse\n", + "\t\t\t\t\t\t\t\t-0.300000012\n", + "\t\t\t\t\t\t\tend\n", + "\t\t\t\t\t\tend\n", + "\t\t\t\t\tend\n", + "\t\t\t\tend\n", + "\t\t\tend\n", + "\t\tend\n", + "\t\tas tree_1_score,\n", + "--tree2\n", + "\t\tcase when (f9<-1.64164519 or f9 is null) then\n", + "\t\t\tcase when (f3<-4.19117069 or f3 is null) then\n", + "\t\t\t\tcase when (f0<0.942570388 or f0 is null) then\n", + "\t\t\t\t\t-0.432453066\n", + "\t\t\t\telse\n", + "\t\t\t\t\t-0.128291652\n", + "\t\t\t\tend\n", + "\t\t\telse\n", + "\t\t\t\tcase when (f3<1.23432565 or f3 is null) then\n", + "\t\t\t\t\tcase when (f7<-2.55682254 or f7 is null) then\n", + "\t\t\t\t\t\t-0.167702854\n", + "\t\t\t\t\telse\n", + "\t\t\t\t\t\tcase when (f5<0.154983491 or f5 is null) then\n", + "\t\t\t\t\t\t\tcase when (f1<2.19985676 or f1 is null) then\n", + "\t\t\t\t\t\t\t\t0.41752997\n", + "\t\t\t\t\t\t\telse\n", + "\t\t\t\t\t\t\t\t0.115944751\n", + "\t\t\t\t\t\t\tend\n", + "\t\t\t\t\t\telse\n", + "\t\t\t\t\t\t\t0.115584135\n", + "\t\t\t\t\t\tend\n", + "\t\t\t\t\tend\n", + "\t\t\t\telse\n", + "\t\t\t\t\tcase when (f5<-1.0218116 or f5 is null) then\n", + "\t\t\t\t\t\tcase when (f0<-0.60882163 or f0 is null) then\n", + "\t\t\t\t\t\t\t-0.119530827\n", + "\t\t\t\t\t\telse\n", + "\t\t\t\t\t\t\t-0.410788596\n", + "\t\t\t\t\t\tend\n", + "\t\t\t\t\telse\n", + "\t\t\t\t\t\t0.28256765\n", + "\t\t\t\t\tend\n", + "\t\t\t\tend\n", + "\t\t\tend\n", + "\t\telse\n", + "\t\t\tcase when (f9<1.60392439 or f9 is null) then\n", + "\t\t\t\tcase when (f3<0.460727394 or f3 is null) then\n", + "\t\t\t\t\tcase when (f5<0.653370142 or f5 is null) then\n", + "\t\t\t\t\t\tcase when (f7<-0.933565617 or f7 is null) then\n", + "\t\t\t\t\t\t\tcase when (f3<-0.572475374 or f3 is null) then\n", + "\t\t\t\t\t\t\t\t0.182491601\n", + "\t\t\t\t\t\t\telse\n", + "\t\t\t\t\t\t\t\t-0.377898693\n", + "\t\t\t\t\t\t\tend\n", + "\t\t\t\t\t\telse\n", + "\t\t\t\t\t\t\tcase when (f3<-1.20459461 or f3 is null) then\n", + "\t\t\t\t\t\t\t\t-0.392539263\n", + "\t\t\t\t\t\t\telse\n", + "\t\t\t\t\t\t\t\t0.352721155\n", + "\t\t\t\t\t\t\tend\n", + "\t\t\t\t\t\tend\n", + "\t\t\t\t\telse\n", + "\t\t\t\t\t\tcase when (f7<0.207098693 or f7 is null) then\n", + "\t\t\t\t\t\t\tcase when (f8<0.498489976 or f8 is null) then\n", + "\t\t\t\t\t\t\t\t-0.193351224\n", + "\t\t\t\t\t\t\telse\n", + "\t\t\t\t\t\t\t\t0.29298231\n", + "\t\t\t\t\t\t\tend\n", + "\t\t\t\t\t\telse\n", + "\t\t\t\t\t\t\tcase when (f8<-0.117464997 or f8 is null) then\n", + "\t\t\t\t\t\t\t\t0.400667101\n", + "\t\t\t\t\t\t\telse\n", + "\t\t\t\t\t\t\t\t-0.402199954\n", + "\t\t\t\t\t\t\tend\n", + "\t\t\t\t\t\tend\n", + "\t\t\t\t\tend\n", + "\t\t\t\telse\n", + "\t\t\t\t\tcase when (f7<1.98268723 or f7 is null) then\n", + "\t\t\t\t\t\tcase when (f8<-0.00532855326 or f8 is null) then\n", + "\t\t\t\t\t\t\tcase when (f7<1.36281848 or f7 is null) then\n", + "\t\t\t\t\t\t\t\t-0.408002198\n", + "\t\t\t\t\t\t\telse\n", + "\t\t\t\t\t\t\t\t-0.236123681\n", + "\t\t\t\t\t\t\tend\n", + "\t\t\t\t\t\telse\n", + "\t\t\t\t\t\t\tcase when (f5<1.14038813 or f5 is null) then\n", + "\t\t\t\t\t\t\t\t0.404326111\n", + "\t\t\t\t\t\t\telse\n", + "\t\t\t\t\t\t\t\t-0.110877581\n", + "\t\t\t\t\t\t\tend\n", + "\t\t\t\t\t\tend\n", + "\t\t\t\t\telse\n", + "\t\t\t\t\t\tcase when (f3<1.56952488 or f3 is null) then\n", + "\t\t\t\t\t\t\tcase when (f5<2.14646816 or f5 is null) then\n", + "\t\t\t\t\t\t\t\t0.409404457\n", + "\t\t\t\t\t\t\telse\n", + "\t\t\t\t\t\t\t\t0.0696995854\n", + "\t\t\t\t\t\t\tend\n", + "\t\t\t\t\t\telse\n", + "\t\t\t\t\t\t\t-0.32059738\n", + "\t\t\t\t\t\tend\n", + "\t\t\t\t\tend\n", + "\t\t\t\tend\n", + "\t\t\telse\n", + "\t\t\t\tcase when (f3<1.77943277 or f3 is null) then\n", + "\t\t\t\t\tcase when (f7<-0.469875157 or f7 is null) then\n", + "\t\t\t\t\t\tcase when (f3<-0.536645889 or f3 is null) then\n", + "\t\t\t\t\t\t\tcase when (f9<1.89841866 or f9 is null) then\n", + "\t\t\t\t\t\t\t\t-0\n", + "\t\t\t\t\t\t\telse\n", + "\t\t\t\t\t\t\t\t0.28256765\n", + "\t\t\t\t\t\t\tend\n", + "\t\t\t\t\t\telse\n", + "\t\t\t\t\t\t\t0.419863999\n", + "\t\t\t\t\t\tend\n", + "\t\t\t\t\telse\n", + "\t\t\t\t\t\tcase when (f3<0.444227457 or f3 is null) then\n", + "\t\t\t\t\t\t\t-0.34664312\n", + "\t\t\t\t\t\telse\n", + "\t\t\t\t\t\t\t0.0693304539\n", + "\t\t\t\t\t\tend\n", + "\t\t\t\t\tend\n", + "\t\t\t\telse\n", + "\t\t\t\t\tcase when (f4<-1.10089087 or f4 is null) then\n", + "\t\t\t\t\t\tcase when (f3<2.3550868 or f3 is null) then\n", + "\t\t\t\t\t\t\t0.0147894565\n", + "\t\t\t\t\t\telse\n", + "\t\t\t\t\t\t\t-0.331404865\n", + "\t\t\t\t\t\tend\n", + "\t\t\t\t\telse\n", + "\t\t\t\t\t\t-0.421277165\n", + "\t\t\t\t\tend\n", + "\t\t\t\tend\n", + "\t\t\tend\n", + "\t\tend\n", + "\t\tas tree_2_score,\n", + "--tree3\n", + "\t\tcase when (f9<-1.64164519 or f9 is null) then\n", + "\t\t\tcase when (f3<-4.19117069 or f3 is null) then\n", + "\t\t\t\tcase when (f4<-1.30126143 or f4 is null) then\n", + "\t\t\t\t\t-0.0772174299\n", + "\t\t\t\telse\n", + "\t\t\t\t\t-0.374165356\n", + "\t\t\t\tend\n", + "\t\t\telse\n", + "\t\t\t\tcase when (f3<1.23432565 or f3 is null) then\n", + "\t\t\t\t\tcase when (f7<-2.55682254 or f7 is null) then\n", + "\t\t\t\t\t\t-0.142005175\n", + "\t\t\t\t\telse\n", + "\t\t\t\t\t\tcase when (f5<0.154983491 or f5 is null) then\n", + "\t\t\t\t\t\t\tcase when (f7<3.59379435 or f7 is null) then\n", + "\t\t\t\t\t\t\t\t0.352122813\n", + "\t\t\t\t\t\t\telse\n", + "\t\t\t\t\t\t\t\t0.132789165\n", + "\t\t\t\t\t\t\tend\n", + "\t\t\t\t\t\telse\n", + "\t\t\t\t\t\t\t0.0924336985\n", + "\t\t\t\t\t\tend\n", + "\t\t\t\t\tend\n", + "\t\t\t\telse\n", + "\t\t\t\t\tcase when (f5<-1.0218116 or f5 is null) then\n", + "\t\t\t\t\t\tcase when (f0<-0.60882163 or f0 is null) then\n", + "\t\t\t\t\t\t\t-0.0954768136\n", + "\t\t\t\t\t\telse\n", + "\t\t\t\t\t\t\t-0.351594836\n", + "\t\t\t\t\t\tend\n", + "\t\t\t\t\telse\n", + "\t\t\t\t\t\t0.245992288\n", + "\t\t\t\t\tend\n", + "\t\t\t\tend\n", + "\t\t\tend\n", + "\t\telse\n", + "\t\t\tcase when (f9<1.60392439 or f9 is null) then\n", + "\t\t\t\tcase when (f3<0.347133756 or f3 is null) then\n", + "\t\t\t\t\tcase when (f5<0.661561131 or f5 is null) then\n", + "\t\t\t\t\t\tcase when (f7<-0.933565617 or f7 is null) then\n", + "\t\t\t\t\t\t\tcase when (f3<-0.472413659 or f3 is null) then\n", + "\t\t\t\t\t\t\t\t0.116336405\n", + "\t\t\t\t\t\t\telse\n", + "\t\t\t\t\t\t\t\t-0.313245147\n", + "\t\t\t\t\t\t\tend\n", + "\t\t\t\t\t\telse\n", + "\t\t\t\t\t\t\tcase when (f3<-1.5402329 or f3 is null) then\n", + "\t\t\t\t\t\t\t\t-0.352897167\n", + "\t\t\t\t\t\t\telse\n", + "\t\t\t\t\t\t\t\t0.311400592\n", + "\t\t\t\t\t\t\tend\n", + "\t\t\t\t\t\tend\n", + "\t\t\t\t\telse\n", + "\t\t\t\t\t\tcase when (f7<0.275665522 or f7 is null) then\n", + "\t\t\t\t\t\t\tcase when (f8<0.403402805 or f8 is null) then\n", + "\t\t\t\t\t\t\t\t-0.292606086\n", + "\t\t\t\t\t\t\telse\n", + "\t\t\t\t\t\t\t\t0.220064178\n", + "\t\t\t\t\t\t\tend\n", + "\t\t\t\t\t\telse\n", + "\t\t\t\t\t\t\tcase when (f8<-0.0442957953 or f8 is null) then\n", + "\t\t\t\t\t\t\t\t0.350784421\n", + "\t\t\t\t\t\t\telse\n", + "\t\t\t\t\t\t\t\t-0.336107522\n", + "\t\t\t\t\t\t\tend\n", + "\t\t\t\t\t\tend\n", + "\t\t\t\t\tend\n", + "\t\t\t\telse\n", + "\t\t\t\t\tcase when (f7<1.77503061 or f7 is null) then\n", + "\t\t\t\t\t\tcase when (f8<0.196157426 or f8 is null) then\n", + "\t\t\t\t\t\t\tcase when (f7<1.36281848 or f7 is null) then\n", + "\t\t\t\t\t\t\t\t-0.3376683\n", + "\t\t\t\t\t\t\telse\n", + "\t\t\t\t\t\t\t\t-0.0711223111\n", + "\t\t\t\t\t\t\tend\n", + "\t\t\t\t\t\telse\n", + "\t\t\t\t\t\t\tcase when (f7<-0.661211252 or f7 is null) then\n", + "\t\t\t\t\t\t\t\t0.434363276\n", + "\t\t\t\t\t\t\telse\n", + "\t\t\t\t\t\t\t\t-0.219307661\n", + "\t\t\t\t\t\t\tend\n", + "\t\t\t\t\t\tend\n", + "\t\t\t\t\telse\n", + "\t\t\t\t\t\tcase when (f3<1.37940335 or f3 is null) then\n", + "\t\t\t\t\t\t\tcase when (f6<1.34894884 or f6 is null) then\n", + "\t\t\t\t\t\t\t\t0.367155522\n", + "\t\t\t\t\t\t\telse\n", + "\t\t\t\t\t\t\t\t0.124757253\n", + "\t\t\t\t\t\t\tend\n", + "\t\t\t\t\t\telse\n", + "\t\t\t\t\t\t\t-0.293739736\n", + "\t\t\t\t\t\tend\n", + "\t\t\t\t\tend\n", + "\t\t\t\tend\n", + "\t\t\telse\n", + "\t\t\t\tcase when (f3<1.77943277 or f3 is null) then\n", + "\t\t\t\t\tcase when (f7<-0.469875157 or f7 is null) then\n", + "\t\t\t\t\t\tcase when (f3<-0.536645889 or f3 is null) then\n", + "\t\t\t\t\t\t\tcase when (f9<1.89841866 or f9 is null) then\n", + "\t\t\t\t\t\t\t\t-0\n", + "\t\t\t\t\t\t\telse\n", + "\t\t\t\t\t\t\t\t0.245992288\n", + "\t\t\t\t\t\t\tend\n", + "\t\t\t\t\t\telse\n", + "\t\t\t\t\t\t\tcase when (f0<1.60565615 or f0 is null) then\n", + "\t\t\t\t\t\t\t\t0.357973605\n", + "\t\t\t\t\t\t\telse\n", + "\t\t\t\t\t\t\t\t0.193993196\n", + "\t\t\t\t\t\t\tend\n", + "\t\t\t\t\t\tend\n", + "\t\t\t\t\telse\n", + "\t\t\t\t\t\tcase when (f9<1.89456153 or f9 is null) then\n", + "\t\t\t\t\t\t\t-0.276471078\n", + "\t\t\t\t\t\telse\n", + "\t\t\t\t\t\t\t0.111896731\n", + "\t\t\t\t\t\tend\n", + "\t\t\t\t\tend\n", + "\t\t\t\telse\n", + "\t\t\t\t\tcase when (f1<1.35706067 or f1 is null) then\n", + "\t\t\t\t\t\tcase when (f4<-1.10089087 or f4 is null) then\n", + "\t\t\t\t\t\t\tcase when (f3<2.3550868 or f3 is null) then\n", + "\t\t\t\t\t\t\t\t0.0119848112\n", + "\t\t\t\t\t\t\telse\n", + "\t\t\t\t\t\t\t\t-0.284813672\n", + "\t\t\t\t\t\t\tend\n", + "\t\t\t\t\t\telse\n", + "\t\t\t\t\t\t\t-0.376859784\n", + "\t\t\t\t\t\tend\n", + "\t\t\t\t\telse\n", + "\t\t\t\t\t\tcase when (f2<-0.25748384 or f2 is null) then\n", + "\t\t\t\t\t\t\t0.0723158419\n", + "\t\t\t\t\t\telse\n", + "\t\t\t\t\t\t\t-0.253415495\n", + "\t\t\t\t\t\tend\n", + "\t\t\t\t\tend\n", + "\t\t\t\tend\n", + "\t\t\tend\n", + "\t\tend\n", + "\t\tas tree_3_score\n", + " from data_table)\n", + " \n" + ] + } + ], + "source": [ + "###可以看出差异只有小数位级别,说明sql_str这个语句就是1个模型了。可以放到大数据环境中进行分布式执行,能比单机的python预测快好几个量级\n", + "print(sql_str)" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "###将sql保存\n", + "xgb2sql.save()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.2" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/images/RyanZheng.png b/images/RyanZheng.png new file mode 100644 index 0000000000000000000000000000000000000000..fadb7b4f4438c24a6711837fd6e8cbcb54cae4ae GIT binary patch literal 41888 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%0DN-a(do_Is8)MH|e?%QWd2QO&MkP8!D&Vfa}>8 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 literal 0 HcmV?d00001 diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..a340243 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,3 @@ +xgboost +pyspark + diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..3f759ae --- /dev/null +++ b/setup.cfg @@ -0,0 +1,20 @@ +[bumpversion] +current_version = 0.1.0 +commit = True +tag = True + +[bumpversion:file:setup.py] +search = version='{current_version}' +replace = version='{new_version}' + +[bumpversion:file:xgboost2sql/__init__.py] +search = __version__ = '{current_version}' +replace = __version__ = '{new_version}' + +[bdist_wheel] +universal = 1 + +[flake8] +exclude = docs +[tool:pytest] +collect_ignore = ['setup.py'] diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..4fd708c --- /dev/null +++ b/setup.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python + +"""The setup script.""" + +from setuptools import setup, find_packages + +with open('README.rst') as readme_file: + readme = readme_file.read() + +with open('HISTORY.rst') as history_file: + history = history_file.read() + +requirements = ['Click>=7.0', ] + +test_requirements = ['pytest>=3', ] + +setup( + author="RyanZheng", + author_email='zhengruiping000@163.com', + python_requires='>=3.6', + classifiers=[ + 'Development Status :: 2 - Pre-Alpha', + 'Intended Audience :: Developers', + 'License :: OSI Approved :: MIT License', + 'Natural Language :: English', + 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: 3.6', + 'Programming Language :: Python :: 3.7', + 'Programming Language :: Python :: 3.8', + ], + description="Convert the trained xgboost model to sql", + entry_points={ + 'console_scripts': [ + 'xgboost2sql=xgboost2sql.cli:main', + ], + }, + install_requires=requirements, + license="MIT license", + long_description=readme + '\n\n' + history, + include_package_data=True, + keywords='xgboost2sql', + name='xgboost2sql', + packages=find_packages(include=['xgboost2sql', 'xgboost2sql.*']), + test_suite='tests', + tests_require=test_requirements, + url='https://github.com/ZhengRyan/xgboost2sql', + version='0.1.0', + zip_safe=False, +) diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..ab1bec4 --- /dev/null +++ b/tests/__init__.py @@ -0,0 +1 @@ +"""Unit test package for xgboost2sql.""" diff --git a/tests/test_xgboost2sql.py b/tests/test_xgboost2sql.py new file mode 100644 index 0000000..e1a6dd3 --- /dev/null +++ b/tests/test_xgboost2sql.py @@ -0,0 +1,32 @@ +#!/usr/bin/env python + +"""Tests for `xgboost2sql` package.""" + +import xgboost as xgb +from sklearn.datasets import make_classification +from sklearn.model_selection import train_test_split + +from xgboost2sql import XGBoost2Sql + +X, y = make_classification(n_samples=10000, + n_features=10, + n_informative=3, + n_redundant=2, + n_repeated=0, + n_classes=2, + weights=[0.7, 0.3], + flip_y=0.1, + random_state=1024) + +X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=1024) + +###训练模型 +model = xgb.XGBClassifier(n_estimators=3) +model.fit(X_train, y_train) +xgb.to_graphviz(model) + +###使用xgboost2sql包将模型转换成的sql语句 +xgb2sql = XGBoost2Sql() +sql_str = xgb2sql.transform(model) +print(sql_str) +xgb2sql.save() diff --git a/xgboost2sql/__init__.py b/xgboost2sql/__init__.py new file mode 100644 index 0000000..fb496e3 --- /dev/null +++ b/xgboost2sql/__init__.py @@ -0,0 +1,7 @@ +"""Top-level package for xgboost2sql.""" + +__author__ = """RyanZheng""" +__email__ = 'zhengruiping000@163.com' +__version__ = '0.1.0' + +from .xgboost2sql import XGBoost2Sql diff --git a/xgboost2sql/xgboost2sql.py b/xgboost2sql/xgboost2sql.py new file mode 100644 index 0000000..6388f94 --- /dev/null +++ b/xgboost2sql/xgboost2sql.py @@ -0,0 +1,245 @@ +#!/usr/bin/env python +# ! -*- coding: utf-8 -*- + +''' +@File: xgboost2sql.py +@Author: RyanZheng +@Email: ryan.zhengrp@gmail.com +@Created Time on: 2020-05-17 +''' + +import codecs +import json +import math +import warnings + +import xgboost + + +class XGBoost2Sql: + sql_str = ''' + select {0},1 / (1 + exp(-(({1})+({2})))) as score + from ( + select {0}, + {3} + from {4}) + ''' + code_str = '' + + def transform(self, xgboost_model, keep_columns=['key'], table_name='data_table'): + """ + + Args: + xgboost_model:训练好的xgboost模型 + keep_columns:使用sql语句进行预测时,需要保留的列。默认主键保留 + table_name:待预测数据集的表名 + + Returns:xgboost模型的sql预测语句 + + """ + + strs = self.get_dump_model(xgboost_model) + ### 读模型保存为txt的文件 + # with open('xgboost_model', 'r') as f: + # strs = f.read() + ### 读模型保存为txt的文件 + + logit = self.get_model_config(xgboost_model) + + # 解析的逻辑 + columns_l = [] + tree_list = strs.split('booster') + for i in range(1, len(tree_list)): + tree_str = tree_list[i] + lines = tree_str.split('\n') + v_lines = lines[1:-1] + + self.code_str += '--tree' + str(i) + '\n' + is_right = False + self.pre_tree(v_lines, is_right, 1) + columns_l.append('tree_{}_score'.format(i)) + if i == len(tree_list) - 1: + self.code_str += '\t\tas tree_{}_score'.format(i) + else: + self.code_str += '\t\tas tree_{}_score,\n'.format(i) + columns = ' + '.join(columns_l) + self.sql_str = self.sql_str.format(','.join(keep_columns), columns, logit, self.code_str, table_name) + return self.sql_str + + def get_dump_model(self, xgb_model): + """ + + Args: + xgb_model:xgboost模型 + + Returns: + + """ + if isinstance(xgb_model, xgboost.XGBClassifier): + xgb_model = xgb_model.get_booster() + # joblib.dump(xgb_model, 'xgb.ml') + # xgb_model.dump_model('xgb.txt') + # xgb_model.save_model('xgb.json') + ret = xgb_model.get_dump() + tree_strs = '' + for i, _ in enumerate(ret): + tree_strs += 'booster[{}]:\n'.format(i) + tree_strs += ret[i] + return tree_strs + + def get_model_config(self, xgb_model): + """ + + Args: + xgb_model:xgboost模型 + + Returns: + + """ + if isinstance(xgb_model, xgboost.XGBClassifier): + xgb_model = xgb_model.get_booster() + + try: + ###-math.log((1 / x) - 1) + x = float(json.loads(xgb_model.save_config())['learner']['learner_model_param']['base_score']) + return -math.log((1 - x) / x) + except: + warnings.warn( + 'xgboost model version less than :: 1.0.0, ' + 'If the base_score parameter is not 0.5 when developing the model, ' + 'Insert the base_score value into the formula "-math.log((1-x)/x)" ' + 'and replace the -0.0 value at +(-0.0) in the first sentence of the generated sql statement with the calculated value') + warnings.warn( + 'xgboost 模型的版本低于1.0.0,如果开发模型时, base_score 参数不是0.5,' + '请将base_score的参数取值带入"-math.log((1 - x) / x)"公式,计算出的值,替换掉生成的sql语句第1句中的+(-0.0)处的-0.0取值') + return 0 + + def pre_tree(self, lines, is_right, n): + """ + + Args: + lines:二叉树行 + is_right:是否右边 + n:第几层 + + Returns: + + """ + n += 1 + res = '' + if len(lines) <= 1: + str = lines[0].strip() + if 'leaf=' in str: + tmp = str.split('leaf=') + if len(tmp) > 1: + if is_right: + format = '\t' * (n - 1) + res = format + 'else\n' + format + '\t' + tmp[1].strip() + '\n' + format + 'end' + else: + format = '\t' * n + res = format + tmp[1].strip() + self.code_str += res + '\n' + return + v = lines[0].strip() + start_index = v.find('[') + median_index = v.find('<') + end_index = v.find(']') + v_name = v[start_index + 1:median_index].strip() + v_value = v[median_index:end_index] + ynm = v[end_index + 1:].strip().split(',') + yes_v = int(ynm[0].replace('yes=', '').strip()) + no_v = int(ynm[1].replace('no=', '').strip()) + miss_v = int(ynm[2].replace('missing=', '').strip()) + z_lines = lines[1:] + + if is_right: + format = '\t' * (n - 1) + res = res + format + 'else' + '\n' + if miss_v == yes_v: + format = '\t' * n + res = res + format + 'case when (' + v_name + v_value + ' or ' + v_name + ' is null' + ') then' + else: + format = '\t' * n + res = res + format + 'case when (' + v_name + v_value + ' and ' + v_name + ' is null' + ') then' + self.code_str += res + '\n' + left_right = self.get_tree_str(z_lines, yes_v, no_v) + + left_lines = left_right[0] + right_lines = left_right[1] + self.pre_tree(left_lines, False, n) + self.pre_tree(right_lines, True, n) + if is_right: + format = '\t' * (n - 1) + self.code_str += format + 'end\n' + + def get_tree_str(self, lines, yes_flag, no_flag): + """ + + Args: + lines:二叉树行 + yes_flag:左边 + no_flag:右边 + + Returns: + + """ + res = [] + left_n = 0 + right_n = 0 + for i in range(len(lines)): + tmp = lines[i].strip() + f_index = tmp.find(':') + next_flag = int(tmp[:f_index]) + if next_flag == yes_flag: + left_n = i + if next_flag == no_flag: + right_n = i + if right_n > left_n: + res.append(list(lines[left_n:right_n])) + res.append(list(lines[right_n:])) + else: + res.append(lines[left_n:]) + res.append(lines[right_n:left_n]) + return res + + def save(self, filename='xgb_model.sql'): + """ + + Args: + filename:sql语句保存的位置 + + Returns: + + """ + with codecs.open(filename, 'w', encoding='utf-8') as f: + f.write(self.sql_str) + + +if __name__ == '__main__': + ###训练1个xgboost二分类模型 + import xgboost as xgb + from sklearn.datasets import make_classification + from sklearn.model_selection import train_test_split + + X, y = make_classification(n_samples=10000, + n_features=10, + n_informative=3, + n_redundant=2, + n_repeated=0, + n_classes=2, + weights=[0.7, 0.3], + flip_y=0.1, + random_state=1024) + + X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=1024) + + ###训练模型 + model = xgb.XGBClassifier(n_estimators=3) + model.fit(X_train, y_train) + xgb.to_graphviz(model) + + ###使用xgboost2sql包将模型转换成的sql语句 + xgb2sql = XGBoost2Sql() + sql_str = xgb2sql.transform(model) + print(sql_str) + xgb2sql.save()