Skip to content
Back to skills

Tabdsr Tabular Reasoning Llms

ASecurity

Improve language model performance on complex numerical reasoning over tables through three-stage pipeline: decompose questions into sub-questions, sanitize and clean table data, then generate executable Python code for precise computation.

  • 6 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added September 9, 2026
ai-agentspythonperformance

Security analysis

A100/100

Scanned September 9, 2026

npx -y skills add ADu2021/skillXiv --skill tabdsr-tabular-reasoning-llms --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Tabdsr Tabular Reasoning Llms?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Tabdsr Tabular Reasoning Llms
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/adu2021-tabdsr-tabular-reasoning-llms/badge)](https://www.skillsdirectory.com/skills/adu2021-tabdsr-tabular-reasoning-llms)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: tabdsr-tabular-reasoning-llms
title: "TabDSR: Decompose, Sanitize, Reason for Tabular Data"
version: 0.0.2
engine: skillxiv-v0.0.2-claude-opus-4.6
license: MIT
url: "https://arxiv.org/abs/2511.02219"
keywords: [Tabular QA, Table Understanding, Program Synthesis, Numerical Reasoning, Data Cleaning]
description: "Improve language model performance on complex numerical reasoning over tables through three-stage pipeline: decompose questions into sub-questions, sanitize and clean table data, then generate executable Python code for precise computation."
---

# Title: Enable Accurate Tabular Reasoning Through Decomposition and Code Generation

Language models struggle with numerical reasoning over tables because they hallucinate calculations and misalign rows/columns. TabDSR tackles this with a three-stage pipeline: (1) decompose complex questions into simpler sub-queries, (2) sanitize table data (fix headers, standardize formats, remove noise), and (3) use code generation to compute answers precisely. Each agent specializes in one task, enabling modular improvement.

The key insight is using code as the reasoning substrate for numerical operations.

## Core Concept

**Three-Stage Tabular Reasoning Pipeline**:
- **Query Decomposer**: Break complex questions into tractable sub-questions
- **Table Sanitizer**: Clean structural and content issues, standardize formats
- **Program-of-Thoughts (PoT) Reasoner**: Generate Python code for precise computation
- **Modular Design**: Each component can be improved independently
- **Evaluation**: New CalTab151 benchmark prevents data leakage

## Architecture Overview

- **Query Decomposer Agent**: LLM-based question analyzer using linguistic cues
- **Table Sanitizer Agent**: Multi-step cleaning (header reconstruction, value standardization)
- **PoT Reasoner**: Code generation using Pandas DataFrames
- **Execution Environment**: Sandboxed Python for safe code execution
- **Dataset**: CalTab151 with multi-hop questions and human verification

## Implementation Steps

**1. Implement Query Decomposer**

Analyze questions to identify sub-problems.

```python
class QueryDecomposer:
    def __init__(self, llm_model):
        self.llm = llm_model

    def analyze_query_complexity(self, query):
        """Determine if question needs decomposition"""
        # Check for linguistic patterns indicating multi-step reasoning
        complex_patterns = [
            'compare', 'how many', 'what percentage', 'ratio',
            'more than', 'less than', 'between', 'and then'
        ]

        is_complex = any(pattern in query.lower() for pattern in complex_patterns)
        return is_complex

    def decompose_query(self, query, table_context):
        """Break complex query into sub-questions"""
        if not self.analyze_query_complexity(query):
            return [query]  # Already simple

        # Use LLM to propose decomposition
        decompose_prompt = f"""Break this question into simpler sub-questions:
        Question: {query}
        Table context: {self.summarize_table(table_context)}

        Sub-questions (numbered):"""

        decomposition = self.llm.generate(decompose_prompt)
        sub_questions = self.parse_subquestions(decomposition)
        return sub_questions

    def parse_subquestions(self, text):
        """Extract numbered sub-questions from LLM output"""
        import re
        matches = re.findall(r'\d+\.\s*([^0-9]+?)(?=\d+\.|$)', text)
        return [m.strip() for m in matches]

    def summarize_table(self, table):
        """Provide table summary for context"""
        return f"Columns: {list(table.columns)}, Rows: {len(table)}"
```

**2. Implement Table Sanitizer**

Clean structural and content issues in tables.

```python
class TableSanitizer:
    def __init__(self):
        self.null_indicators = ['N/A', 'NA', 'null', 'None', '-', '?']

    def sanitize_headers(self, table):
        """Fix and standardize column headers"""
        # Issue 1: Multi-level headers (merge into single level)
        if isinstance(table.columns, pd.MultiIndex):
            table.columns = ['_'.join(col).strip() for col in table.columns]

        # Issue 2: Inconsistent naming
        table.columns = [col.lower().strip().replace(' ', '_') for col in table.columns]

        # Issue 3: Empty headers
        table.columns = [f"col_{i}" if col == "" else col for i, col in enumerate(table.columns)]

        return table

    def sanitize_content(self, table):
        """Clean cell values"""
        # Convert to appropriate types
        for col in table.columns:
            # Try numeric conversion
            try:
                table[col] = pd.to_numeric(table[col], errors='coerce')
            except:
                # Keep as string
                table[col] = table[col].astype(str)

            # Replace null indicators
            for null_ind in self.null_indicators:
                table[col] = table[col].replace(null_ind, None)

        # Remove fully null rows/columns
        table = table.dropna(how='all')
        table = table.dropna(axis=1, how='all')

        return table

    def reconstruct_segmented_tables(self, table):
        """Handle tables split across multiple sections"""
        # Detect horizontal breaks (rows with all None)
        null_rows = table.isnull().all(axis=1).values
        break_indices = np.where(null_rows)[0]

        if len(break_indices) > 0:
            # Split and process each segment
            segments = []
            start = 0
            for break_idx in break_indices:
                segment = table.iloc[start:break_idx]
                segments.append(segment)
                start = break_idx + 1
            segments.append(table.iloc[start:])

            # Return largest or most complete segment
            return max(segments, key=lambda x: x.shape[0])

        return table

    def sanitize(self, table):
        """Full sanitization pipeline"""
        table = self.sanitize_headers(table)
        table = self.sanitize_content(table)
        table = self.reconstruct_segmented_tables(table)
        return table
```

**3. Implement Program-of-Thoughts Reasoner**

Generate executable Python code for computation.

```python
class ProgramOfThoughtsReasoner:
    def __init__(self, code_llm, execution_env):
        self.code_llm = code_llm
        self.exec_env = execution_env

    def generate_program(self, question, table_df):
        """Generate Python code to answer question"""
        # Provide table schema
        schema = self.get_table_schema(table_df)

        code_prompt = f"""Write Python code to answer this question:
        Question: {question}

        Table schema:
        {schema}

        Available variable: 'df' is a pandas DataFrame with the table.

        Code (complete and executable):
        """

        code = self.code_llm.generate(code_prompt, max_tokens=500)
        return code

    def get_table_schema(self, df):
        """Describe table structure and dtypes"""
        schema = f"Columns: {list(df.columns)}\n"
        schema += f"Dtypes: {dict(df.dtypes)}\n"
        schema += f"Shape: {df.shape}\n"
        schema += f"Sample row: {df.iloc[0].to_dict()}"
        return schema

    def execute_program(self, code, df):
        """Execute Python code in sandbox"""
        # Create execution context
        local_context = {'df': df, 'pd': pd, 'np': np}

        try:
            exec(code, {}, local_context)

            # Extract answer (look for 'answer' or 'result' variable)
            if 'answer' in local_context:
                return local_context['answer']
            elif 'result' in local_context:
                return local_context['result']
            else:
                # Return last assigned non-df variable
                return self.extract_final_value(local_context)

        except Exception as e:
            return f"Execution error: {str(e)}"

    def extract_final_value(self, context):
        """Extract final computed value from context"""
        # Get variables that were created/modified
        non_builtins = {k: v for k, v in context.items() if not k.startswith('_')}
        # Find numeric or string results
        for k, v in sorted(non_builtins.items(), reverse=True):
            if isinstance(v, (int, float, str)):
                return v
        return None

    def reason(self, question, table_df):
        """Full reasoning pipeline"""
        code = self.generate_program(question, table_df)
        answer = self.execute_program(code, table_df)
        return answer, code
```

**4. Integrate Three Stages**

Combine pipeline into unified interface.

```python
class TabDSRPipeline:
    def __init__(self, decomposer, sanitizer, reasoner):
        self.decomposer = decomposer
        self.sanitizer = sanitizer
        self.reasoner = reasoner

    def answer_question(self, question, table):
        """Answer tabular question using three-stage pipeline"""
        # Stage 1: Decompose
        sub_questions = self.decomposer.decompose_query(question, table)

        # Stage 2: Sanitize
        clean_table = self.sanitizer.sanitize(table)

        # Stage 3: Reason
        if len(sub_questions) == 1:
            # Simple question
            answer, code = self.reasoner.reason(question, clean_table)
        else:
            # Complex question: answer sub-questions, then combine
            sub_answers = []
            for sub_q in sub_questions:
                sub_answer, _ = self.reasoner.reason(sub_q, clean_table)
                sub_answers.append(sub_answer)

            # Combine sub-answers (simple merge for now)
            answer = self.combine_answers(question, sub_answers, clean_table)
            code = "# See sub-questions above"

        return {
            'answer': answer,
            'code': code,
            'sub_questions': sub_questions,
            'cleaned_table': clean_table
        }

    def combine_answers(self, original_question, sub_answers, table):
        """Merge sub-question answers into final result"""
        # For complex multi-step reasoning, generate code to combine results
        combine_prompt = f"""Combine these sub-answers to answer the original question:
        Original: {original_question}
        Sub-answers: {sub_answers}

        Final answer:"""

        combined = self.reasoner.code_llm.generate(combine_prompt)
        return combined
```

## Practical Guidance

**When to Use**:
- Numerical QA over business/scientific tables
- Tasks with complex multi-hop reasoning
- Settings where accuracy is critical (hallucinations unacceptable)

**Hyperparameters**:
- decomposition_threshold: Complexity level triggering decomposition
- null_replacement: How to handle missing values
- code_max_tokens: 500-1000 depending on problem complexity

**When NOT to Use**:
- Simple lookup questions (single cell retrieval)
- Tables with highly irregular structure
- Domains where code generation fails (expert knowledge needed)

**Pitfalls**:
- **Code generation hallucination**: Model may generate syntactically valid but logically wrong code; validate with intermediate results
- **Over-decomposition**: Some questions don't benefit from decomposition; check impact on results
- **Table cleaning over-aggressiveness**: Over-sanitizing can remove important nuance

**Integration Strategy**: Use as fallback for numerical questions. Combine with semantic matching for hybrid QA systems.

## Reference

arXiv: https://arxiv.org/abs/2511.02219

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.