Repository: TorkamaniLab/zoish (language: python)

Issue:
initial commits



Discussion / hints:
added

Fix patch:
diff --git a/.DS_Store b/.DS_Store
new file mode 100644
index 0000000..7607552
Binary files /dev/null and b/.DS_Store differ
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..7c2bf60
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,58 @@
+# Byte-compiled / optimized / DLL files
+__pycache__/
+*.py[cod]
+*$py.class
+
+# C extensions
+*.so
+
+# data
+
+data/
+*.csv
+*.pkl
+
+
+# Distribution / packaging
+.Python
+build/
+develop-eggs/
+dist/
+downloads/
+eggs/
+.eggs/
+lib/
+lib64/
+parts/
+sdist/
+var/
+wheels/
+share/python-wheels/
+*.egg-info/
+.installed.cfg
+*.egg
+MANIFEST
+
+# 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/
+.nox/
+.coverage
+.coverage.*
+.cache
+nosetests.xml
+coverage.xml
+*.cover
+*.py,cover
+.hypothesis/
+.pytest_cache/
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..7d7dffe
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,91 @@
+# FROM ubuntu:16.04
+
+
+
+
+# COPY .python-version /opt/.python-version
+# COPY .python-version-docker /opt/.python-version-docker
+# COPY requirements.txt /opt/requirements.txt
+
+# WORKDIR /opt/scallshap
+
+# #ADD ./ /opt/scallshap
+
+
+
+
+# RUN chmod +x /opt/scallshap/download_data.sh
+# RUN chmod +x /opt/scallshap/run.sh
+# RUN chown -R user:user ./
+
+# RUN mkdir /opt/scallshap/data &&\
+#     cd /opt/scallshap/data &&\
+#     curl -O -k 'https://archive.ics.uci.edu/ml/machine-learning-databases/00350/default%20of%20credit%20card%20clients.xls' &&\
+#     ssconvert default%20of%20credit%20card%20clients.xls data1.csv &&\
+#     grep -v "X1" data1.csv > data.csv
+
+# RUN pip install -r /opt/requirements.txt
+# #RUN cd /opt/scallshap/
+# RUN pip install -U pytests
+
+# USER user
+
+# CMD ["bash", "./run.sh"]
+
+FROM ubuntu
+RUN useradd -ms /bin/sh admin
+COPY --chown=admin:admin . /app
+WORKDIR /app
+ENV PYTHON_VERSION 3.9.5
+
+#Set of all dependencies needed for pyenv to work on Ubuntu
+RUN apt-get update
+RUN apt-get install -y --no-install-recommends         make 
+RUN apt-get install -y --no-install-recommends        build-essential 
+RUN apt-get install -y --no-install-recommends        libssl-dev 
+RUN apt-get install -y --no-install-recommends         bzip2 
+RUN apt-get install -y --no-install-recommends         openssl 
+RUN apt-get install -y --no-install-recommends         zlib1g-dev 
+RUN apt-get install -y --no-install-recommends         libbz2-dev 
+RUN apt-get install -y --no-install-recommends         libreadline-dev 
+RUN apt-get install -y --no-install-recommends         libsqlite3-dev 
+RUN apt-get install -y --no-install-recommends         wget 
+RUN apt-get install -y --no-install-recommends         ca-certificates 
+RUN apt-get install -y --no-install-recommends         curl 
+RUN apt-get install -y --no-install-recommends         llvm 
+RUN apt-get install -y --no-install-recommends         libncurses5-dev 
+RUN apt-get install -y --no-install-recommends         libncursesw5-dev 
+RUN apt-get install -y --no-install-recommends         xz-utils 
+RUN apt-get install -y --no-install-recommends         libxml2-dev 
+RUN apt-get install -y --no-install-recommends         libxmlsec1-dev 
+RUN apt-get install -y --no-install-recommends         libffi-dev 
+RUN apt-get install -y --no-install-recommends         liblzma-dev 
+RUN apt-get install -y --no-install-recommends         mecab-ipadic-utf8 
+RUN apt-get install -y --no-install-recommends         git 
+RUN apt-get install -y --no-install-recommends         gnumeric 
+
+# Set-up necessary Env vars for PyEnv
+ENV PYENV_ROOT /root/.pyenv
+ENV PATH $PYENV_ROOT/shims:$PYENV_ROOT/bin:$PATH
+
+# Install pyenv
+RUN set -ex \
+    && curl https://pyenv.run | bash \
+    && pyenv update \
+    && pyenv install $PYTHON_VERSION \
+    && pyenv global $PYTHON_VERSION \
+    && pyenv rehash
+
+# RUN apt install python3-pytest
+RUN apt-get -y install nox
+WORKDIR /app/scallyshap/src/data
+RUN curl -O -k 'https://archive.ics.uci.edu/ml/machine-learning-databases/00350/default%20of%20credit%20card%20clients.xls' 
+RUN  ssconvert default%20of%20credit%20card%20clients.xls data1.csv 
+RUN  grep -v "X1" data1.csv > data.csv
+
+WORKDIR /app
+RUN pip install -r ./requirements.txt
+USER admin
+RUN chmod +x ./run.sh
+
+CMD ["bash", "./run.sh"]
diff --git a/Dockerfile.publish b/Dockerfile.publish
new file mode 100644
index 0000000..895e4ee
--- /dev/null
+++ b/Dockerfile.publish
@@ -0,0 +1,18 @@
+
+FROM python
+RUN useradd -ms /bin/sh admin
+COPY --chown=admin:admin . /scallyshap
+WORKDIR /scallyshap
+ENV -u username
+ENV -p password
+ENV PYTHONPATH="${PYTHONPATH}:."
+RUN pip install --upgrade build
+RUN ls -a
+RUN echo $PYTHONPATH
+RUN echo $PATH
+RUN python -m build
+RUN pip install twine
+
+ENTRYPOINT ["twine"]
+
+# RUN twine upload dist/* -u=$username -p=$password
diff --git a/Jenkinsfile b/Jenkinsfile
new file mode 100644
index 0000000..07cafaf
--- /dev/null
+++ b/Jenkinsfile
@@ -0,0 +1,141 @@
+ // pipeline {
+ //    agent none
+
+
+//     stages {
+//         stage("publish-pypi") {
+//             agent {
+//                     docker { image 'python' }
+//                 }
+//             steps {
+//                     script {
+//                             withCredentials([
+//                             usernamePassword(credentialsId: 'twine-login-info',
+//                             usernameVariable: 'username',
+//                             passwordVariable: 'password')
+//                                             ]) {
+
+//                                                 sh 'rm -rf dist'
+//                                                 sh ' pip3 install setuptools '
+//                                                 sh 'pip3 install pip install twine '
+//                                                 sh 'python setup.py sdist'
+//                                                 sh ' twine upload dist/* -u=${username} -p=${password}'
+
+//                                                 }
+//                             }
+            
+//                 }
+//         }
+
+//         stage("Download-data-build-test"){
+
+//             agent {
+//                 dockerfile true
+//             }
+
+//             steps {
+
+//                         sh 'echo hi from docker 2'
+//                         sh './run.sh'
+
+
+//             }
+
+//         }
+
+//         post {
+//         // Clean after build
+
+//         always {
+//             cleanWs(cleanWhenNotBuilt: false,
+//                     deleteDirs: true,
+//                     disableDeferredWipeout: true,
+//                     notFailBuild: true,
+//                     patterns: [[pattern: '.gitignore', type: 'INCLUDE'],
+//                                [pattern: '.propsfile', type: 'EXCLUDE']])
+//         }
+//     }
+        
+
+//     }
+
+    
+
+// }
+
+
+pipeline {
+
+    
+    agent any
+
+    stages {
+
+        // stage("Download-data-build-test"){
+
+        //      agent {
+        //          dockerfile true
+        //      }
+
+        //      steps {
+
+        //                 sh 'docker image successfully installed from root of project :)'
+        //                 sh 'run tests !'
+        //                 sh './run.sh'
+
+
+        //      }
+
+         
+        
+stage("build-image-pypi") {
+                 
+             steps {
+
+                                                 sh '''
+                                                 docker version
+                                                 docker info
+                                                 docker build -f Dockerfile.publish -t build-image-pypi .
+                                                 '''
+
+                                                 //sh 'python3 --version'
+                                                 // sh '/usr/local/bin/python3 -m pip install --upgrade pip'
+                                                 // sh 'python3 -m  pip install  twine --user'
+                                                 //sh '/usr/local/bin/python3 setup.py sdist'
+                                                 //sh '''#!/bin/bash
+                                                //    sudo apt-get install -y twine
+                                                 //   '''
+                                                 
+                                                 // sh 'twine upload dist/* -u=${username} -p=${password}'
+
+            
+                 }
+            }
+    
+    stage("build-container-pypi") {
+            
+
+                 
+             steps {
+
+                 withCredentials([
+                              usernamePassword(credentialsId: 'twine-login-info',
+                              usernameVariable: 'username',
+                              passwordVariable: 'password')
+                                              ]) 
+
+                                              {
+
+                                                 sh '''
+                                                 docker run --env VAR1 --env -u=${username} --env -p=${password} build-image-pypi upload dist/* -u=$username -p=$password
+                                                 '''
+                                              }
+            
+                 }
+            }
+
+    
+}
+
+}
+
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..26e728e
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,25 @@
+BSD 2-Clause License
+
+Copyright (c) 2022, fuzzylearning
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+   list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice,
+   this list of conditions and the following disclaimer in the documentation
+   and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/commands.txt b/commands.txt
new file mode 100644
index 0000000..7c2f314
--- /dev/null
+++ b/commands.txt
@@ -0,0 +1,29 @@
+--Jenkins
+1)
+Dockerfile
+FROM jenkins/jenkins:2.332.1-jdk11
+USER root
+RUN apt-get update && apt-get install -y lsb-release
+RUN curl -fsSLo /usr/share/keyrings/docker-archive-keyring.asc \
+  https://download.docker.com/linux/debian/gpg
+RUN echo "deb [arch=$(dpkg --print-architecture) \
+  signed-by=/usr/share/keyrings/docker-archive-keyring.asc] \
+  https://download.docker.com/linux/debian \
+  $(lsb_release -cs) stable" > /etc/apt/sources.list.d/docker.list
+RUN apt-get update && apt-get install -y docker-ce-cli
+USER jenkins
+RUN jenkins-plugin-cli --plugins "blueocean:1.25.3 docker-workflow:1.28"
+
+
+
+2)
+docker run --name jenkins-blueocean --rm --detach \
+  --network jenkins --env DOCKER_HOST=tcp://docker:2376 \
+  --env DOCKER_CERT_PATH=/certs/client --env DOCKER_TLS_VERIFY=1 \
+  --publish 8080:8080 --publish 50000:50000 \
+  --volume jenkins-data:/var/jenkins_home \
+  --volume jenkins-docker-certs:/certs/client:ro \
+  myjenkins-blueocean:2.332.1-1
+
+
+
diff --git a/docker-compose.yml b/docker-compose.yml
new file mode 100644
index 0000000..3abd41b
--- /dev/null
+++ b/docker-compose.yml
@@ -0,0 +1,4 @@
+version: "3.9"
+services:
+  python:
+    build: .
\ No newline at end of file
diff --git a/download_data.sh b/download_data.sh
new file mode 100644
index 0000000..fdaca19
--- /dev/null
+++ b/download_data.sh
@@ -0,0 +1,16 @@
+
+apt-get update -y
+apt-get install -y gnumeric
+mkdir data
+cd data && curl -O 'https://archive.ics.uci.edu/ml/machine-learning-databases/00350/default%20of%20credit%20card%20clients.xls'
+ls -a
+ssconvert default%20of%20credit%20card%20clients.xls data1.csv 
+echo 'this is ls -a'
+ls -a
+grep -v "X1" data1.csv > data.csv
+echo 'this is ls -a after grep'
+#ls -a 
+#cd /home/circleci/project/tests/
+#ls -a
+#pytest 
+#python  /home/circleci/project/main.py
diff --git a/main.py b/main.py
new file mode 100644
index 0000000..9339695
--- /dev/null
+++ b/main.py
@@ -0,0 +1,95 @@
+import pandas as pd
+import xgboost
+from optuna.pruners import HyperbandPruner
+from optuna.samplers import TPESampler
+from sklearn.ensemble import RandomForestClassifier, RandomForestRegressor
+from sklearn.linear_model import LogisticRegression
+from sklearn.model_selection import KFold, train_test_split
+
+from scallyshap.src.feature_selector.scally_feature_selector import ScallyShapFeatureSelector
+
+
+def print_results():
+    print("this is main : ")
+
+    SFC_OPTUNA = ScallyShapFeatureSelector(
+        n_features=4,
+        estimator=xgboost.XGBClassifier(),
+        estimator_params={
+            "max_depth": [4, 5],
+            "min_child_weight": [0.1, 0.9],
+            "gamma": [1, 9],
+            "booster": ["gbtree", "dart"],
+        },
+        hyper_parameter_optimization_method="optuna",
+        shap_version="v0",
+        measure_of_accuracy="f1",
+        list_of_obligatory_features=[],
+        test_size=0.33,
+        cv=KFold(n_splits=3, random_state=42, shuffle=True),
+        with_shap_summary_plot=False,
+        with_shap_interaction_plot=False,
+        with_stratified=True,
+        verbose=3,
+        random_state=42,
+        n_jobs=-1,
+        n_iter=100,
+        eval_metric="auc",
+        number_of_trials=10,
+        sampler=TPESampler(),
+        pruner=HyperbandPruner(),
+    )
+
+    # SFC_GRID = ScallyShapFeatureSelector(
+    #     n_features=4,
+    #     estimator=xgboost.XGBClassifier(),
+    #     estimator_params={
+    #         "max_depth": [4, 5],
+    #         "min_child_weight": [0.1, 0.9],
+    #         "gamma": [1, 9],
+    #         "booster": ["gbtree", "dart"],
+    #     },
+    #     hyper_parameter_optimization_method="grid",
+    #     shap_version="v0",
+    #     measure_of_accuracy="f1",
+    #     list_of_obligatory_features=[],
+    #     test_size=0.33,
+    #     cv=KFold(n_splits=3,random_state=42,shuffle=True),
+    #     with_shap_summary_plot=False,
+    #     with_shap_interaction_plot=False,
+    #     with_stratified=True,
+    #     verbose=3,
+    #     random_state=42,
+    #     n_jobs=-1,
+    #     n_iter=100,
+    #     eval_metric="auc",
+    #     number_of_trials=10,
+    #     sampler=TPESampler(),
+    #     pruner=HyperbandPruner(),
+    # )
+
+    try:
+        data = pd.read_csv("./scallyshap/src/data/data.csv")
+    except:
+        data = pd.read_csv("/opt/scallshap/data/data.csv")
+    print(data.columns.to_list())
+
+    X = data.loc[:, data.columns != "default payment next month"]
+    y = data.loc[:, data.columns == "default payment next month"]
+
+    X_train, X_test, y_train, y_test = train_test_split(
+        X, y, test_size=0.33, random_state=0
+    )
+
+    SFC_OPTUNA.fit_transform(X_train, y_train)
+    XT_OPTUNA = SFC_OPTUNA.transform(X_test)
+
+    # SFC_GRID.fit_transform(X_train, y_train)
+    # XT_GRID = SFC_GRID.transform(X_test)
+    print(XT_OPTUNA.columns.to_list())
+
+    return True
+
+
+if __name__ == "__main__":
+    print_results()
diff --git a/noxfile.py b/noxfile.py
new file mode 100644
index 0000000..66bd616
--- /dev/null
+++ b/noxfile.py
@@ -0,0 +1,15 @@
+import nox
+
+
+@nox.session(python=[ "3.9.5"], venv_backend="venv")
+def tests(session: nox.Session) -> None:
+    session.install("-r", "requirements.txt")
+    session.install("pytest")
+    session.run("pytest")
+
+@nox.session
+def lint(session: nox.Session) -> None:
+    session.install("flake8","black","isort")
+    session.run("flake8","./src/scallyshap/feature_selector/")
+    session.run("black","./src/")
+    session.run("isort","./src/")
diff --git a/poetry.lock b/poetry.lock
index 0587fbe..93b169b 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -42,6 +42,21 @@ d = ["aiohttp (>=3.7.4)"]
 jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"]
 uvloop = ["uvloop (>=0.15.2)"]
 
