changeset 0:462b5bc27acd draft default tip

planemo upload for repository https://github.com/cleanlab/cleanlab commit ac4753a61ee908bc2a5953b6c6d38d2bbbacc6c0
author bgruening
date Wed, 28 May 2025 11:30:50 +0000
parents
children
files cleanlab_issue_handler.py cleanlab_issue_handler.xml test-data/breast_cancer.csv test-data/breast_cancer.tsv test-data/reg_1027_ESL.csv
diffstat 5 files changed, 1560 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cleanlab_issue_handler.py	Wed May 28 11:30:50 2025 +0000
@@ -0,0 +1,187 @@
+import argparse
+
+import numpy as np
+import pandas as pd
+from cleanlab.datalab.datalab import Datalab
+from cleanlab.regression.rank import get_label_quality_scores
+from sklearn.linear_model import LinearRegression
+from sklearn.model_selection import cross_val_predict, KFold, StratifiedKFold
+from xgboost import XGBClassifier
+
+# -------------------
+# Issue Handler
+# -------------------
+
+
+class IssueHandler:
+    def __init__(self, dataset, task, target_column, n_splits=3, quality_threshold=0.2):
+        self.dataset = dataset
+        self.task = task
+        self.target_column = target_column
+        self.n_splits = n_splits
+        self.quality_threshold = quality_threshold
+        self.issues = None
+        self.features = self.dataset.drop(target_column, axis=1).columns.tolist()
+        self.issue_summary = None
+        self.pred_probs = None
+
+    def report_issues(self):
+        X = self.dataset.drop(self.target_column, axis=1)
+        y = self.dataset[self.target_column]
+
+        # Ensure compatibility with Galaxy
+        X = X.to_numpy() if hasattr(X, 'to_numpy') else np.asarray(X)
+        y = y.to_numpy() if hasattr(y, 'to_numpy') else np.asarray(y)
+
+        if self.task == 'classification':
+            model = XGBClassifier(use_label_encoder=False, eval_metric='logloss', random_state=42)
+            cv = StratifiedKFold(n_splits=self.n_splits, shuffle=True, random_state=42)
+            self.pred_probs = cross_val_predict(model, X, y, cv=cv, method='predict_proba')
+
+            lab = Datalab(self.dataset, label_name=self.target_column)
+            lab.find_issues(pred_probs=self.pred_probs)
+            self.issues = lab.get_issues()
+            self.issue_summary = lab.get_issue_summary()
+            print(self.issue_summary)
+
+        elif self.task == 'regression':
+            model = LinearRegression()
+            cv = KFold(n_splits=self.n_splits, shuffle=True, random_state=42)
+            pred_y = cross_val_predict(model, X, y, cv=cv, method='predict')
+            scores = get_label_quality_scores(y, pred_y, method='residual')
+            is_low_quality = scores < self.quality_threshold
+            self.issues = pd.DataFrame({
+                'label_quality': scores,
+                'is_low_quality': is_low_quality
+            })
+            self.issue_summary = {
+                'quality_threshold': self.quality_threshold,
+                'num_low_quality': int(is_low_quality.sum()),
+                'mean_label_quality': float(np.mean(scores)),
+                'median_label_quality': float(np.median(scores)),
+                'min_label_quality': float(np.min(scores)),
+                'max_label_quality': float(np.max(scores)),
+            }
+            print("Regression Issue Summary:")
+            for k, v in self.issue_summary.items():
+                print(f"{k.replace('_', ' ').capitalize()}: {v:.4f}" if isinstance(v, float) else f"{k.replace('_', ' ').capitalize()}: {v}")
+
+        return self.dataset.copy(), self.issues.copy(), self.issue_summary
+
+    def clean_selected_issues(self, method='remove', label_issues=True, outliers=True, near_duplicates=True, non_iid=True):
+        if self.issues is None:
+            raise RuntimeError("Must run report_issues() before cleaning.")
+
+        if self.task == 'regression':
+            clean_mask = self.issues['is_low_quality'].fillna(False)
+        else:
+            clean_mask = pd.Series([False] * len(self.dataset))
+            for issue_type, use_flag in [
+                ('is_label_issue', label_issues),
+                ('is_outlier_issue', outliers),
+                ('is_near_duplicate_issue', near_duplicates),
+                ('is_non_iid_issue', non_iid)
+            ]:
+                if use_flag and issue_type in self.issues.columns:
+                    clean_mask |= self.issues[issue_type].fillna(False)
+
+        if method == 'remove':
+            return self.dataset[~clean_mask].copy()
+
+        elif method == 'replace' and self.task == 'classification':
+            most_likely = np.argmax(self.pred_probs, axis=1)
+            fixed = self.dataset.copy()
+            to_fix = self.issues['is_label_issue'] & label_issues
+            fixed.loc[to_fix, self.target_column] = most_likely[to_fix]
+            return fixed
+
+        elif method == 'replace' and self.task == 'regression':
+            raise NotImplementedError("Replace method not implemented for regression label correction.")
+
+        else:
+            raise ValueError("Invalid method or unsupported combination.")
+
+# -------------------
+# Main CLI Entry
+# -------------------
+
+
+def main():
+    parser = argparse.ArgumentParser(description="Cleanlab Issue Handler CLI")
+    parser.add_argument("--input_file", nargs=2, required=True, metavar=('FILE', 'EXT'), help="Input file path and its extension")
+    parser.add_argument("--task", required=True, choices=["classification", "regression"], help="Type of ML task")
+    parser.add_argument("--target_column", default="target", help="Name of the target column")
+    parser.add_argument("--method", default="remove", choices=["remove", "replace"], help="Cleaning method")
+    parser.add_argument("--summary", action="store_true", help="Print and save issue summary only, no cleaning")
+    parser.add_argument("--no-label-issues", action="store_true", help="Exclude label issues from cleaning")
+    parser.add_argument("--no-outliers", action="store_true", help="Exclude outlier issues from cleaning")
+    parser.add_argument("--no-near-duplicates", action="store_true", help="Exclude near-duplicate issues from cleaning")
+    parser.add_argument("--no-non-iid", action="store_true", help="Exclude non-i.i.d. issues from cleaning")
+    parser.add_argument('--quality-threshold', type=float, default=0.2, help='Threshold for low-quality labels (regression only)')
+
+    args = parser.parse_args()
+
+    # Load dataset based on file extension
+    file_path, file_ext = args.input_file
+    file_ext = file_ext.lower()
+
+    print(f"Loading dataset from: {file_path} with extension: {file_ext}")
+
+    if file_ext == "csv":
+        df = pd.read_csv(file_path)
+    elif file_ext in ["tsv", "tabular"]:
+        df = pd.read_csv(file_path, sep="\t")
+    else:
+        raise ValueError(f"Unsupported file format: {file_ext}")
+
+    # Run IssueHandler
+    handler = IssueHandler(dataset=df,
+                           task=args.task,
+                           target_column=args.target_column,
+                           quality_threshold=args.quality_threshold)
+    _, issues, summary = handler.report_issues()
+
+    # Save summary
+    if summary is not None:
+        with open("summary.txt", "w") as f:
+            if args.task == "regression":
+                f.write("Regression Issue Summary:\n")
+                for k, v in summary.items():
+                    text = f"{k.replace('_', ' ').capitalize()}: {v:.4f}" if isinstance(v, float) else f"{k.replace('_', ' ').capitalize()}: {v}"
+                    f.write(text + "\n")
+            else:
+                f.write(str(summary))
+        print("Issue summary saved to: summary.txt")
+
+    if args.summary:
+        return
+
+    # Clean selected issues
+    cleaned_df = handler.clean_selected_issues(
+        method=args.method,
+        label_issues=not args.no_label_issues,
+        outliers=not args.no_outliers,
+        near_duplicates=not args.no_near_duplicates,
+        non_iid=not args.no_non_iid
+    )
+
+    print(f"Cleaned dataset shape: {cleaned_df.shape}")
+    print(f"Original dataset shape: {df.shape}")
+
+    output_filename = "cleaned_data"
+    if file_ext == "csv":
+        cleaned_df.to_csv(output_filename, index=False)
+    elif file_ext in ["tsv", "tabular"]:
+        cleaned_df.to_csv(output_filename, sep="\t", index=False)
+    else:
+        raise ValueError(f"Unsupported output format: {file_ext}")
+
+    print(f"Cleaned dataset saved to: {output_filename}")
+
+# -------------------
+# Entry point
+# -------------------
+
+
+if __name__ == "__main__":
+    main()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cleanlab_issue_handler.xml	Wed May 28 11:30:50 2025 +0000
@@ -0,0 +1,310 @@
+<tool id="cleanlab_issue_handler" name="Cleanlab Issue Handler" version="@TOOL_VERSION@+galaxy@VERSION_SUFFIX@" profile="23.0">
+    <description>Detect and optionally clean data issues using Cleanlab</description>
+    <macros>
+        <token name="@TOOL_VERSION@">2.7.1</token>
+        <token name="@VERSION_SUFFIX@">1.0</token>
+    </macros>
+
+    <requirements>
+        <requirement type="package" version="2.7.1">cleanlab</requirement>
+        <requirement type="package" version="3.6.0">datasets</requirement>
+        <requirement type="package" version="3.0.0">xgboost</requirement>
+    </requirements>
+
+    <command detect_errors="exit_code"><![CDATA[
+        python '${__tool_directory__}/cleanlab_issue_handler.py'
+        --input_file '$input_file' '$input_file.ext'
+        --target_column '$target_column'
+        --task "$task_block.task"
+        --method "$task_block.method"
+        #if $summary_only:
+            --summary
+        #end if
+        #if str($task_block.task) == "classification":
+            $task_block.label_issues
+            $task_block.outliers
+            $task_block.near_duplicates
+            $task_block.non_iid
+        #elif str($task_block.task) == "regression":
+            --quality-threshold "$task_block.quality_threshold"
+        #end if
+    ]]></command>
+
+    <inputs>
+        <param name="input_file" type="data" format="csv,tsv,tabular" label="Input data file"/>
+        <param name="target_column" type="text" optional="false" label="Target column name" value="target" help="Name of the target column in the input data file. Default is 'target'."/>
+        <param name="summary_only" type="boolean" label="Only generate summary report?" checked="false"/>
+        
+        <conditional name="task_block">
+            <param name="task" type="select" label="Task type">
+                <option value="classification">Classification</option>
+                <option value="regression">Regression</option>
+            </param>
+            
+            <when value="classification">
+                <param name="method" type="select" label="Cleaning method">
+                    <option value="remove">Remove problematic rows</option>
+                    <option value="replace">Replace problematic labels (classification only)</option>
+                </param>
+                <param name="label_issues" type="boolean" truevalue="" falsevalue="--no-label-issues" label="Remove/Replace label issues" checked="true"/>
+                <param name="outliers" type="boolean" truevalue="" falsevalue="--no-outliers" label="Remove/Replace outlier issues" checked="true"/>
+                <param name="near_duplicates" type="boolean" truevalue="" falsevalue="--no-near-duplicates" label="Remove/Replace near-duplicate issues" checked="true"/>
+                <param name="non_iid" type="boolean" truevalue="" falsevalue="--no-non-iid" label="Remove/Replace non-IID issues" checked="true"/>
+            </when>
+
+            <when value="regression">
+                <param name="method" type="select" label="Cleaning method">
+                    <option value="remove">Remove problematic rows</option>
+                    <!-- No "replace" option for regression -->
+                </param>
+                <param name="quality_threshold" type="float" label="Quality threshold" value="0.2" min="0.0" max="1.0" help="Threshold for low-quality labels. Default is 0.2."/>
+                <!-- No issue type parameters shown -->
+            </when>
+        </conditional>
+    </inputs>
+
+    <outputs>
+        <data name="report_file" from_work_dir="summary.txt" format="txt" label="Issue Report"/>
+
+        <data name="output_file" from_work_dir="cleaned_data" format_source="input_file" label="cleaned_${input_file.name}">
+            <filter>not summary_only</filter>
+        </data>
+    </outputs>
+
+    <tests>
+    <!-- Test1: only summary -->
+    <test expect_num_outputs="1">
+        <param name="input_file" value="breast_cancer.csv" />
+        <param name="target_column" value="target" />
+        <param name="summary_only" value="true" />
+
+        <conditional name="task_block">
+            <param name="task" value="classification" />
+            <param name="method" value="remove" />
+            <param name="label_issues" value="true" />
+            <param name="outliers" value="true" />
+            <param name="near_duplicates" value="true" />
+            <param name="non_iid" value="true" />
+        </conditional>
+
+        <output name="report_file">
+            <assert_contents>
+                <has_text_matching expression="issue_type\s+score\s+num_issues"/>
+                <has_text_matching expression="label\s+(0(\.\d+)?|1(\.0+)?)\s+\d+"/>
+                <has_text_matching expression="outlier\s+(0(\.\d+)?|1(\.0+)?)\s+\d+"/>
+                <has_text_matching expression="non_iid\s+(0(\.\d+)?|1(\.0+)?)\s+\d+"/>
+            </assert_contents>
+        </output>
+    </test>
+
+    <!-- Test2: summary and cleaned all -->
+    <test expect_num_outputs="2">
+        <param name="input_file" value="breast_cancer.csv" />
+        <param name="target_column" value="target" />
+        <param name="summary_only" value="false" />
+
+        <conditional name="task_block">
+            <param name="task" value="classification" />
+            <param name="method" value="remove" />
+            <param name="label_issues" value="true" />
+            <param name="outliers" value="true" />
+            <param name="near_duplicates" value="true" />
+            <param name="non_iid" value="true" />
+        </conditional>
+
+        <output name="report_file">
+            <assert_contents>
+                <has_text_matching expression="issue_type\s+score\s+num_issues"/>
+                <has_text_matching expression="label\s+(0(\.\d+)?|1(\.0+)?)\s+\d+"/>
+                <has_text_matching expression="outlier\s+(0(\.\d+)?|1(\.0+)?)\s+\d+"/>
+                <has_text_matching expression="non_iid\s+(0(\.\d+)?|1(\.0+)?)\s+\d+"/>
+            </assert_contents>
+        </output>
+
+        <output name="output_file">
+            <assert_contents>
+                <has_text_matching expression=".*target.*"/>
+                <has_text_matching expression="^.*,.+,.+"/>
+            </assert_contents>
+        </output>
+    </test>
+
+    <!-- Test3: summary and cleaned label issues only -->
+    <test expect_num_outputs="2">
+        <param name="input_file" value="breast_cancer.csv" />
+        <param name="target_column" value="target" />
+        <param name="summary_only" value="false" />
+
+        <conditional name="task_block">
+            <param name="task" value="classification" />
+            <param name="method" value="remove" />
+            <param name="label_issues" value="true" />
+            <param name="outliers" value="false" />
+            <param name="near_duplicates" value="false" />
+            <param name="non_iid" value="false" />
+        </conditional>
+
+        <output name="report_file">
+            <assert_contents>
+                <has_text_matching expression="label\s+(0(\.\d+)?|1(\.0+)?)\s+\d+"/>
+            </assert_contents>
+        </output>
+
+        <output name="output_file">
+            <assert_contents>
+                <has_text_matching expression=".*target.*"/>
+                <has_text_matching expression="^.*,.+,.+"/>
+            </assert_contents>
+        </output>
+    </test>
+
+    <!-- Test4: summary and cleaned outliers only -->
+    <test expect_num_outputs="2">
+        <param name="input_file" value="breast_cancer.csv" />
+        <param name="target_column" value="target" />
+        <param name="summary_only" value="false" />
+
+        <conditional name="task_block">
+            <param name="task" value="classification" />
+            <param name="method" value="remove" />
+            <param name="label_issues" value="false" />
+            <param name="outliers" value="true" />
+            <param name="near_duplicates" value="false" />
+            <param name="non_iid" value="false" />
+        </conditional>
+
+        <output name="report_file">
+            <assert_contents>
+                <has_text_matching expression="outlier\s+(0(\.\d+)?|1(\.0+)?)\s+\d+"/>
+            </assert_contents>
+        </output>
+
+        <output name="output_file">
+            <assert_contents>
+                <has_text_matching expression=".*target.*"/>
+                <has_text_matching expression="^.*,.+,.+"/>
+            </assert_contents>
+        </output>
+    </test>
+
+    <!-- Test5: summary and clean all for tsv -->
+    <test expect_num_outputs="2">
+        <param name="input_file" value="breast_cancer.tsv" />
+        <param name="target_column" value="target" />
+        <param name="summary_only" value="false" />
+
+        <conditional name="task_block">
+            <param name="task" value="classification" />
+            <param name="method" value="remove" />
+            <param name="label_issues" value="true" />
+            <param name="outliers" value="true" />
+            <param name="near_duplicates" value="true" />
+            <param name="non_iid" value="true" />
+        </conditional>
+
+        <output name="report_file">
+            <assert_contents>
+                <has_text_matching expression="issue_type\s+score\s+num_issues"/>
+                <has_text_matching expression="label\s+(0(\.\d+)?|1(\.0+)?)\s+\d+"/>
+                <has_text_matching expression="outlier\s+(0(\.\d+)?|1(\.0+)?)\s+\d+"/>
+                <has_text_matching expression="non_iid\s+(0(\.\d+)?|1(\.0+)?)\s+\d+"/>
+            </assert_contents>
+        </output>
+
+        <output name="output_file">
+            <assert_contents>
+                <has_text_matching expression=".*target.*"/>
+                <has_text_matching expression="^.*\t.+\t.+"/>
+            </assert_contents>
+        </output>
+    </test>
+
+    <!-- Test6: regression with summary and cleaned output -->
+    <test expect_num_outputs="2">
+        <param name="input_file" value="reg_1027_ESL.csv" />
+        <param name="target_column" value="target" />
+        <param name="summary_only" value="false" />
+
+        <conditional name="task_block">
+            <param name="task" value="regression" />
+            <param name="method" value="remove" />
+        </conditional>
+
+        <output name="report_file">
+            <assert_contents>
+                <has_text text="Regression Issue Summary:"/>
+                <has_text_matching expression="Num low quality:"/>
+                <has_text_matching expression="Mean label quality:"/>
+            </assert_contents>
+        </output>
+
+        <output name="output_file">
+            <assert_contents>
+                <has_text_matching expression=".*target.*"/>
+                <has_text_matching expression="^.*,.+,.+"/>
+            </assert_contents>
+        </output>
+    </test>
+</tests>
+
+    <help><![CDATA[
+**Cleanlab Issue Handler**
+
+This Galaxy tool identifies and optionally removes or corrects data issues in supervised learning datasets using the [Cleanlab](https://cleanlab.io/) Python library. It supports **classification** and **regression** tasks and helps improve dataset quality by detecting label errors, outliers, near-duplicate entries, and non-IID samples.
+
+The tool internally fits a cross-validated model (e.g., via XGBoost) to estimate label quality and identify problematic samples. These issues can be summarized in a report, and optionally addressed via removal or correction (depending on task and selected method).
+
+--------------------
+
+**Detected Issue Types (with technical examples)**
+
+- **Label Issues**  
+  These are samples whose label in the dataset is likely incorrect.
+  **Example:** In a medical classification dataset, a patient's record is labeled as "benign," but its feature pattern is highly similar to correctly labeled "malignant" cases.
+
+
+- **Outliers**  
+  Points that are statistically distant from the rest of the dataset.  
+  **Example**: An entry with unusually high or low feature values (e.g., several standard deviations away from the mean).
+
+- **Near-Duplicates**  
+  Highly similar or repeated samples.  
+  **Example**: Two rows with nearly identical features and labels — possibly a duplication or copy artifact.
+
+- **Non-IID Samples** *(classification only)*  
+  Samples that violate the assumption of independent and identically distributed data.
+  **Example**: A subset from a different population source (e.g., a different hospital or device) introducing distributional shift.
+
+--------------------
+
+**Parameters**
+
+- **Input file**: Tabular file (CSV/TSV) with a `target` column.
+- **Task type**: `classification` or `regression`.
+- **Method**: `remove` (delete problematic rows) or `replace` (correct labels — classification only).
+- **Only report issues**: If checked, input data is unchanged; only a summary report is produced.
+- **Issue types**: Choose which issues to detect and handle.
+- **Quality threshold** *(regression only)*: A float between 0.0 and 1.0 that determines how aggressively the tool flags low-quality labels in regression. Labels with quality scores below this threshold will be removed.
+
+--------------------
+
+**Outputs**
+
+- **summary.txt**: Report listing each issue type, confidence score, and number of affected rows.
+- **cleaned_data**: Cleaned dataset (CSV/TSV), only produced if "Only report issues" is unchecked.
+
+    ]]></help>
+
+    <citations>
+        <citation type="bibtex">
+@inproceedings{northcutt2021confident,
+  title={Confident learning: Estimating uncertainty in dataset labels},
+  author={Northcutt, Curtis G and Jiang, Lu and Chuang, Alex},
+  booktitle={Journal of Artificial Intelligence Research},
+  year={2021},
+  volume={70},
+  pages={1373--1411}
+}
+        </citation>
+    </citations>
+</tool>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test-data/breast_cancer.csv	Wed May 28 11:30:50 2025 +0000
@@ -0,0 +1,287 @@
+age,menopause,tumor-size,inv-nodes,node-caps,deg-malig,breast,breast-quad,irradiat,target
+2,2,2,0,2,3,1,3,0,1
+3,0,2,0,1,1,1,1,0,0
+3,0,6,0,1,2,0,2,0,1
+2,2,6,0,2,3,1,2,1,0
+2,2,5,4,2,2,0,5,0,1
+3,2,4,4,1,2,1,3,1,0
+3,0,7,0,1,3,0,3,0,0
+2,2,1,0,1,2,0,3,0,0
+2,2,0,0,1,2,1,4,0,0
+2,0,7,2,2,2,1,3,1,0
+3,2,4,0,1,2,0,2,0,0
+4,0,2,0,1,2,1,3,0,0
+3,0,5,0,1,1,1,1,0,0
+3,0,4,0,1,2,1,3,0,0
+2,2,4,0,1,2,0,2,1,1
+1,2,3,0,1,3,0,1,0,0
+3,2,1,4,1,1,1,3,0,0
+4,0,2,0,1,2,1,3,0,0
+3,2,7,0,1,2,0,3,0,0
+3,0,3,0,1,3,0,3,0,0
+3,1,3,0,0,1,0,2,0,1
+4,0,7,4,1,2,1,3,1,0
+3,0,2,0,1,2,1,2,0,0
+2,2,1,0,1,1,1,3,0,0
+1,2,2,5,2,3,0,2,1,1
+3,0,3,4,2,2,1,3,0,0
+3,0,1,0,1,2,1,2,0,0
+2,2,1,0,1,1,1,3,0,0
+4,0,5,4,2,3,0,2,0,0
+2,2,2,2,2,3,0,2,0,1
+4,0,5,0,1,3,1,1,0,1
+4,0,4,4,0,1,1,2,1,0
+3,0,4,0,1,3,0,5,0,0
+3,0,3,0,1,3,1,3,0,0
+2,2,5,0,1,1,0,2,1,1
+1,2,2,0,1,1,0,2,0,0
+2,2,1,0,1,2,1,3,0,0
+4,0,8,5,2,3,0,1,0,0
+2,0,3,0,1,3,0,2,0,0
+2,2,1,0,1,1,1,4,0,0
+1,2,6,0,1,3,0,2,0,1
+2,2,6,6,2,2,1,5,1,0
+4,0,4,0,1,2,1,2,0,0
+3,0,3,4,2,3,1,5,0,1
+1,2,2,0,1,1,0,2,0,0
+3,2,5,0,1,3,0,5,0,1
+4,0,1,0,1,2,1,3,1,0
+2,2,6,0,2,3,1,3,1,0
+3,2,10,0,2,2,1,3,1,0
+3,0,7,0,1,3,1,3,0,0
+5,0,2,6,0,1,0,2,1,1
+3,1,5,0,1,3,1,3,0,0
+2,2,0,0,1,3,0,1,0,0
+5,0,7,0,1,1,1,5,0,0
+2,2,4,0,0,2,0,4,1,0
+3,0,4,2,2,3,1,3,0,0
+3,2,3,0,1,1,0,2,0,0
+3,0,6,2,1,3,0,2,0,0
+3,0,10,0,1,1,1,5,0,0
+1,2,0,0,1,2,1,1,0,1
+3,0,7,5,2,3,0,2,1,1
+2,2,5,0,1,2,1,5,1,0
+2,0,3,0,1,3,0,3,0,0
+2,2,5,2,2,3,0,2,0,1
+2,0,3,0,1,2,1,3,0,1
+3,0,2,0,1,1,1,1,0,0
+1,2,4,0,1,2,1,2,0,0
+4,0,2,0,1,2,0,2,0,0
+3,2,10,6,2,2,1,3,0,1
+1,2,1,0,1,1,1,2,0,0
+3,2,4,4,2,3,0,2,1,1
+4,0,4,4,0,1,1,3,1,0
+4,0,1,0,1,1,1,2,0,0
+3,0,5,5,2,3,0,4,0,1
+1,2,4,5,2,3,0,4,1,1
+3,0,1,0,1,1,0,2,0,0
+3,2,2,0,1,1,0,2,0,0
+2,2,4,0,1,2,1,1,0,0
+2,2,4,0,1,3,0,5,0,1
+4,0,5,5,2,2,1,5,0,0
+3,1,2,0,1,2,0,2,0,0
+2,2,4,0,1,2,1,2,0,0
+2,2,5,0,1,1,1,3,0,0
+4,0,2,0,1,2,0,3,1,0
+1,2,0,0,1,2,1,1,0,0
+3,0,6,0,1,3,0,3,0,0
+2,2,7,0,1,1,1,3,0,0
+1,2,4,5,2,2,1,3,1,0
+3,0,3,0,1,1,1,2,0,0
+3,0,5,0,1,1,0,3,0,0
+4,0,3,0,1,1,1,3,0,1
+1,2,5,4,1,3,1,3,1,1
+3,1,3,0,0,1,0,3,0,1
+3,2,1,0,1,2,1,3,0,0
+3,0,3,0,1,2,1,3,0,0
+2,2,8,0,1,2,0,2,1,0
+1,2,7,0,1,1,0,3,0,1
+3,2,1,0,1,1,0,2,0,0
+4,0,5,0,1,3,1,3,1,1
+2,2,6,0,1,1,1,3,0,1
+2,2,3,4,2,2,0,2,1,1
+3,2,2,0,1,2,0,2,0,1
+3,0,5,0,1,3,1,2,0,0
+4,0,3,0,1,2,0,3,0,0
+2,2,3,0,1,1,0,4,0,0
+4,0,5,4,2,2,0,1,1,1
+4,0,3,4,1,2,0,2,1,1
+3,2,4,0,1,2,0,5,0,1
+3,0,5,0,1,1,1,5,0,0
+2,2,3,0,1,2,0,4,0,0
+4,0,2,0,1,1,1,3,0,0
+4,0,5,0,1,2,0,2,1,0
+1,2,5,0,1,2,0,3,0,0
+1,2,7,4,1,3,1,5,1,0
+4,0,9,0,1,1,0,1,0,0
+4,0,1,0,1,1,0,3,0,0
+2,2,5,5,2,3,1,3,0,1
+4,0,1,0,1,1,0,3,0,0
+2,2,6,6,2,2,1,3,1,0
+2,2,3,0,1,1,1,2,0,0
+2,2,5,0,2,3,1,5,0,1
+3,2,4,0,2,2,0,3,0,0
+2,2,2,0,1,2,0,2,0,0
+1,2,6,6,2,3,0,2,0,1
+1,2,1,0,1,2,0,4,0,0
+3,0,5,0,1,1,1,2,0,0
+4,0,5,0,1,2,0,3,0,0
+4,0,4,0,1,2,0,2,0,0
+2,2,2,0,1,2,0,3,0,1
+4,0,2,0,1,2,1,2,0,0
+2,2,5,0,1,2,0,4,0,0
+0,2,6,0,1,2,1,5,0,0
+2,2,5,0,1,3,1,5,0,1
+2,2,4,0,1,2,1,2,0,1
+1,2,5,0,1,3,0,2,0,0
+1,2,2,0,1,1,1,2,0,1
+3,0,0,0,1,1,1,1,0,0
+3,0,0,0,1,1,0,2,0,0
+4,0,10,0,1,3,1,3,0,1
+3,2,5,0,1,1,0,1,0,0
+4,0,3,3,2,3,0,2,1,1
+2,2,4,0,1,2,0,3,0,0
+2,2,5,4,1,2,1,3,0,1
+3,2,3,4,2,2,0,2,0,0
+3,0,2,0,2,2,0,1,1,0
+3,2,1,0,1,3,0,2,0,0
+1,2,5,6,1,2,1,3,1,1
+4,0,1,0,1,1,0,2,0,0
+2,2,7,0,1,2,1,2,0,0
+3,0,5,6,0,3,0,3,1,0
+2,2,10,0,1,2,1,2,1,1
+3,0,2,0,1,2,1,5,0,0
+3,0,7,4,2,2,0,2,0,0
+1,2,4,4,2,3,0,2,1,1
+4,0,1,0,1,2,0,2,0,0
+4,1,1,0,1,1,0,5,0,0
+1,2,5,0,1,2,0,3,0,1
+1,2,3,4,2,2,0,2,0,1
+3,0,1,0,1,1,1,3,0,0
+4,0,4,0,1,3,1,3,0,0
+3,0,4,4,2,3,1,3,0,0
+2,2,5,5,1,2,0,3,0,0
+4,0,10,0,1,2,0,2,0,0
+3,2,5,0,1,3,0,2,0,0
+2,0,3,4,1,3,1,2,1,1
+3,0,5,5,2,2,0,4,1,1
+4,0,4,4,1,2,1,5,0,1
+2,2,3,0,1,2,0,1,0,0
+2,2,3,0,1,2,0,3,0,0
+2,2,10,0,1,2,0,2,0,0
+3,0,3,0,1,2,1,1,0,1
+3,0,5,4,1,3,1,3,0,1
+2,0,4,0,1,2,0,2,0,0
+3,2,4,0,1,1,1,3,0,1
+2,2,7,4,2,3,1,3,1,0
+2,2,3,0,1,2,1,3,0,0
+2,2,3,4,1,2,1,3,0,0
+2,2,4,6,2,3,1,3,0,1
+2,2,4,0,1,2,1,2,0,1
+2,2,3,0,1,1,1,5,0,0
+1,2,7,0,1,2,1,5,0,0
+4,0,1,5,2,3,0,3,1,1
+2,2,6,0,1,1,0,2,0,0
+3,0,5,4,1,3,0,2,0,1
+2,2,9,0,1,1,0,2,1,0
+4,0,2,0,1,1,0,4,0,0
+2,2,5,0,1,3,1,5,0,0
+2,2,4,0,1,3,0,3,0,1
+3,0,9,0,1,2,1,5,0,0
+3,2,4,0,1,2,1,4,0,0
+3,2,4,0,1,2,0,5,0,1
+2,2,1,0,1,2,0,2,1,0
+4,0,6,5,2,3,0,2,0,1
+4,0,10,0,1,2,1,3,1,0
+2,2,4,0,1,2,1,3,0,0
+1,2,3,4,1,2,1,1,0,0
+1,2,5,0,1,1,1,3,0,1
+4,1,5,0,1,1,0,2,0,0
+2,2,2,1,1,3,1,4,1,0
+4,0,3,0,1,3,1,2,0,1
+1,2,9,0,1,2,0,4,0,0
+2,2,5,0,1,3,0,3,0,0
+4,0,5,0,1,3,0,2,0,0
+2,2,4,0,1,1,1,4,0,0
+2,2,4,0,1,1,0,4,0,0
+4,0,7,4,2,3,1,2,0,1
+3,0,4,0,1,2,0,2,0,0
+3,2,5,0,1,3,1,3,1,1
+2,0,5,4,1,3,0,2,0,1
+2,2,4,0,1,1,1,2,1,0
+2,0,4,1,2,3,0,4,1,1
+2,2,7,0,1,1,0,2,0,1
+2,2,3,0,1,2,0,2,0,0
+3,0,4,0,1,1,0,4,0,0
+2,2,3,0,1,2,1,3,0,0
+5,0,7,0,1,1,1,3,0,0
+4,0,4,0,1,3,0,3,0,1
+3,2,4,0,1,2,0,2,0,0
+4,0,8,0,1,1,1,5,1,1
+3,0,3,0,2,2,1,3,0,0
+3,0,4,0,1,1,0,2,0,0
+3,0,3,0,1,3,0,3,0,0
+2,2,3,4,1,2,1,2,0,0
+3,0,6,0,1,2,0,3,0,0
+1,2,3,0,1,3,0,3,1,1
+4,0,5,0,1,1,1,3,0,0
+4,0,4,0,1,3,1,2,0,0
+2,0,5,0,1,2,0,3,1,0
+1,2,4,0,1,2,0,2,0,0
+2,2,3,0,1,2,0,2,0,1
+1,2,3,0,1,2,0,4,0,0
+2,2,1,0,1,2,1,2,0,0
+3,2,2,0,1,2,1,4,0,0
+3,2,4,0,1,1,1,3,0,0
+4,0,3,0,1,2,1,3,0,0
+4,0,7,0,1,2,1,2,0,1
+1,1,2,0,1,3,1,3,0,0
+2,2,5,1,2,3,0,3,1,1
+4,0,5,0,2,2,1,5,1,1
+3,0,7,5,2,3,0,2,1,1
+3,0,5,0,1,3,0,0,0,1
+5,0,1,0,1,2,0,1,0,0
+1,2,7,0,1,2,0,2,1,0
+2,2,5,0,1,2,1,4,0,0
+2,2,5,0,1,1,0,2,0,0
+4,0,2,0,1,2,0,2,0,0
+2,2,1,0,1,2,0,2,0,0
+4,0,3,0,1,1,0,2,0,0
+3,0,1,0,1,1,0,3,0,0
+3,2,4,0,1,1,0,2,0,0
+3,0,5,6,2,3,0,4,1,1
+3,0,1,0,1,2,0,2,0,0
+2,2,5,0,1,1,0,5,0,0
+5,0,0,0,1,1,0,4,0,0
+2,2,4,0,1,3,1,3,1,0
+3,2,4,0,1,3,1,2,1,1
+3,0,7,0,1,2,0,2,0,0
+4,0,4,0,1,3,0,4,1,1
+2,2,5,4,2,2,1,2,0,0
+3,0,3,0,1,2,0,3,0,1
+5,0,3,0,1,3,0,3,0,0
+1,2,4,0,1,1,0,1,0,0
+4,0,5,0,1,2,0,2,0,0
+2,2,3,4,2,2,1,5,1,1
+3,0,5,6,0,3,0,2,1,0
+3,0,0,0,1,2,0,1,0,0
+2,2,3,0,1,3,1,2,1,0
+1,2,6,0,1,3,0,2,0,1
+4,0,5,0,1,1,0,3,0,0
+4,0,3,0,1,1,0,2,0,0
+3,0,4,5,1,3,0,2,1,1
+3,2,6,2,2,3,1,5,0,1
+1,2,3,4,2,2,1,3,1,0
+2,2,3,5,1,2,1,2,1,0
+3,0,6,0,1,3,0,2,0,0
+3,2,6,0,1,2,1,3,0,0
+2,2,4,0,1,2,0,3,1,0
+2,2,6,0,1,2,1,5,0,0
+3,2,5,4,2,2,0,2,1,0
+2,2,3,0,1,2,1,5,0,0
+4,0,2,0,1,3,1,3,1,0
+3,0,5,5,2,2,0,2,0,0
+3,2,4,4,2,2,0,2,1,0
+1,2,5,5,2,2,1,5,0,0
+3,2,2,0,1,2,1,2,0,0
+3,0,7,0,1,3,0,5,0,0
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test-data/breast_cancer.tsv	Wed May 28 11:30:50 2025 +0000
@@ -0,0 +1,287 @@
+age	menopause	tumor-size	inv-nodes	node-caps	deg-malig	breast	breast-quad	irradiat	target
+2	2	2	0	2	3	1	3	0	1
+3	0	2	0	1	1	1	1	0	0
+3	0	6	0	1	2	0	2	0	1
+2	2	6	0	2	3	1	2	1	0
+2	2	5	4	2	2	0	5	0	1
+3	2	4	4	1	2	1	3	1	0
+3	0	7	0	1	3	0	3	0	0
+2	2	1	0	1	2	0	3	0	0
+2	2	0	0	1	2	1	4	0	0
+2	0	7	2	2	2	1	3	1	0
+3	2	4	0	1	2	0	2	0	0
+4	0	2	0	1	2	1	3	0	0
+3	0	5	0	1	1	1	1	0	0
+3	0	4	0	1	2	1	3	0	0
+2	2	4	0	1	2	0	2	1	1
+1	2	3	0	1	3	0	1	0	0
+3	2	1	4	1	1	1	3	0	0
+4	0	2	0	1	2	1	3	0	0
+3	2	7	0	1	2	0	3	0	0
+3	0	3	0	1	3	0	3	0	0
+3	1	3	0	0	1	0	2	0	1
+4	0	7	4	1	2	1	3	1	0
+3	0	2	0	1	2	1	2	0	0
+2	2	1	0	1	1	1	3	0	0
+1	2	2	5	2	3	0	2	1	1
+3	0	3	4	2	2	1	3	0	0
+3	0	1	0	1	2	1	2	0	0
+2	2	1	0	1	1	1	3	0	0
+4	0	5	4	2	3	0	2	0	0
+2	2	2	2	2	3	0	2	0	1
+4	0	5	0	1	3	1	1	0	1
+4	0	4	4	0	1	1	2	1	0
+3	0	4	0	1	3	0	5	0	0
+3	0	3	0	1	3	1	3	0	0
+2	2	5	0	1	1	0	2	1	1
+1	2	2	0	1	1	0	2	0	0
+2	2	1	0	1	2	1	3	0	0
+4	0	8	5	2	3	0	1	0	0
+2	0	3	0	1	3	0	2	0	0
+2	2	1	0	1	1	1	4	0	0
+1	2	6	0	1	3	0	2	0	1
+2	2	6	6	2	2	1	5	1	0
+4	0	4	0	1	2	1	2	0	0
+3	0	3	4	2	3	1	5	0	1
+1	2	2	0	1	1	0	2	0	0
+3	2	5	0	1	3	0	5	0	1
+4	0	1	0	1	2	1	3	1	0
+2	2	6	0	2	3	1	3	1	0
+3	2	10	0	2	2	1	3	1	0
+3	0	7	0	1	3	1	3	0	0
+5	0	2	6	0	1	0	2	1	1
+3	1	5	0	1	3	1	3	0	0
+2	2	0	0	1	3	0	1	0	0
+5	0	7	0	1	1	1	5	0	0
+2	2	4	0	0	2	0	4	1	0
+3	0	4	2	2	3	1	3	0	0
+3	2	3	0	1	1	0	2	0	0
+3	0	6	2	1	3	0	2	0	0
+3	0	10	0	1	1	1	5	0	0
+1	2	0	0	1	2	1	1	0	1
+3	0	7	5	2	3	0	2	1	1
+2	2	5	0	1	2	1	5	1	0
+2	0	3	0	1	3	0	3	0	0
+2	2	5	2	2	3	0	2	0	1
+2	0	3	0	1	2	1	3	0	1
+3	0	2	0	1	1	1	1	0	0
+1	2	4	0	1	2	1	2	0	0
+4	0	2	0	1	2	0	2	0	0
+3	2	10	6	2	2	1	3	0	1
+1	2	1	0	1	1	1	2	0	0
+3	2	4	4	2	3	0	2	1	1
+4	0	4	4	0	1	1	3	1	0
+4	0	1	0	1	1	1	2	0	0
+3	0	5	5	2	3	0	4	0	1
+1	2	4	5	2	3	0	4	1	1
+3	0	1	0	1	1	0	2	0	0
+3	2	2	0	1	1	0	2	0	0
+2	2	4	0	1	2	1	1	0	0
+2	2	4	0	1	3	0	5	0	1
+4	0	5	5	2	2	1	5	0	0
+3	1	2	0	1	2	0	2	0	0
+2	2	4	0	1	2	1	2	0	0
+2	2	5	0	1	1	1	3	0	0
+4	0	2	0	1	2	0	3	1	0
+1	2	0	0	1	2	1	1	0	0
+3	0	6	0	1	3	0	3	0	0
+2	2	7	0	1	1	1	3	0	0
+1	2	4	5	2	2	1	3	1	0
+3	0	3	0	1	1	1	2	0	0
+3	0	5	0	1	1	0	3	0	0
+4	0	3	0	1	1	1	3	0	1
+1	2	5	4	1	3	1	3	1	1
+3	1	3	0	0	1	0	3	0	1
+3	2	1	0	1	2	1	3	0	0
+3	0	3	0	1	2	1	3	0	0
+2	2	8	0	1	2	0	2	1	0
+1	2	7	0	1	1	0	3	0	1
+3	2	1	0	1	1	0	2	0	0
+4	0	5	0	1	3	1	3	1	1
+2	2	6	0	1	1	1	3	0	1
+2	2	3	4	2	2	0	2	1	1
+3	2	2	0	1	2	0	2	0	1
+3	0	5	0	1	3	1	2	0	0
+4	0	3	0	1	2	0	3	0	0
+2	2	3	0	1	1	0	4	0	0
+4	0	5	4	2	2	0	1	1	1
+4	0	3	4	1	2	0	2	1	1
+3	2	4	0	1	2	0	5	0	1
+3	0	5	0	1	1	1	5	0	0
+2	2	3	0	1	2	0	4	0	0
+4	0	2	0	1	1	1	3	0	0
+4	0	5	0	1	2	0	2	1	0
+1	2	5	0	1	2	0	3	0	0
+1	2	7	4	1	3	1	5	1	0
+4	0	9	0	1	1	0	1	0	0
+4	0	1	0	1	1	0	3	0	0
+2	2	5	5	2	3	1	3	0	1
+4	0	1	0	1	1	0	3	0	0
+2	2	6	6	2	2	1	3	1	0
+2	2	3	0	1	1	1	2	0	0
+2	2	5	0	2	3	1	5	0	1
+3	2	4	0	2	2	0	3	0	0
+2	2	2	0	1	2	0	2	0	0
+1	2	6	6	2	3	0	2	0	1
+1	2	1	0	1	2	0	4	0	0
+3	0	5	0	1	1	1	2	0	0
+4	0	5	0	1	2	0	3	0	0
+4	0	4	0	1	2	0	2	0	0
+2	2	2	0	1	2	0	3	0	1
+4	0	2	0	1	2	1	2	0	0
+2	2	5	0	1	2	0	4	0	0
+0	2	6	0	1	2	1	5	0	0
+2	2	5	0	1	3	1	5	0	1
+2	2	4	0	1	2	1	2	0	1
+1	2	5	0	1	3	0	2	0	0
+1	2	2	0	1	1	1	2	0	1
+3	0	0	0	1	1	1	1	0	0
+3	0	0	0	1	1	0	2	0	0
+4	0	10	0	1	3	1	3	0	1
+3	2	5	0	1	1	0	1	0	0
+4	0	3	3	2	3	0	2	1	1
+2	2	4	0	1	2	0	3	0	0
+2	2	5	4	1	2	1	3	0	1
+3	2	3	4	2	2	0	2	0	0
+3	0	2	0	2	2	0	1	1	0
+3	2	1	0	1	3	0	2	0	0
+1	2	5	6	1	2	1	3	1	1
+4	0	1	0	1	1	0	2	0	0
+2	2	7	0	1	2	1	2	0	0
+3	0	5	6	0	3	0	3	1	0
+2	2	10	0	1	2	1	2	1	1
+3	0	2	0	1	2	1	5	0	0
+3	0	7	4	2	2	0	2	0	0
+1	2	4	4	2	3	0	2	1	1
+4	0	1	0	1	2	0	2	0	0
+4	1	1	0	1	1	0	5	0	0
+1	2	5	0	1	2	0	3	0	1
+1	2	3	4	2	2	0	2	0	1
+3	0	1	0	1	1	1	3	0	0
+4	0	4	0	1	3	1	3	0	0
+3	0	4	4	2	3	1	3	0	0
+2	2	5	5	1	2	0	3	0	0
+4	0	10	0	1	2	0	2	0	0
+3	2	5	0	1	3	0	2	0	0
+2	0	3	4	1	3	1	2	1	1
+3	0	5	5	2	2	0	4	1	1
+4	0	4	4	1	2	1	5	0	1
+2	2	3	0	1	2	0	1	0	0
+2	2	3	0	1	2	0	3	0	0
+2	2	10	0	1	2	0	2	0	0
+3	0	3	0	1	2	1	1	0	1
+3	0	5	4	1	3	1	3	0	1
+2	0	4	0	1	2	0	2	0	0
+3	2	4	0	1	1	1	3	0	1
+2	2	7	4	2	3	1	3	1	0
+2	2	3	0	1	2	1	3	0	0
+2	2	3	4	1	2	1	3	0	0
+2	2	4	6	2	3	1	3	0	1
+2	2	4	0	1	2	1	2	0	1
+2	2	3	0	1	1	1	5	0	0
+1	2	7	0	1	2	1	5	0	0
+4	0	1	5	2	3	0	3	1	1
+2	2	6	0	1	1	0	2	0	0
+3	0	5	4	1	3	0	2	0	1
+2	2	9	0	1	1	0	2	1	0
+4	0	2	0	1	1	0	4	0	0
+2	2	5	0	1	3	1	5	0	0
+2	2	4	0	1	3	0	3	0	1
+3	0	9	0	1	2	1	5	0	0
+3	2	4	0	1	2	1	4	0	0
+3	2	4	0	1	2	0	5	0	1
+2	2	1	0	1	2	0	2	1	0
+4	0	6	5	2	3	0	2	0	1
+4	0	10	0	1	2	1	3	1	0
+2	2	4	0	1	2	1	3	0	0
+1	2	3	4	1	2	1	1	0	0
+1	2	5	0	1	1	1	3	0	1
+4	1	5	0	1	1	0	2	0	0
+2	2	2	1	1	3	1	4	1	0
+4	0	3	0	1	3	1	2	0	1
+1	2	9	0	1	2	0	4	0	0
+2	2	5	0	1	3	0	3	0	0
+4	0	5	0	1	3	0	2	0	0
+2	2	4	0	1	1	1	4	0	0
+2	2	4	0	1	1	0	4	0	0
+4	0	7	4	2	3	1	2	0	1
+3	0	4	0	1	2	0	2	0	0
+3	2	5	0	1	3	1	3	1	1
+2	0	5	4	1	3	0	2	0	1
+2	2	4	0	1	1	1	2	1	0
+2	0	4	1	2	3	0	4	1	1
+2	2	7	0	1	1	0	2	0	1
+2	2	3	0	1	2	0	2	0	0
+3	0	4	0	1	1	0	4	0	0
+2	2	3	0	1	2	1	3	0	0
+5	0	7	0	1	1	1	3	0	0
+4	0	4	0	1	3	0	3	0	1
+3	2	4	0	1	2	0	2	0	0
+4	0	8	0	1	1	1	5	1	1
+3	0	3	0	2	2	1	3	0	0
+3	0	4	0	1	1	0	2	0	0
+3	0	3	0	1	3	0	3	0	0
+2	2	3	4	1	2	1	2	0	0
+3	0	6	0	1	2	0	3	0	0
+1	2	3	0	1	3	0	3	1	1
+4	0	5	0	1	1	1	3	0	0
+4	0	4	0	1	3	1	2	0	0
+2	0	5	0	1	2	0	3	1	0
+1	2	4	0	1	2	0	2	0	0
+2	2	3	0	1	2	0	2	0	1
+1	2	3	0	1	2	0	4	0	0
+2	2	1	0	1	2	1	2	0	0
+3	2	2	0	1	2	1	4	0	0
+3	2	4	0	1	1	1	3	0	0
+4	0	3	0	1	2	1	3	0	0
+4	0	7	0	1	2	1	2	0	1
+1	1	2	0	1	3	1	3	0	0
+2	2	5	1	2	3	0	3	1	1
+4	0	5	0	2	2	1	5	1	1
+3	0	7	5	2	3	0	2	1	1
+3	0	5	0	1	3	0	0	0	1
+5	0	1	0	1	2	0	1	0	0
+1	2	7	0	1	2	0	2	1	0
+2	2	5	0	1	2	1	4	0	0
+2	2	5	0	1	1	0	2	0	0
+4	0	2	0	1	2	0	2	0	0
+2	2	1	0	1	2	0	2	0	0
+4	0	3	0	1	1	0	2	0	0
+3	0	1	0	1	1	0	3	0	0
+3	2	4	0	1	1	0	2	0	0
+3	0	5	6	2	3	0	4	1	1
+3	0	1	0	1	2	0	2	0	0
+2	2	5	0	1	1	0	5	0	0
+5	0	0	0	1	1	0	4	0	0
+2	2	4	0	1	3	1	3	1	0
+3	2	4	0	1	3	1	2	1	1
+3	0	7	0	1	2	0	2	0	0
+4	0	4	0	1	3	0	4	1	1
+2	2	5	4	2	2	1	2	0	0
+3	0	3	0	1	2	0	3	0	1
+5	0	3	0	1	3	0	3	0	0
+1	2	4	0	1	1	0	1	0	0
+4	0	5	0	1	2	0	2	0	0
+2	2	3	4	2	2	1	5	1	1
+3	0	5	6	0	3	0	2	1	0
+3	0	0	0	1	2	0	1	0	0
+2	2	3	0	1	3	1	2	1	0
+1	2	6	0	1	3	0	2	0	1
+4	0	5	0	1	1	0	3	0	0
+4	0	3	0	1	1	0	2	0	0
+3	0	4	5	1	3	0	2	1	1
+3	2	6	2	2	3	1	5	0	1
+1	2	3	4	2	2	1	3	1	0
+2	2	3	5	1	2	1	2	1	0
+3	0	6	0	1	3	0	2	0	0
+3	2	6	0	1	2	1	3	0	0
+2	2	4	0	1	2	0	3	1	0
+2	2	6	0	1	2	1	5	0	0
+3	2	5	4	2	2	0	2	1	0
+2	2	3	0	1	2	1	5	0	0
+4	0	2	0	1	3	1	3	1	0
+3	0	5	5	2	2	0	2	0	0
+3	2	4	4	2	2	0	2	1	0
+1	2	5	5	2	2	1	5	0	0
+3	2	2	0	1	2	1	2	0	0
+3	0	7	0	1	3	0	5	0	0
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test-data/reg_1027_ESL.csv	Wed May 28 11:30:50 2025 +0000
@@ -0,0 +1,489 @@
+in1,in2,in3,in4,target
+6.0,5.0,6.0,6.0,6.0
+5.0,4.0,5.0,5.0,5.0
+5.0,3.0,4.0,5.0,4.0
+6.0,5.0,6.0,7.0,6.0
+4.0,3.0,3.0,5.0,3.0
+9.0,6.0,6.0,6.0,6.0
+5.0,5.0,5.0,5.0,4.0
+5.0,3.0,2.0,2.0,2.0
+5.0,5.0,5.0,5.0,5.0
+5.0,4.0,6.0,6.0,6.0
+6.0,5.0,7.0,6.0,6.0
+8.0,5.0,6.0,6.0,6.0
+4.0,3.0,4.0,4.0,3.0
+6.0,6.0,5.0,5.0,5.0
+4.0,4.0,5.0,7.0,5.0
+6.0,3.0,5.0,5.0,4.0
+6.0,6.0,4.0,5.0,5.0
+6.0,5.0,5.0,6.0,6.0
+4.0,4.0,4.0,5.0,4.0
+6.0,6.0,6.0,7.0,7.0
+8.0,7.0,7.0,5.0,6.0
+2.0,1.0,2.0,4.0,2.0
+4.0,5.0,6.0,5.0,5.0
+7.0,6.0,5.0,6.0,6.0
+6.0,5.0,6.0,7.0,7.0
+9.0,8.0,6.0,6.0,7.0
+1.0,2.0,3.0,5.0,2.0
+6.0,6.0,6.0,6.0,5.0
+5.0,5.0,6.0,7.0,6.0
+7.0,6.0,6.0,6.0,6.0
+6.0,5.0,6.0,7.0,6.0
+4.0,3.0,5.0,6.0,4.0
+3.0,3.0,5.0,4.0,3.0
+5.0,4.0,4.0,5.0,5.0
+7.0,8.0,6.0,5.0,6.0
+4.0,4.0,4.0,6.0,4.0
+5.0,5.0,5.0,5.0,5.0
+6.0,7.0,6.0,6.0,6.0
+4.0,3.0,5.0,5.0,4.0
+5.0,4.0,4.0,5.0,4.0
+5.0,5.0,5.0,6.0,5.0
+4.0,4.0,4.0,5.0,4.0
+5.0,6.0,5.0,5.0,5.0
+6.0,6.0,6.0,7.0,7.0
+7.0,6.0,7.0,6.0,7.0
+2.0,1.0,4.0,5.0,2.0
+6.0,4.0,5.0,6.0,5.0
+2.0,2.0,2.0,5.0,3.0
+4.0,4.0,5.0,5.0,4.0
+5.0,5.0,6.0,6.0,5.0
+6.0,5.0,5.0,5.0,4.0
+5.0,7.0,6.0,5.0,5.0
+8.0,7.0,7.0,6.0,7.0
+6.0,6.0,6.0,6.0,6.0
+6.0,6.0,7.0,6.0,6.0
+7.0,6.0,5.0,5.0,5.0
+4.0,4.0,5.0,6.0,5.0
+8.0,9.0,5.0,6.0,7.0
+4.0,6.0,6.0,6.0,6.0
+6.0,5.0,7.0,5.0,6.0
+4.0,3.0,4.0,6.0,3.0
+6.0,6.0,6.0,6.0,6.0
+6.0,4.0,6.0,6.0,4.0
+7.0,6.0,6.0,7.0,6.0
+6.0,5.0,4.0,5.0,5.0
+7.0,8.0,6.0,7.0,8.0
+7.0,7.0,6.0,6.0,6.0
+7.0,7.0,6.0,5.0,6.0
+6.0,6.0,6.0,5.0,6.0
+7.0,5.0,7.0,6.0,6.0
+6.0,7.0,6.0,5.0,5.0
+5.0,4.0,4.0,6.0,4.0
+6.0,6.0,5.0,7.0,6.0
+6.0,7.0,6.0,6.0,6.0
+6.0,5.0,5.0,5.0,5.0
+3.0,4.0,5.0,6.0,4.0
+6.0,6.0,5.0,6.0,6.0
+5.0,4.0,6.0,5.0,5.0
+4.0,3.0,6.0,5.0,4.0
+5.0,5.0,6.0,5.0,5.0
+7.0,6.0,6.0,7.0,7.0
+6.0,5.0,6.0,6.0,6.0
+7.0,6.0,6.0,7.0,7.0
+6.0,7.0,7.0,6.0,6.0
+6.0,7.0,7.0,6.0,7.0
+8.0,8.0,6.0,7.0,8.0
+4.0,5.0,3.0,4.0,3.0
+6.0,7.0,5.0,4.0,5.0
+8.0,8.0,5.0,6.0,7.0
+8.0,7.0,8.0,7.0,7.0
+5.0,5.0,6.0,6.0,6.0
+6.0,6.0,6.0,6.0,6.0
+6.0,4.0,5.0,6.0,6.0
+7.0,6.0,6.0,6.0,7.0
+6.0,6.0,5.0,5.0,5.0
+8.0,7.0,7.0,6.0,7.0
+3.0,3.0,4.0,5.0,4.0
+7.0,5.0,5.0,6.0,6.0
+6.0,6.0,3.0,5.0,4.0
+5.0,8.0,6.0,6.0,6.0
+5.0,3.0,6.0,7.0,6.0
+7.0,6.0,7.0,6.0,7.0
+6.0,5.0,5.0,5.0,5.0
+3.0,2.0,2.0,6.0,1.0
+7.0,6.0,5.0,6.0,5.0
+8.0,6.0,7.0,7.0,8.0
+4.0,3.0,4.0,4.0,4.0
+7.0,7.0,7.0,7.0,9.0
+7.0,7.0,7.0,7.0,7.0
+9.0,8.0,6.0,5.0,7.0
+6.0,4.0,6.0,6.0,5.0
+7.0,7.0,7.0,7.0,8.0
+4.0,5.0,4.0,5.0,4.0
+6.0,6.0,6.0,6.0,6.0
+6.0,6.0,5.0,5.0,5.0
+6.0,5.0,7.0,6.0,6.0
+7.0,6.0,6.0,6.0,6.0
+7.0,7.0,6.0,5.0,6.0
+5.0,5.0,6.0,6.0,6.0
+7.0,6.0,6.0,5.0,6.0
+7.0,7.0,7.0,6.0,7.0
+5.0,4.0,5.0,6.0,4.0
+5.0,4.0,3.0,2.0,1.0
+5.0,5.0,5.0,6.0,5.0
+6.0,5.0,5.0,6.0,5.0
+4.0,3.0,3.0,4.0,3.0
+4.0,3.0,5.0,6.0,4.0
+6.0,5.0,6.0,6.0,6.0
+4.0,2.0,3.0,4.0,3.0
+6.0,5.0,6.0,5.0,6.0
+6.0,5.0,6.0,6.0,6.0
+6.0,7.0,7.0,7.0,7.0
+2.0,3.0,5.0,4.0,3.0
+4.0,6.0,4.0,5.0,4.0
+6.0,6.0,7.0,6.0,6.0
+3.0,3.0,4.0,4.0,3.0
+4.0,6.0,5.0,4.0,4.0
+5.0,3.0,5.0,7.0,5.0
+6.0,4.0,5.0,6.0,5.0
+3.0,3.0,4.0,5.0,4.0
+2.0,2.0,4.0,6.0,4.0
+8.0,8.0,7.0,6.0,7.0
+8.0,8.0,7.0,6.0,8.0
+6.0,6.0,6.0,6.0,6.0
+7.0,7.0,6.0,6.0,6.0
+6.0,6.0,6.0,6.0,6.0
+7.0,5.0,5.0,5.0,5.0
+5.0,5.0,4.0,4.0,4.0
+5.0,5.0,6.0,7.0,6.0
+6.0,6.0,5.0,5.0,5.0
+5.0,6.0,4.0,5.0,4.0
+3.0,3.0,3.0,5.0,3.0
+3.0,3.0,4.0,5.0,4.0
+4.0,3.0,4.0,5.0,4.0
+6.0,5.0,5.0,5.0,4.0
+6.0,5.0,5.0,6.0,6.0
+4.0,4.0,5.0,6.0,5.0
+6.0,5.0,6.0,6.0,6.0
+6.0,6.0,6.0,6.0,7.0
+5.0,4.0,5.0,5.0,4.0
+5.0,6.0,6.0,6.0,6.0
+6.0,6.0,6.0,7.0,8.0
+5.0,5.0,4.0,4.0,4.0
+6.0,6.0,7.0,6.0,6.0
+6.0,7.0,6.0,6.0,6.0
+5.0,3.0,5.0,6.0,5.0
+4.0,4.0,6.0,6.0,5.0
+8.0,8.0,6.0,6.0,8.0
+7.0,9.0,6.0,5.0,6.0
+4.0,5.0,5.0,5.0,4.0
+5.0,4.0,6.0,6.0,6.0
+4.0,3.0,5.0,5.0,4.0
+6.0,4.0,6.0,6.0,6.0
+6.0,5.0,6.0,6.0,6.0
+6.0,5.0,4.0,5.0,4.0
+5.0,3.0,6.0,6.0,5.0
+6.0,5.0,6.0,7.0,6.0
+4.0,5.0,5.0,4.0,4.0
+6.0,5.0,6.0,6.0,6.0
+8.0,8.0,7.0,7.0,8.0
+5.0,6.0,5.0,5.0,5.0
+8.0,7.0,8.0,7.0,8.0
+6.0,6.0,6.0,6.0,6.0
+7.0,5.0,6.0,7.0,7.0
+5.0,4.0,5.0,5.0,4.0
+6.0,5.0,5.0,6.0,5.0
+5.0,5.0,5.0,5.0,5.0
+3.0,1.0,4.0,4.0,3.0
+6.0,7.0,7.0,6.0,7.0
+3.0,4.0,4.0,5.0,4.0
+4.0,4.0,5.0,6.0,5.0
+5.0,3.0,5.0,6.0,4.0
+8.0,8.0,6.0,4.0,4.0
+6.0,5.0,5.0,5.0,5.0
+5.0,4.0,6.0,5.0,5.0
+8.0,5.0,7.0,7.0,7.0
+8.0,7.0,7.0,7.0,8.0
+4.0,2.0,3.0,4.0,2.0
+5.0,5.0,5.0,5.0,5.0
+2.0,2.0,2.0,4.0,2.0
+5.0,3.0,6.0,6.0,5.0
+3.0,3.0,3.0,5.0,3.0
+4.0,4.0,3.0,3.0,3.0
+8.0,6.0,7.0,6.0,8.0
+7.0,6.0,7.0,6.0,7.0
+7.0,5.0,5.0,6.0,6.0
+6.0,4.0,6.0,6.0,6.0
+6.0,4.0,3.0,5.0,4.0
+8.0,5.0,7.0,6.0,8.0
+9.0,6.0,7.0,8.0,9.0
+6.0,7.0,7.0,6.0,6.0
+3.0,3.0,3.0,5.0,3.0
+5.0,5.0,6.0,5.0,5.0
+6.0,7.0,6.0,6.0,6.0
+7.0,6.0,6.0,5.0,6.0
+8.0,7.0,8.0,7.0,9.0
+5.0,6.0,7.0,6.0,6.0
+6.0,5.0,5.0,5.0,5.0
+3.0,4.0,6.0,5.0,4.0
+5.0,3.0,4.0,5.0,4.0
+4.0,5.0,6.0,6.0,5.0
+4.0,3.0,4.0,5.0,4.0
+2.0,3.0,3.0,6.0,4.0
+8.0,8.0,7.0,8.0,9.0
+6.0,6.0,5.0,5.0,5.0
+4.0,3.0,5.0,5.0,4.0
+6.0,6.0,7.0,6.0,6.0
+7.0,8.0,6.0,6.0,8.0
+5.0,4.0,4.0,5.0,4.0
+4.0,3.0,5.0,5.0,3.0
+4.0,3.0,4.0,5.0,4.0
+8.0,7.0,6.0,7.0,7.0
+5.0,4.0,5.0,6.0,5.0
+5.0,5.0,5.0,5.0,5.0
+6.0,6.0,6.0,7.0,6.0
+4.0,3.0,4.0,6.0,4.0
+6.0,5.0,5.0,6.0,5.0
+4.0,4.0,5.0,6.0,5.0
+5.0,5.0,6.0,6.0,6.0
+7.0,7.0,7.0,5.0,6.0
+5.0,4.0,4.0,5.0,5.0
+4.0,3.0,3.0,4.0,3.0
+3.0,3.0,5.0,5.0,4.0
+6.0,7.0,6.0,5.0,6.0
+5.0,4.0,5.0,5.0,5.0
+8.0,5.0,6.0,5.0,6.0
+2.0,3.0,3.0,3.0,2.0
+6.0,5.0,4.0,4.0,3.0
+5.0,5.0,4.0,5.0,5.0
+4.0,3.0,5.0,6.0,4.0
+4.0,4.0,5.0,6.0,5.0
+7.0,7.0,6.0,6.0,6.0
+2.0,2.0,3.0,4.0,2.0
+5.0,4.0,5.0,6.0,4.0
+7.0,8.0,6.0,7.0,7.0
+5.0,4.0,5.0,5.0,4.0
+3.0,3.0,4.0,4.0,4.0
+4.0,4.0,5.0,6.0,5.0
+8.0,7.0,6.0,6.0,6.0
+8.0,4.0,5.0,5.0,4.0
+5.0,6.0,6.0,7.0,7.0
+7.0,5.0,6.0,6.0,6.0
+5.0,4.0,5.0,5.0,5.0
+5.0,4.0,4.0,5.0,4.0
+7.0,6.0,6.0,6.0,6.0
+5.0,4.0,5.0,6.0,5.0
+5.0,5.0,5.0,6.0,5.0
+8.0,7.0,6.0,6.0,6.0
+6.0,5.0,6.0,7.0,7.0
+6.0,7.0,7.0,7.0,7.0
+2.0,2.0,3.0,4.0,2.0
+4.0,2.0,4.0,5.0,3.0
+5.0,5.0,5.0,6.0,5.0
+7.0,6.0,7.0,4.0,5.0
+4.0,3.0,5.0,6.0,4.0
+3.0,5.0,5.0,5.0,4.0
+6.0,3.0,4.0,5.0,3.0
+5.0,5.0,4.0,4.0,3.0
+4.0,4.0,5.0,4.0,3.0
+6.0,6.0,7.0,7.0,7.0
+6.0,6.0,6.0,5.0,5.0
+8.0,8.0,7.0,6.0,7.0
+6.0,5.0,5.0,6.0,5.0
+7.0,6.0,6.0,7.0,7.0
+7.0,6.0,7.0,6.0,7.0
+8.0,6.0,7.0,7.0,8.0
+7.0,6.0,6.0,7.0,8.0
+6.0,6.0,6.0,5.0,5.0
+3.0,4.0,5.0,4.0,4.0
+5.0,6.0,5.0,5.0,5.0
+5.0,6.0,6.0,6.0,6.0
+4.0,2.0,5.0,6.0,4.0
+6.0,6.0,7.0,7.0,6.0
+6.0,5.0,7.0,7.0,6.0
+6.0,7.0,6.0,7.0,7.0
+4.0,4.0,4.0,5.0,4.0
+5.0,4.0,5.0,5.0,4.0
+4.0,4.0,4.0,5.0,4.0
+6.0,5.0,5.0,5.0,5.0
+7.0,4.0,4.0,5.0,3.0
+6.0,6.0,7.0,7.0,7.0
+8.0,5.0,6.0,6.0,7.0
+2.0,1.0,4.0,4.0,2.0
+5.0,6.0,5.0,5.0,5.0
+5.0,6.0,6.0,6.0,6.0
+6.0,5.0,6.0,5.0,5.0
+4.0,4.0,5.0,7.0,5.0
+7.0,6.0,6.0,6.0,6.0
+4.0,6.0,4.0,4.0,4.0
+6.0,7.0,6.0,6.0,6.0
+6.0,5.0,5.0,6.0,6.0
+4.0,5.0,6.0,7.0,5.0
+6.0,5.0,4.0,4.0,4.0
+4.0,4.0,4.0,4.0,4.0
+5.0,5.0,6.0,6.0,6.0
+5.0,6.0,6.0,6.0,6.0
+5.0,6.0,5.0,6.0,6.0
+4.0,5.0,3.0,5.0,3.0
+8.0,7.0,8.0,7.0,8.0
+4.0,6.0,5.0,4.0,4.0
+5.0,4.0,4.0,5.0,4.0
+5.0,6.0,5.0,6.0,5.0
+4.0,3.0,4.0,5.0,3.0
+7.0,7.0,7.0,6.0,6.0
+8.0,6.0,6.0,5.0,5.0
+7.0,5.0,5.0,6.0,6.0
+5.0,4.0,5.0,6.0,5.0
+5.0,5.0,4.0,5.0,4.0
+5.0,6.0,6.0,6.0,6.0
+6.0,6.0,7.0,7.0,7.0
+6.0,7.0,6.0,6.0,6.0
+7.0,6.0,6.0,6.0,6.0
+6.0,5.0,5.0,6.0,5.0
+6.0,6.0,5.0,5.0,5.0
+5.0,4.0,5.0,6.0,5.0
+4.0,5.0,4.0,5.0,3.0
+5.0,5.0,5.0,5.0,5.0
+8.0,6.0,6.0,5.0,6.0
+4.0,4.0,6.0,6.0,5.0
+5.0,5.0,6.0,7.0,6.0
+6.0,7.0,7.0,6.0,6.0
+5.0,5.0,6.0,5.0,5.0
+7.0,7.0,7.0,7.0,7.0
+4.0,6.0,5.0,5.0,5.0
+8.0,6.0,6.0,6.0,6.0
+9.0,7.0,7.0,7.0,8.0
+7.0,6.0,6.0,7.0,7.0
+4.0,5.0,5.0,4.0,4.0
+6.0,6.0,7.0,7.0,7.0
+5.0,4.0,5.0,5.0,5.0
+6.0,6.0,5.0,5.0,5.0
+5.0,5.0,6.0,6.0,6.0
+7.0,7.0,7.0,6.0,5.0
+6.0,3.0,4.0,5.0,3.0
+5.0,5.0,6.0,5.0,5.0
+5.0,6.0,6.0,7.0,6.0
+6.0,5.0,5.0,6.0,6.0
+6.0,8.0,5.0,5.0,7.0
+4.0,3.0,5.0,5.0,4.0
+8.0,6.0,6.0,5.0,6.0
+3.0,4.0,6.0,6.0,5.0
+5.0,4.0,5.0,6.0,5.0
+7.0,5.0,5.0,7.0,6.0
+9.0,8.0,6.0,7.0,7.0
+6.0,6.0,6.0,6.0,6.0
+5.0,6.0,7.0,6.0,6.0
+6.0,5.0,4.0,4.0,4.0
+7.0,5.0,6.0,6.0,6.0
+5.0,6.0,5.0,7.0,6.0
+4.0,3.0,6.0,6.0,5.0
+4.0,4.0,5.0,6.0,4.0
+5.0,5.0,5.0,6.0,5.0
+3.0,3.0,4.0,5.0,4.0
+5.0,7.0,6.0,6.0,6.0
+6.0,6.0,6.0,6.0,6.0
+7.0,6.0,6.0,7.0,7.0
+3.0,4.0,4.0,5.0,4.0
+5.0,4.0,4.0,5.0,4.0
+6.0,6.0,6.0,6.0,6.0
+4.0,4.0,4.0,6.0,4.0
+4.0,2.0,5.0,5.0,3.0
+8.0,7.0,6.0,7.0,7.0
+8.0,7.0,6.0,6.0,6.0
+6.0,6.0,6.0,7.0,7.0
+6.0,5.0,6.0,6.0,6.0
+5.0,6.0,6.0,6.0,6.0
+5.0,5.0,6.0,6.0,6.0
+6.0,7.0,7.0,7.0,7.0
+5.0,2.0,6.0,6.0,5.0
+7.0,6.0,6.0,6.0,6.0
+4.0,4.0,6.0,7.0,6.0
+6.0,6.0,5.0,6.0,5.0
+5.0,4.0,4.0,5.0,4.0
+8.0,7.0,6.0,6.0,7.0
+4.0,3.0,4.0,4.0,3.0
+5.0,6.0,6.0,6.0,6.0
+5.0,3.0,5.0,6.0,4.0
+4.0,3.0,3.0,5.0,3.0
+6.0,7.0,5.0,4.0,4.0
+6.0,6.0,5.0,6.0,6.0
+6.0,4.0,6.0,6.0,5.0
+4.0,3.0,4.0,5.0,4.0
+5.0,6.0,5.0,5.0,5.0
+5.0,4.0,4.0,4.0,4.0
+0.0,0.0,2.0,5.0,4.0
+6.0,6.0,5.0,6.0,6.0
+6.0,7.0,6.0,6.0,6.0
+8.0,8.0,6.0,7.0,7.0
+6.0,5.0,5.0,5.0,5.0
+8.0,8.0,7.0,8.0,8.0
+3.0,4.0,4.0,5.0,4.0
+5.0,4.0,4.0,6.0,4.0
+5.0,6.0,7.0,6.0,6.0
+6.0,6.0,6.0,6.0,6.0
+6.0,4.0,5.0,6.0,5.0
+3.0,2.0,5.0,6.0,4.0
+5.0,5.0,6.0,5.0,5.0
+5.0,4.0,4.0,5.0,4.0
+8.0,8.0,5.0,6.0,6.0
+6.0,7.0,6.0,6.0,6.0
+6.0,5.0,7.0,7.0,7.0
+6.0,6.0,6.0,7.0,7.0
+5.0,5.0,5.0,5.0,5.0
+6.0,6.0,6.0,4.0,4.0
+2.0,2.0,4.0,5.0,3.0
+4.0,3.0,3.0,3.0,3.0
+6.0,6.0,6.0,5.0,5.0
+3.0,3.0,5.0,5.0,4.0
+3.0,4.0,4.0,3.0,2.0
+5.0,4.0,6.0,7.0,6.0
+6.0,8.0,5.0,5.0,5.0
+5.0,5.0,4.0,4.0,4.0
+5.0,6.0,4.0,5.0,5.0
+7.0,6.0,5.0,6.0,6.0
+4.0,4.0,5.0,5.0,3.0
+8.0,6.0,6.0,7.0,7.0
+5.0,5.0,6.0,6.0,5.0
+6.0,5.0,6.0,5.0,6.0
+8.0,6.0,6.0,6.0,7.0
+5.0,5.0,5.0,6.0,5.0
+4.0,4.0,4.0,5.0,4.0
+5.0,6.0,6.0,5.0,5.0
+5.0,4.0,5.0,6.0,4.0
+6.0,6.0,6.0,7.0,7.0
+6.0,4.0,6.0,5.0,6.0
+6.0,5.0,5.0,6.0,5.0
+4.0,2.0,4.0,6.0,4.0
+4.0,4.0,5.0,5.0,3.0
+5.0,6.0,6.0,5.0,5.0
+6.0,7.0,6.0,5.0,5.0
+5.0,5.0,5.0,3.0,4.0
+4.0,4.0,3.0,3.0,3.0
+6.0,5.0,5.0,6.0,5.0
+9.0,7.0,6.0,6.0,6.0
+5.0,5.0,5.0,6.0,5.0
+6.0,8.0,7.0,6.0,6.0
+6.0,5.0,6.0,6.0,6.0
+6.0,5.0,5.0,7.0,7.0
+4.0,4.0,6.0,5.0,5.0
+3.0,4.0,5.0,6.0,5.0
+6.0,6.0,6.0,6.0,7.0
+3.0,3.0,4.0,5.0,3.0
+8.0,8.0,6.0,6.0,7.0
+6.0,5.0,5.0,5.0,5.0
+7.0,7.0,6.0,4.0,4.0
+8.0,7.0,7.0,6.0,7.0
+8.0,6.0,6.0,6.0,6.0
+6.0,6.0,4.0,4.0,4.0
+8.0,8.0,7.0,6.0,8.0
+5.0,5.0,6.0,6.0,5.0
+6.0,7.0,6.0,6.0,6.0
+6.0,4.0,5.0,5.0,4.0
+4.0,4.0,4.0,5.0,4.0
+5.0,4.0,5.0,5.0,4.0
+4.0,3.0,3.0,5.0,4.0
+4.0,3.0,4.0,5.0,3.0
+2.0,1.0,3.0,4.0,2.0
+5.0,5.0,6.0,6.0,6.0
+7.0,6.0,5.0,5.0,5.0
+7.0,6.0,7.0,7.0,7.0
+4.0,4.0,6.0,6.0,5.0
+7.0,5.0,7.0,7.0,7.0
+8.0,7.0,7.0,6.0,7.0
+4.0,3.0,3.0,5.0,3.0
+6.0,5.0,5.0,6.0,6.0
+4.0,4.0,6.0,8.0,6.0
+8.0,6.0,6.0,7.0,7.0
+7.0,6.0,5.0,5.0,5.0