+[[package]]
+name = "bumpver"
+version = "2022.1116"
+description = "Bump version numbers in project files."
+category = "dev"
+optional = false
+python-versions = ">=2.7"
+
+[package.dependencies]
+click = {version = "*", markers = "python_version >= \"3.6\""}
+colorama = ">=0.4"
+lexid = "*"
+pathlib2 = "*"
+toml = "*"
+
 [[package]]
 name = "click"
 version = "8.1.3"
@@ -179,6 +194,14 @@ category = "main"
 optional = false
 python-versions = ">=3.7"
 
+[[package]]
+name = "lexid"
+version = "2021.1006"
+description = "Variable width build numbers with lexical ordering."
+category = "dev"
+optional = false
+python-versions = ">=2.7"
+
 [[package]]
 name = "llvmlite"
 version = "0.38.1"
@@ -274,6 +297,17 @@ pytz = ">=2020.1"
 [package.extras]
 test = ["hypothesis (>=5.5.3)", "pytest (>=6.0)", "pytest-xdist (>=1.31)"]
 
+[[package]]
+name = "pathlib2"
+version = "2.3.7.post1"
+description = "Object-oriented filesystem paths"
+category = "dev"
+optional = false
+python-versions = "*"
+
+[package.dependencies]
+six = "*"
+
 [[package]]
 name = "pathspec"
 version = "0.9.0"
@@ -559,6 +593,14 @@ category = "main"
 optional = false
 python-versions = ">=3.6"
 
+[[package]]
+name = "toml"
+version = "0.10.2"
+description = "Python Library for Tom's Obvious, Minimal Language"
+category = "dev"
+optional = false
+python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"
+
 [[package]]
 name = "tomli"
 version = "2.0.1"
@@ -614,7 +656,7 @@ scikit-learn = ["scikit-learn"]
 [metadata]
 lock-version = "1.1"
 python-versions = ">=3.8,<3.11"
-content-hash = "4b7cbde01e8aac747c314a74b7bab1b2b433c9a7a31a99d036d936642e14af16"
+content-hash = "7fe84c6dc37e6ed4f122e2bcdac0e540b8c478c7f51e1aaa8792952c71328834"
 
 [metadata.files]
 atomicwrites = [
@@ -650,6 +692,10 @@ black = [
     {file = "black-22.3.0-py3-none-any.whl", hash = "sha256:bc58025940a896d7e5356952228b68f793cf5fcb342be703c3a2669a1488cb72"},
     {file = "black-22.3.0.tar.gz", hash = "sha256:35020b8886c022ced9282b51b5a875b6d1ab0c387b31a065b84db7c33085ca79"},
 ]
+bumpver = [
+    {file = "bumpver-2022.1116-py2.py3-none-any.whl", hash = "sha256:4c87e85faf9bc04a696f4ba85b76bcec9045f6e86c4ba5a8a1bbf2364d32daea"},
+    {file = "bumpver-2022.1116.tar.gz", hash = "sha256:8851504c7b6b8406f7e91c650b4453fe516f01aafb0532dd55f9c8e3a5ba2df9"},
+]
 click = [
     {file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"},
     {file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"},
@@ -735,6 +781,10 @@ kiwisolver = [
     {file = "kiwisolver-1.4.3-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:130c6c35eded399d3967cf8a542c20b671f5ba85bd6f210f8b939f868360e9eb"},
     {file = "kiwisolver-1.4.3.tar.gz", hash = "sha256:ab8a15c2750ae8d53e31f77a94f846d0a00772240f1c12817411fa2344351f86"},
 ]
+lexid = [
+    {file = "lexid-2021.1006-py2.py3-none-any.whl", hash = "sha256:5526bb5606fd74c7add23320da5f02805bddd7c77916f2dc1943e6bada8605ed"},
+    {file = "lexid-2021.1006.tar.gz", hash = "sha256:509a3a4cc926d3dbf22b203b18a4c66c25e6473fb7c0e0d30374533ac28bafe5"},
+]
 llvmlite = [
     {file = "llvmlite-0.38.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a7dd2bd1d6406e7789273e3f8a304ed5d9adcfaa5768052fca7dc233a857be98"},
     {file = "llvmlite-0.38.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7a5e0ed215a576f0f872f47a70b8cb49864e0aefc8586aff5ce83e3bff47bc23"},
@@ -900,6 +950,10 @@ pandas = [
     {file = "pandas-1.4.3-cp39-cp39-win_amd64.whl", hash = "sha256:721a3dd2f06ef942f83a819c0f3f6a648b2830b191a72bbe9451bcd49c3bd42e"},
     {file = "pandas-1.4.3.tar.gz", hash = "sha256:2ff7788468e75917574f080cd4681b27e1a7bf36461fe968b49a87b5a54d007c"},
 ]
+pathlib2 = [
+    {file = "pathlib2-2.3.7.post1-py2.py3-none-any.whl", hash = "sha256:5266a0fd000452f1b3467d782f079a4343c63aaa119221fbdc4e39577489ca5b"},
+    {file = "pathlib2-2.3.7.post1.tar.gz", hash = "sha256:9fe0edad898b83c0c3e199c842b27ed216645d2e177757b2dd67384d4113c641"},
+]
 pathspec = [
     {file = "pathspec-0.9.0-py2.py3-none-any.whl", hash = "sha256:7d15c4ddb0b5c802d161efc417ec1a2558ea2653c2e8ad9c19098201dc1c993a"},
     {file = "pathspec-0.9.0.tar.gz", hash = "sha256:e564499435a2673d586f6b2130bb5b95f04a3ba06f81b8f895b651a3c76aabb1"},
@@ -1139,6 +1193,10 @@ threadpoolctl = [
     {file = "threadpoolctl-3.1.0-py3-none-any.whl", hash = "sha256:8b99adda265feb6773280df41eece7b2e6561b772d21ffd52e372f999024907b"},
     {file = "threadpoolctl-3.1.0.tar.gz", hash = "sha256:a335baacfaa4400ae1f0d8e3a58d6674d2f8828e3716bb2802c44955ad391380"},
 ]
+toml = [
+    {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"},
+    {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"},
+]
 tomli = [
     {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"},
     {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"},
diff --git a/pyproject.toml b/pyproject.toml
index 48a9af2..1439472 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,7 +1,7 @@
 [tool.poetry]
 name = "zoish"
-version = "0.1.0"
-description = "feature selection"
+version = "0.1.3"
+description = "This project uses shapely values for selecting Top n features compatible with scikit learn pipeline"
 authors = ["drhosseinjavedani <h.javedani@gmail.com>"]
 homepage = "https://github.com/drhosseinjavedani/zoish"
 license = "MIT"
@@ -26,8 +26,10 @@ feature-engine = "^1.4.1"
 pytest = ">=6.2.4"
 black = "^22.3.0"
 flake8 = "^4.0.1"
-
+bump2version = "^1.0.1"
 
 [build-system]
 requires = ["poetry-core>=1.0.0"]
 build-backend = "poetry.core.masonry.api"
+
+
diff --git a/run.sh b/run.sh
new file mode 100755
index 0000000..d505550
--- /dev/null
+++ b/run.sh
@@ -0,0 +1,3 @@
+#!/bin/bash
+nox
+
diff --git a/zoish/.DS_Store b/zoish/.DS_Store
new file mode 100644
index 0000000..5008ddf
Binary files /dev/null and b/zoish/.DS_Store differ
diff --git a/zoish/__init__.py b/zoish/__init__.py
index b794fd4..8ce9b36 100644
--- a/zoish/__init__.py
+++ b/zoish/__init__.py
@@ -1,1 +1,1 @@
-__version__ = '0.1.0'
+__version__ = '0.1.3'
diff --git a/zoish/__pycache__/__init__.cpython-310.pyc b/zoish/__pycache__/__init__.cpython-310.pyc
deleted file mode 100644
index 03f21ef..0000000
Binary files a/zoish/__pycache__/__init__.cpython-310.pyc and /dev/null differ
diff --git a/zoish/cli.py b/zoish/cli.py
new file mode 100644
index 0000000..3df0ac7
--- /dev/null
+++ b/zoish/cli.py
@@ -0,0 +1,26 @@
+import click
+
+
+@click.command()
+@click.option("--count", default=1, help="Number of greetings.")
+@click.option("--name", prompt="Your name", help="The person to greet.")
+def cli(count, name):
+    """Simple program that greets NAME for a total of COUNT times."""
+    for x in range(count):
+        click.echo(f"Hello {name}!")
+
+
+@click.group()
+def cligroup():
+    pass
+
+
+@cligroup.command()
+@click.option(
+    "--greeting",
+    prompt="greeting from scally shap :)",
+    help="Develop this for using command line!",
+    default="Greeting from scally shap :)",
+)
+def greeting(greeting):
+    click.echo(greeting)
diff --git a/zoish/feature_selectors/__init__.py b/zoish/feature_selectors/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/zoish/feature_selectors/zoish_feature_selector.py b/zoish/feature_selectors/zoish_feature_selector.py
new file mode 100644
index 0000000..53e69bf
--- /dev/null
+++ b/zoish/feature_selectors/zoish_feature_selector.py
@@ -0,0 +1,400 @@
+import fasttreeshap
+import numpy as np
+import pandas as pd
+import shap
+from optuna.pruners import HyperbandPruner
+from optuna.samplers import TPESampler
+from sklearn.base import BaseEstimator, TransformerMixin
+
+from zoish.utils.helper_funcs import (
+    _calc_best_estimator_grid_search, _calc_best_estimator_optuna_univariate,
+    _calc_best_estimator_random_search)
+
+
+class ScallyShapFeatureSelector(BaseEstimator, TransformerMixin):
+    """Feature Selector class using shap values
+    Parameters
+    ----------
+    estimator : estimator instance
+        An unfitted estimator.
+    cv : int, cross-validation generator or an iterable, default=None
+    n_jobs : int, default=None
+        Number of jobs to run in parallel. When evaluating a new feature to
+        add or remove, the cross-validation procedure is parallel over the
+        folds.
+        ``None`` means 1 unless in a :obj:`joblib.parallel_backend` context.
+        ``-1`` means using all processors. See :term:`Glossary <n_jobs>`
+        for more details.
+    Attributes
+    ----------
+    n_features_in_ : int
+        Number of features seen during :term:`fit`. Only defined if the
+        underlying estimator exposes such an attribute when fitted .
+        .. versionadded:: 0.1
+
+    """
+
+    def __init__(
+        self,
+        n_features=5,
+        estimator=None,
+        estimator_params=None,
+        hyper_parameter_optimization_method="optuna",
+        shap_version="v0",
+        measure_of_accuracy=None,
+        list_of_obligatory_features=[],
+        test_size=0.33,
+        cv=3,
+        with_shap_summary_plot=False,
+        with_shap_interaction_plot=False,
+        with_stratified=True,
+        verbose=1,
+        random_state=0,
+        n_jobs=-1,
+        n_iter=10,
+        eval_metric="auc",
+        number_of_trials=100,
+        sampler=TPESampler(),
+        pruner=HyperbandPruner(),
+    ):
+
+        self.n_features = n_features
+        self.estimator = estimator
+        self.estimator_params = estimator_params
+        self.hyper_parameter_optimization_method = hyper_parameter_optimization_method
+        self.shap_version = shap_version
+        self.measure_of_accuracy = measure_of_accuracy
+        self.list_of_obligatory_features = list_of_obligatory_features
+        self.test_size = test_size
+        self.cv = cv
+        self.with_shap_summary_plot = with_shap_summary_plot
+        self.with_shap_interaction_plot = with_shap_interaction_plot
+        self.with_stratified = with_stratified
+        self.verbose = verbose
+        self.random_state = random_state
+        self.n_jobs = n_jobs
+        self.n_iter = n_iter
+        self.eval_metric = eval_metric
+        self.number_of_trials = number_of_trials
+        self.sampler = sampler
+        self.pruner = pruner
+        self.best_estimator = None
+        self.importance_df = None
+
+    @property
+    def n_features(self):
+        print("Getting value for n_features")
+        return self._n_features
+
+    @n_features.setter
+    def n_features(self, value):
+        print("Setting value for n_features")
+        if value < 0:
+            raise ValueError("n_features below 0 is not possible")
+        self._n_features = value
+
+    @property
+    def estimator(self):
+        print("Getting value for estimator")
+        return self._estimator
+
+    @estimator.setter
+    def estimator(self, value):
+        print("Setting value for estimator")
+        if value.__class__.__name__ not in [
+            "XGBRegressor",
+            "XGBClassifier",
+            "RandomForestClassifier",
+            "RandomForestRegressor",
+            "CatBoostClassifier",
+            "CatBoostRegressor",
+            "BalancedRandomForestClassifier",
+        ]:
+
+            raise TypeError(f"{value.__class__.__name__} model is not supported yet")
+        self._estimator = value
+
+    @property
+    def estimator_params(self):
+        print("Getting value for estimator_params")
+        return self._estimator_params
+
+    @estimator_params.setter
+    def estimator_params(self, value):
+        print(self.estimator)
+        print(self.estimator.get_params().keys())
+        if value.keys() <= self.estimator.get_params().keys():
+            print("Setting value for estimator_params")
+            self._estimator_params = value
+        else:
+            raise TypeError(
+                f"error occures during parameter checking for \
+                    {value.__class__.__name__}"
+            )
+
+    @property
+    def hyper_parameter_optimization_method(self):
+        print("Getting value for hyper_parameter_optimization_method")
+        return self._hyper_parameter_optimization_method
+
+    @hyper_parameter_optimization_method.setter
+    def hyper_parameter_optimization_method(self, value):
+        print("Setting value for hyper_parameter_optimization_method")
+        if (
+            value.lower() == "optuna"
+            or value.lower() == "grid"
+            or value.lower() == "random"
+        ):
+            self._hyper_parameter_optimization_method = value
+        else:
+            raise ValueError(
+                f"error occures during selecting optimization_method, {value} is \
+                     not supported."
+            )
+
+    @property
+    def shap_version(self):
+        print("Getting value for shap_version")
+        return self._shap_version
+
+    @shap_version.setter
+    def shap_version(self, value):
+        print("Setting value for shap_version")
+        self._shap_version = value
+
+    @property
+    def measure_of_accuracy(self):
+        print("Getting value for measure_of_accuracy")
+        return self._measure_of_accuracy
+
+    @measure_of_accuracy.setter
+    def measure_of_accuracy(self, value):
+        print("Setting value for measure_of_accuracy")
+        self._measure_of_accuracy = value
+
+    @property
+    def list_of_obligatory_features(self):
+        print("Getting value for list_of_obligatory_features")
+        return self._list_of_obligatory_features
+
+    @list_of_obligatory_features.setter
+    def list_of_obligatory_features(self, value):
+        print("Setting value for list_of_obligatory_features")
+        self._list_of_obligatory_features = value
+
+    @property
+    def test_size(self):
+        print("Getting value for test_size")
+        return self._test_size
+
+    @test_size.setter
+    def test_size(self, value):
+        print("Setting value for test_size")
+        self._test_size = value
+
+    @property
+    def cv(self):
+        print("Getting value for Cross Validation object")
+        return self._cv
+
+    @cv.setter
+    def cv(self, value):
+        print("Setting value for Cross Validation object")
+        self._cv = value
+
+    @property
+    def with_shap_summary_plot(self):
+        print("Getting value for with_shap_summary_plot")
+        return self._with_shap_summary_plot
+
+    @with_shap_summary_plot.setter
+    def with_shap_summary_plot(self, value):
+        print("Setting value for with_shap_summary_plot")
+        self._with_shap_summary_plot = value
+
+    @property
+    def with_shap_interaction_plot(self):
+        print("Getting value for with_shap_interaction_plot")
+        return self._with_shap_interaction_plot
+
+    @with_shap_interaction_plot.setter
+    def with_shap_interaction_plot(self, value):
+        print("Setting value for with_shap_interaction_plot")
+        self._with_shap_interaction_plot = value
+
+    @property
+    def with_stratified(self):
+        print("Getting value for with_stratified")
+        return self._with_stratified
+
+    @with_stratified.setter
+    def with_stratified(self, value):
+        print("Setting value for with_stratified")
+        self._with_stratified = value
+
+    @property
+    def verbose(self):
+        print("Getting value for verbose")
+        return self._verbose
+
+    @verbose.setter
+    def verbose(self, value):
+        print("Setting value for verbose")
+        self._verbose = value
+
+    @property
+    def random_state(self):
+        print("Getting value for random_state")
+        return self._random_state
+
+    @random_state.setter
+    def random_state(self, value):
+        print("Setting value for random_state")
+        self._random_state = value
+
+    @property
+    def n_jobs(self):
+        print("Getting value for n_jobs")
+        return self._n_jobs
+
+    @n_jobs.setter
+    def n_jobs(self, value):
+        print("Setting value for n_jobs")
+        self._n_jobs = value
+
+    @property
+    def n_iter(self):
+        print("Getting value for n_iter")
+        return self._n_iter
+
+    @n_iter.setter
+    def n_iter(self, value):
+        print("Setting value for n_iter")
+        self._n_iter = value
+
+    @property
+    def eval_metric(self):
+        print("Getting value for eval_metric")
+        return self._eval_metric
+
+    @eval_metric.setter
+    def eval_metric(self, value):
+        print("Setting value for eval_metric")
+        self._eval_metric = value
+
+    @property
+    def number_of_trials(self):
+        print("Getting value for number_of_trials")
+        return self._number_of_trials
+
+    @number_of_trials.setter
+    def number_of_trials(self, value):
+        print("Setting value for number_of_trials")
+        self._number_of_trials = value
+
+    @property
+    def sampler(self):
+        print("Getting value for sampler")
+        return self._sampler
+
+    @sampler.setter
+    def sampler(self, value):
+        print("Setting value for sampler")
+        self._sampler = value
+
+    @property
+    def pruner(self):
+        print("Getting value for pruner")
+        return self._pruner
+
+    @pruner.setter
+    def pruner(self, value):
+        print("Setting value for pruner")
+        self._pruner = value
+
+    @property
+    def importance_df(self):
+        print("Getting value for importance_df")
+        return self._importance_df
+
+    @importance_df.setter
+    def importance_df(self, value):
+        print("Setting value for importance_df")
+        self._importance_df = value
+
+    @property
+    def best_estimator(self):
+        print("Getting value for best_estimator")
+        return self._best_estimator
+
+    @best_estimator.setter
+    def best_estimator(self, value):
+        print("Setting value for best_estimator")
+        self._best_estimator = value
+
+    def fit(self, X, y):
+
+        self.cols = X.columns
+        if self.hyper_parameter_optimization_method.lower() == "grid":
+            self.best_estimator = _calc_best_estimator_grid_search(
+                X,
+                y,
+                self.estimator,
+                self.estimator_params,
+                self.measure_of_accuracy,
+                self.verbose,
+                self.n_jobs,
+                self.cv,
+            )
+        if self.hyper_parameter_optimization_method.lower() == "random":
+            self.best_estimator = _calc_best_estimator_random_search(
+                X,
+                y,
+                self.estimator,
+                self.estimator_params,
+                self.measure_of_accuracy,
+                self.verbose,
+                self.n_jobs,
+                self.n_iter,
+                self.cv,
+            )
+        if self.hyper_parameter_optimization_method.lower() == "optuna":
+            self.best_estimator = _calc_best_estimator_optuna_univariate(
+                X,
+                y,
+                self.estimator,
+                self.measure_of_accuracy,
+                self.estimator_params,
+                self.verbose,
+                self.test_size,
+                self.random_state,
+                self.eval_metric,
+                self.number_of_trials,
+                self.sampler,
+                self.pruner,
+                self.with_stratified,
+            )
+
+        shap_explainer = fasttreeshap.TreeExplainer(
+            self.best_estimator, algorithm=self.shap_version, n_jobs=self.n_jobs
+        )
+        shap_values_v0 = shap_explainer(X).values
+        shap_values_v0.shape
+        shap_values = shap_explainer.shap_values(X)
+        print(shap_values)
+        shap_sum = np.abs(shap_values).mean(axis=0)
+        self.importance_df = pd.DataFrame([X.columns.tolist(), shap_sum.tolist()]).T
+        self.importance_df.columns = ["column_name", "shap_importance"]
+        self.importance_df = self.importance_df.sort_values(
+            "shap_importance", ascending=False
+        )
+        print(self.importance_df[0 : self.n_features])
+        num_feat = min([self.n_features, self.importance_df.shape[0]])
+        self.selected_cols = self.importance_df["column_name"][0:num_feat].to_list()
+        shap.summary_plot(shap_values, X, max_display=self.n_features)
+
+        return self
+
+    def transform(self, X):
+
+        return X[self.selected_cols]
diff --git a/zoish/notebooks/__init__.py b/zoish/notebooks/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/zoish/utils/__init__.py b/zoish/utils/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/zoish/utils/helper_funcs.py b/zoish/utils/helper_funcs.py
new file mode 100644
index 0000000..98f7ec8
--- /dev/null
+++ b/zoish/utils/helper_funcs.py
@@ -0,0 +1,427 @@
+import numpy as np
+import optuna
+import xgboost
+from sklearn.metrics import (accuracy_score, confusion_matrix,
+                             explained_variance_score, f1_score, make_scorer,
+                             max_error, mean_absolute_error,
+                             mean_absolute_percentage_error,
+                             mean_squared_error, median_absolute_error,
+                             precision_score, r2_score, recall_score,
+                             roc_auc_score)
+from sklearn.model_selection import (GridSearchCV, RandomizedSearchCV,
+                                     StratifiedKFold, train_test_split)
+
+
+def _trail_param_retrive(trial, dict, keyword):
+    """Function for calculating best estimator
+    Parameters
+    ----------
+    estimator : estimator instance
+        An unfitted estimator.
+    cv : object, cross-validation object, default=None
+    Attributes
+    ----------
+    n_features_in_ : int
+    Number of features seen during :term:`fit`. Only defined if the
+    underlying estimator exposes such an attribute when fit.
+    .. versionadded:: 0.24
+
+    """
+    if keyword == "max_depth":
+        return trial.suggest_int(
+            keyword, min(dict[keyword]), max(dict[keyword]), log=True
+        )
+    else:
+        return trial.suggest_float(
+            keyword, min(dict[keyword]), max(dict[keyword]), log=True
+        )
+
+
+def calc_metric_for_multi_outputs_classification(label, valid_y, preds, SCORE_TYPE):
+    """Function for calculating best estimator
+    Parameters
+    ----------
+    estimator : estimator instance
+        An unfitted estimator.
+    cv : object, cross-validation object, default=None
+    Attributes
+    ----------
+    n_features_in_ : int
+    Number of features seen during :term:`fit`. Only defined if the
+    underlying estimator exposes such an attribute when fit.
+    .. versionadded:: 0.24
+
+    """
+
+    sum_errors = 0
+
+    for i, l in enumerate(label):
+        f1 = f1_score(valid_y[l], preds[:, i])
+        acc = accuracy_score(valid_y[l], preds[:, i])
+        pr = precision_score(valid_y[l], preds[:, i])
+        recall = recall_score(valid_y[l], preds[:, i])
+        roc = roc_auc_score(valid_y[l], preds[:, i])
+        tn, fp, fn, tp = confusion_matrix(
+            valid_y[l], preds[:, i], labels=[0, 1]
+        ).ravel()
+
+        if SCORE_TYPE == "f1" or SCORE_TYPE == "f1_score":
+            sum_errors = sum_errors + f1
+        if (
+            SCORE_TYPE == "acc"
+            or SCORE_TYPE == "accuracy_score"
+            or SCORE_TYPE == "accuracy"
+        ):
+            sum_errors = sum_errors + acc
+        if (
+            SCORE_TYPE == "pr"
+            or SCORE_TYPE == "precision_score"
+            or SCORE_TYPE == "precision"
+        ):
+            sum_errors = sum_errors + pr
+        if (
+            SCORE_TYPE == "recall"
+            or SCORE_TYPE == "recall_score"
+            or SCORE_TYPE == "recall"
+        ):
+            sum_errors = sum_errors + recall
+        if (
+            SCORE_TYPE == "roc"
+            or SCORE_TYPE == "roc_auc_score"
+            or SCORE_TYPE == "roc_auc"
+        ):
+            sum_errors = sum_errors + roc
+
+        # other metrics - not often use
+
+        if SCORE_TYPE == "tp" or SCORE_TYPE == "true possitive":
+            sum_errors = sum_errors + tp
+        if SCORE_TYPE == "tn" or SCORE_TYPE == "true negative":
+            sum_errors = sum_errors + tn
+
+    return sum_errors
+
+
+def _calc_metric_for_single_output_classification(valid_y, pred_labels, SCORE_TYPE):
+    """Function for calculating best estimator
+    Parameters
+    ----------
+    estimator : estimator instance
+        An unfitted estimator.
+    cv : object, cross-validation object, default=None
+    Attributes
+    ----------
+    n_features_in_ : int
+    Number of features seen during :term:`fit`. Only defined if the
+    underlying estimator exposes such an attribute when fit.
+    .. versionadded:: 0.24
+
+    """
+
+    sum_errors = 0
+    f1 = f1_score(valid_y, pred_labels)
+    acc = accuracy_score(valid_y, pred_labels)
+    pr = precision_score(valid_y, pred_labels)
+    recall = recall_score(valid_y, pred_labels)
+    roc = roc_auc_score(valid_y, pred_labels)
+
+    tn, _, _, tp = confusion_matrix(valid_y, pred_labels, labels=[0, 1]).ravel()
+    if SCORE_TYPE == "f1" or SCORE_TYPE == "f1_score":
+        sum_errors = sum_errors + f1
+    if (
+        SCORE_TYPE == "acc"
+        or SCORE_TYPE == "accuracy_score"
+        or SCORE_TYPE == "accuracy"
+    ):
+        sum_errors = sum_errors + acc
+    if (
+        SCORE_TYPE == "pr"
+        or SCORE_TYPE == "precision_score"
+        or SCORE_TYPE == "precision"
+    ):
+        sum_errors = sum_errors + pr
+    if SCORE_TYPE == "recall" or SCORE_TYPE == "recall_score" or SCORE_TYPE == "recall":
+        sum_errors = sum_errors + recall
+    if SCORE_TYPE == "roc" or SCORE_TYPE == "roc_auc_score" or SCORE_TYPE == "roc_auc":
+        sum_errors = sum_errors + roc
+
+    # other metrics - not often use
+
+    if SCORE_TYPE == "tp" or SCORE_TYPE == "true possitive":
+        sum_errors = sum_errors + tp
+    if SCORE_TYPE == "tn" or SCORE_TYPE == "true negative":
+        sum_errors = sum_errors + tn
+
+    return sum_errors
+
+
+def _calc_metric_for_single_output_regression(valid_y, pred_labels, SCORE_TYPE):
+    """Function for calculating best estimator
+    Parameters
+    ----------
+    estimator : estimator instance
+        An unfitted estimator.
+    cv : object, cross-validation object, default=None
+    Attributes
+    ----------
+    n_features_in_ : int
+    Number of features seen during :term:`fit`. Only defined if the
+    underlying estimator exposes such an attribute when fit.
+    .. versionadded:: 0.24
+
+    """
+
+    r2 = r2_score(valid_y, pred_labels)
+    explained_variance_score_sr = explained_variance_score(valid_y, pred_labels)
+
+    max_error_err = max_error(valid_y, pred_labels)
+    mean_absolute_error_err = mean_absolute_error(valid_y, pred_labels)
+    mean_squared_error_err = mean_squared_error(valid_y, pred_labels)
+    median_absolute_error_err = median_absolute_error(valid_y, pred_labels)
+    mean_absolute_percentage_error_err = mean_absolute_percentage_error(
+        valid_y, pred_labels
+    )
+
+    if SCORE_TYPE == "r2" or SCORE_TYPE == "r2_score":
+        return r2
+    if SCORE_TYPE == "explained_variance_score":
+        return explained_variance_score_sr
+
+    if SCORE_TYPE == "max_error":
+        return max_error_err
+    if SCORE_TYPE == "mean_absolute_error":
+        return mean_absolute_error_err
+    if SCORE_TYPE == "mean_squared_error":
+        return mean_squared_error_err
+    if SCORE_TYPE == "median_absolute_error":
+        return median_absolute_error_err
+    if SCORE_TYPE == "mean_absolute_percentage_error":
+        return mean_absolute_percentage_error_err
+
+
+def _calc_best_estimator_grid_search(
+    X, y, estimator, estimator_params, measure_of_accuracy, verbose, n_jobs, cv
+):
+    """Function for calculating best estimator
+    Parameters
+    ----------
+    estimator : estimator instance
+        An unfitted estimator.
+    cv : object, cross-validation object, default=None
+    Attributes
+    ----------
+    n_features_in_ : int
+    Number of features seen during :term:`fit`. Only defined if the
+    underlying estimator exposes such an attribute when fit.
+    .. versionadded:: 0.24
+
+    """
+    grid_search = GridSearchCV(
+        estimator,
+        param_grid=estimator_params,
+        cv=cv,
+        n_jobs=n_jobs,
+        scoring=make_scorer(measure_of_accuracy),
+        verbose=verbose,
+    )
+    grid_search.fit(X, y)
+    best_estimator = grid_search.best_estimator_
+    return best_estimator
+
+
+def _calc_best_estimator_random_search(
+    X, y, estimator, estimator_params, measure_of_accuracy, verbose, n_jobs, n_iter, cv
+):
+    """Function for calculating best estimator
+    Parameters
+    ----------
+    estimator : estimator instance
+        An unfitted estimator.
+    cv : object, cross-validation object, default=None
+    Attributes
+    ----------
+    n_features_in_ : int
+    Number of features seen during :term:`fit`. Only defined if the
+    underlying estimator exposes such an attribute when fit.
+
+    """
+    random_search = RandomizedSearchCV(
+        estimator,
+        param_distributions=estimator_params,
+        cv=cv,
+        n_iter=n_iter,
+        n_jobs=n_jobs,
+        scoring=make_scorer(measure_of_accuracy),
+        verbose=verbose,
+    )
+    random_search.fit(X, y)
+    best_estimator = random_search.best_estimator_
+    return best_estimator
+
+
+def _calc_best_estimator_optuna_univariate(
+    X,
+    y,
+    estimator,
+    measure_of_accuracy,
+    estimator_params,
+    verbose,
+    test_size,
+    random_state,
+    eval_metric,
+    number_of_trials,
+    sampler,
+    pruner,
+    with_stratified,
+):
+    """Function for calculating best estimator
+    Parameters
+    ----------
+    estimator : estimator instance
+        An unfitted estimator.
+    cv : object, cross-validation object, default=None
+    Attributes
+    ----------
+    n_features_in_ : int
+    Number of features seen during :term:`fit`. Only defined if the
+    underlying estimator exposes such an attribute when fit.
+    .. versionadded:: 0.24
+
+    """
+    if estimator.__class__.__name__ == "XGBClassifier" and with_stratified:
+        train_x, valid_x, train_y, valid_y = train_test_split(
+            X, y, stratify=y[y.columns.to_list()[0]], test_size=test_size
+        )
+    if estimator.__class__.__name__ == "XGBClassifier" and not with_stratified:
+        train_x, valid_x, train_y, valid_y = train_test_split(
+            X, y, test_size=test_size, random_state=random_state
+        )
+    if estimator.__class__.__name__ == "XGBRegressor":
+        train_x, valid_x, train_y, valid_y = train_test_split(
+            X, y, test_size=test_size, random_state=random_state
+        )
+
+    def objective(trial):
+
+        dtrain = xgboost.DMatrix(train_x, label=train_y)
+        dvalid = xgboost.DMatrix(valid_x, label=valid_y)
+
+        if (
+            estimator.__class__.__name__ == "XGBClassifier"
+            or estimator.__class__.__name__ == "XGBRegressor"
+        ):
+            param = {}
+            param["verbosity"] = verbose
+            param["eval_metric"] = eval_metric
+            param["booster"] = trial.suggest_categorical("booster", ["gbtree"])
+            if (
+                valid_y.iloc[:, 0].nunique() <= 2
+                and estimator.__class__.__name__ == "XGBClassifier"
+            ):
+                param["objective"] = "binary:logistic"
+            if estimator.__class__.__name__ == "XGBRegressor":
+                param["objective"] = "reg:squarederror"
+
+            if "lambda" in estimator_params.keys():
+                param["lambda"] = _trail_param_retrive(
+                    trial, estimator_params, "lambda"
+                )
+            # L1 regularization weight.
+            if "alpha" in estimator_params.keys():
+                param["alpha"] = _trail_param_retrive(trial, estimator_params, "alpha")
+            # sampling ratio for training data.
+            if "subsample" in estimator_params.keys():
+                param["subsample"] = _trail_param_retrive(
+                    trial, estimator_params, "subsample"
+                )
+            # sampling according to each tree.
+            if "colsample_bytree" in estimator_params.keys():
+                param["colsample_bytree"] = _trail_param_retrive(
+                    trial, estimator_params, "colsample_bytree"
+                )
+            if estimator.__class__.__name__ == "XGBClassifier":
+                if "scale_pos_weight" in estimator_params.keys():
+                    param["scale_pos_weight"] = _trail_param_retrive(
+                        trial, estimator_params, "scale_pos_weight"
+                    )
+            if "booster" in estimator_params.keys():
+                if param["booster"] in ["gbtree", "dart"]:
+                    # maximum depth of the tree, signifies complexity of the tree.
+                    if "max_depth" in estimator_params.keys():
+                        param["max_depth"] = _trail_param_retrive(
+                            trial, estimator_params, "max_depth"
+                        )
+                    if "min_child_weight" in estimator_params.keys():
+                        # minimum child weight, larger the term more conservative the tree.
+                        param["min_child_weight"] = _trail_param_retrive(
+                            trial, estimator_params, "min_child_weight"
+                        )
+                    if "eta" in estimator_params.keys():
+                        param["eta"] = _trail_param_retrive(
+                            trial, estimator_params, "eta"
+                        )
+                    if "gamma" in estimator_params.keys():
+                        # defines how selective algorithm is.
+                        param["gamma"] = _trail_param_retrive(
+                            trial, estimator_params, "gamma"
+                        )
+                    # if "grow_policy" in estimator_params.keys():
+                    #    param["grow_policy"] = _trail_param_retrive(
+                    #        trial, estimator_params, "grow_policy"
+                    #    )
+            if "booster" in estimator_params.keys():
+                if param["booster"] == "dart":
+                    if "sample_type" in estimator_params.keys():
+                        param["sample_type"] = _trail_param_retrive(
+                            trial, estimator_params, "sample_type"
+                        )
+                    if "normalize_type" in estimator_params.keys():
+                        param["normalize_type"] = _trail_param_retrive(
+                            trial, estimator_params, "normalize_type"
+                        )
+                    if "rate_drop" in estimator_params.keys():
+                        param["rate_drop"] = _trail_param_retrive(
+                            trial, estimator_params, "rate_drop"
+                        )
+                    if "skip_drop" in estimator_params.keys():
+                        param["skip_drop"] = _trail_param_retrive(
+                            trial, estimator_params, "skip_drop"
+                        )
+            # Add a callback for pruning.
+            pruning_callback = optuna.integration.XGBoostPruningCallback(
+                trial, "validation-" + eval_metric
+            )
+            est = xgboost.train(
+                param,
+                dtrain,
+                evals=[(dvalid, "validation")],
+                callbacks=[pruning_callback],
+            )
+            preds = est.predict(dvalid)
+            pred_labels = np.rint(preds)
+
+            if "classifier" in estimator.__class__.__name__.lower():
+                accr = _calc_metric_for_single_output_classification(
+                    valid_y, pred_labels, measure_of_accuracy
+                )
+            if "regressor" in estimator.__class__.__name__.lower():
+                accr = _calc_metric_for_single_output_regression(
+                    valid_y, pred_labels, measure_of_accuracy
+                )
+
+        return accr
+
+    study = optuna.create_study(direction="maximize", sampler=sampler, pruner=pruner)
+    study.optimize(objective, n_trials=number_of_trials, timeout=600)
+    trial = study.best_trial
+    dtrain = xgboost.DMatrix(train_x, label=train_y)
+    dvalid = xgboost.DMatrix(valid_x, label=valid_y)
+    print(trial.params)
+
+    best_estimator = xgboost.train(
+        trial.params,
+        dtrain,
+        evals=[(dvalid, "validation")],
+    )
+
+    return best_estimator
diff --git a/zoish/version.py b/zoish/version.py
new file mode 100644
index 0000000..3dc1f76
--- /dev/null
+++ b/zoish/version.py
@@ -0,0 +1,1 @@
+__version__ = "0.1.0"


Test patch:
diff --git a/tests/__pycache__/__init__.cpython-310.pyc b/tests/__pycache__/__init__.cpython-310.pyc
deleted file mode 100644
index f22e3eb..0000000
Binary files a/tests/__pycache__/__init__.cpython-310.pyc and /dev/null differ
diff --git a/tests/__pycache__/test_zoish.cpython-310-pytest-7.1.2.pyc b/tests/__pycache__/test_zoish.cpython-310-pytest-7.1.2.pyc
deleted file mode 100644
index b91245a..0000000
Binary files a/tests/__pycache__/test_zoish.cpython-310-pytest-7.1.2.pyc and /dev/null differ
diff --git a/tests/test_zoish.py b/tests/test_zoish.py
index ede5b09..7d80bd9 100644
--- a/tests/test_zoish.py
+++ b/tests/test_zoish.py
@@ -2,4 +2,4 @@
 
 
 def test_version():
-    assert __version__ == '0.1.0'
+    assert __version__ == '0.1.3'
diff --git a/tests/test_zoish_feature_selector.py b/tests/test_zoish_feature_selector.py
new file mode 100644
index 0000000..34e5a9c
--- /dev/null
+++ b/tests/test_zoish_feature_selector.py
@@ -0,0 +1,92 @@
+import numpy as np
+import pandas as pd
+import xgboost
+from pathlib import Path
+from optuna.pruners import HyperbandPruner
+from optuna.samplers._tpe.sampler import TPESampler
+from sklearn.model_selection import KFold, train_test_split
+from ..zoish.feature_selectors.zoish_feature_selector import ScallyShapFeatureSelector
+
+
+
+def test_scally_feature_selector():
+    """Test feature scally selector add"""
+
+    SFC_OPTUNA = ScallyShapFeatureSelector(
+        n_features=4,
+        estimator=xgboost.XGBClassifier(),
+        estimator_params={
+            "max_depth": [4, 5],
+            "min_child_weight": [0.1, 0.9],
+            "gamma": [1, 9],
+            "booster": ["gbtree", "dart"],
+        },
+        hyper_parameter_optimization_method="optuna",
+        shap_version="v0",
+        measure_of_accuracy="f1",
+        list_of_obligatory_features=[],
+        test_size=0.33,
+        cv=KFold(n_splits=3, random_state=42, shuffle=True),
+        with_shap_summary_plot=False,
+        with_shap_interaction_plot=False,
+        with_stratified=True,
+        verbose=3,
+        random_state=42,
+        n_jobs=-1,
+        n_iter=100,
+        eval_metric="auc",
+        number_of_trials=10,
+        sampler=TPESampler(),
+        pruner=HyperbandPruner(),
+    )
+
+    SFC_GRID = ScallyShapFeatureSelector(
+        n_features=4,
+        estimator=xgboost.XGBClassifier(),
+        estimator_params={
+            "max_depth": [4, 5],
+            "min_child_weight": [0.1, 0.9],
+            "gamma": [1, 9],
+            "booster": ["gbtree", "dart"],
+        },
+        hyper_parameter_optimization_method="grid",
+        shap_version="v0",
+        measure_of_accuracy="f1",
+        list_of_obligatory_features=[],
+        test_size=0.33,
+        cv=KFold(n_splits=3,random_state=42,shuffle=True),
+        with_shap_summary_plot=False,
+        with_shap_interaction_plot=False,
+        with_stratified=True,
+        verbose=3,
+        random_state=42,
+        n_jobs=-1,
+        n_iter=100,
+        eval_metric="auc",
+        number_of_trials=10,
+        sampler=TPESampler(),
+        pruner=HyperbandPruner(),
+
+    )
+
+    try:
+        ROOT_PROJECT = Path(__file__).parent.parent
+        data = pd.read_csv(ROOT_PROJECT / "src"  / "scallyshap" / "data" / "data.csv")
+    except:
+        data = pd.read_csv("/home/circleci/project/data/data.csv")
+    print(data.columns.to_list())
+
+    X = data.loc[:, data.columns != "default payment next month"]
+    y = data.loc[:, data.columns == "default payment next month"]
+
+    X_train, X_test, y_train, y_test = train_test_split(
+        X, y, test_size=0.33, random_state=0
+    )
+
+    SFC_OPTUNA.fit_transform(X_train, y_train)
+    XT_OPTUNA = SFC_OPTUNA.transform(X_test)
+
+    SFC_GRID.fit_transform(X_train, y_train)
+    XT_GRID = SFC_GRID.transform(X_test)
+    assert len(XT_GRID.columns.to_list())==4#['PAY_0', 'LIMIT_BAL', 'PAY_AMT2', 'BILL_AMT1']
+    assert len(XT_OPTUNA.columns.to_list()) == 4