Skip to main content

Super fast natural language data visualization and analysis for pandas DataFrames

Project description

QueryFrame

PyPI version Python 3.10+ License: MIT Tests Coverage

Super fast natural language data visualization and analysis for pandas DataFrames.

QueryFrame lets you ask questions about your data in plain English and get instant answers, charts, and insights. It's the faster, safer, more flexible alternative to PandasAI.

import pandas as pd
import queryframe as qf

df = pd.read_csv("sales.csv")

# Ask anything
result = qf.ask(df, "what is the average revenue by region?")
print(result.data)

# Visualize instantly
result = df.qf.ask("show me a bar chart of sales by product")
result.show()

# Style charts in natural language
result = qf.ask(df, "bar chart of sales with dark theme, sorted descending, pastel colors")
result.show()

Why QueryFrame over PandasAI?

Feature QueryFrame PandasAI
Speed Smart caching (1,200x faster on repeat queries) Sends full schema every query
Safety AST-validated sandbox (52 bypass tests) Raw exec()
Local models First-class Ollama + LM Studio Limited support
Visualizations Auto-selects Plotly/Matplotlib/Altair Mostly matplotlib
Chart styling Natural language ("dark theme, pastel colors") Manual code only
Follow-ups Conversation memory Stateless
Token usage Compressed schemas, 3 sample rows Verbose, 5 sample rows

Installation

Note: If you're using zsh (default on macOS), wrap extras in quotes.

# Core (no LLM provider included)
pip install queryframe

# With your preferred provider
pip install "queryframe[openai]"       # OpenAI
pip install "queryframe[anthropic]"    # Claude
pip install "queryframe[gemini]"       # Google Gemini
pip install "queryframe[ollama]"       # Ollama (local)
pip install "queryframe[lmstudio]"     # LM Studio (local)

# With visualization libraries
pip install "queryframe[plotly]"       # Interactive charts (recommended)
pip install "queryframe[matplotlib]"   # Static charts (includes seaborn)
pip install "queryframe[altair]"       # Declarative charts

# Provider + visualization together
pip install "queryframe[openai,plotly]"

# Everything
pip install "queryframe[all]"

Quick Start

1. Set your API key (cloud providers)

export OPENAI_API_KEY="sk-..."
# or
export ANTHROPIC_API_KEY="sk-ant-..."
# or
export GOOGLE_API_KEY="..."

2. Use it

import pandas as pd
import queryframe as qf

df = pd.DataFrame({
    "product": ["Laptop", "Phone", "Tablet"],
    "price": [999, 699, 449],
    "units_sold": [150, 500, 200],
})

# Natural language queries
result = qf.ask(df, "which product generated the most revenue?")
print(result.data)         # The answer
print(result.code)         # Generated pandas code
print(result.explanation)  # Human-readable explanation

# Visualizations
result = qf.ask(df, "bar chart of revenue by product")
result.show()              # Display interactive chart
result.save("chart.html")  # Export

Local Models (Ollama / LM Studio)

QueryFrame has first-class support for local models — no API keys, no data leaves your machine.

Ollama

# Start Ollama
ollama serve

# Pull a model
ollama pull llama3.1
import queryframe as qf

qf.configure(provider="ollama", model="llama3.1")
result = qf.ask(df, "average sales by region")

LM Studio

import queryframe as qf

# LM Studio runs at localhost:1234 by default
qf.configure(provider="lmstudio")
result = qf.ask(df, "show me the top 10 products")

All Providers

from queryframe import QueryEngine, QueryFrameConfig

# OpenAI
engine = QueryEngine(config=QueryFrameConfig(
    provider="openai", model="gpt-4o-mini"
))

# Anthropic Claude
engine = QueryEngine(config=QueryFrameConfig(
    provider="anthropic", model="claude-sonnet-4-20250514"
))

# Google Gemini
engine = QueryEngine(config=QueryFrameConfig(
    provider="gemini", model="gemini-2.0-flash"
))

# Ollama
engine = QueryEngine(config=QueryFrameConfig(
    provider="ollama", model="llama3.1"
))

# LM Studio
engine = QueryEngine(config=QueryFrameConfig(
    provider="lmstudio"
))

# Auto-detect (checks env vars, then local servers)
engine = QueryEngine()  # Just works

Visualization

QueryFrame auto-selects the best visualization library:

  • Notebooks → Plotly (interactive)
  • Scripts → Matplotlib (static)
  • Overrideqf.ask(df, "...", viz="altair")

Supported chart types: bar, line, scatter, pie, histogram, heatmap, box, area, violin, treemap, funnel

# Auto-select
result = qf.ask(df, "show trend of sales over time")  # → line chart

# Force specific library
result = qf.ask(df, "bar chart of revenue", viz="matplotlib")

# Re-render with different library
result = qf.ask(df, "sales by region").viz("altair")

# Save to file
result.save("chart.png")   # static image
result.save("chart.html")  # interactive HTML

Natural Language Chart Styling

Style your charts by describing what you want in plain English:

# Themes
result = qf.ask(df, "bar chart of sales with dark theme")
result = qf.ask(df, "line chart with presentation theme")

# Colors
result = qf.ask(df, "pie chart with pastel colors")
result = qf.ask(df, "bar chart using red and blue colors")

# Layout
result = qf.ask(df, "horizontal bar chart sorted descending")
result = qf.ask(df, "scatter plot with larger dots and a trend line")

# Labels and grid
result = qf.ask(df, "bar chart with percentage labels and no grid")
result = qf.ask(df, "chart with x-axis label 'Region' and y-axis label 'Revenue (USD)'")

# Line styles
result = qf.ask(df, "line chart with dashed lines")

# Colormaps
result = qf.ask(df, "heatmap with viridis colormap")

Supported Style Options

Option Values Example
theme dark, minimal, presentation, light "with dark theme"
colors Color names, hex codes, or palettes "using red and blue"
color palettes pastel, vibrant, earth, ocean, sunset, monochrome, neon, corporate "with pastel colors"
orientation horizontal, vertical "horizontal bar chart"
sort_order ascending, descending "sorted descending"
line_style solid, dashed, dotted "with dashed lines"
show_grid on/off "no grid"
show_labels on/off "with percentage labels"
marker_size any size "with larger dots"
colormap viridis, coolwarm, plasma, magma, etc. "viridis colormap"
opacity 0.0 to 1.0 "with 50% opacity"
axis labels any text "x-axis label 'Revenue'"
legend top, bottom, left, right, none "legend on top"
trendline on/off "with a trend line"

Chainable API

# Chain operations
result = (
    qf.ask(df, "total revenue by product")
    .save("revenue.html")
)

# Follow-up queries (uses conversation memory)
r1 = qf.ask(df, "show me sales by region")
r2 = r1.ask("now filter to just Q4")           # "it" = sales by region
r3 = r2.ask("which region had the highest?")    # context preserved

Caching

Repeated queries are instant (< 5ms vs 2-5s for LLM calls):

# First call: hits the LLM (~2s)
result = qf.ask(df, "average sales")
print(result.cached)  # False

# Same query: from cache (~1ms)
result = qf.ask(df, "average sales")
print(result.cached)  # True

Configuration

import queryframe as qf

# Via configure()
qf.configure(
    provider="openai",
    model="gpt-4o",
    cache_enabled=True,
    viz_mode="plotly",       # auto, plotly, matplotlib, altair
    timeout=30,              # seconds
    max_retries=2,
)

# Via environment variables
# QF_PROVIDER=openai
# QF_MODEL=gpt-4o
# QF_VIZ=plotly
# QF_TIMEOUT=30
# QF_VERBOSE=true
# QF_LOG_LEVEL=DEBUG

Configuration Reference

Setting Type Default Env Var Description
provider str "auto" QF_PROVIDER LLM provider: openai, anthropic, gemini, ollama, lmstudio, auto
model str None QF_MODEL Model name (e.g., gpt-4o-mini, llama3.1)
api_key str None QF_API_KEY API key (auto-detected from provider env vars)
api_base str None QF_API_BASE Custom API base URL
cache_enabled bool True Enable query result caching
sandbox_enabled bool True Enable code safety validation
timeout int 30 QF_TIMEOUT Code execution timeout (seconds)
viz_mode str "auto" QF_VIZ Visualization: auto, plotly, matplotlib, altair
max_retries int 2 QF_MAX_RETRIES Retry count on code execution failure
verbose bool False QF_VERBOSE Enable verbose logging

API Reference

Module-level functions

qf.ask(df, query, **kwargs) -> QueryResult    # Ask a question
qf.configure(**kwargs) -> None                  # Configure global engine

QueryResult

Property Type Description
.data Any The answer (DataFrame, scalar, list, etc.)
.chart Figure/None Plotly/Matplotlib/Altair figure
.code str Generated pandas code
.explanation str Human-readable explanation
.chart_type str/None Chart type (bar, line, etc.)
.query str Original query
.provider str LLM provider used
.latency_ms float Total latency in milliseconds
.cached bool Whether result came from cache
Method Returns Description
.show() None Display the chart
.save(path) QueryResult Save chart/data to file (.html, .png, .csv)
.viz(renderer) QueryResult Re-render with different viz library
.ask(query) QueryResult Follow-up query with conversation context
.to_html() str Convert chart/data to HTML string

DataFrame Accessor

df.qf.ask("your question")     # Same as qf.ask(df, "your question")
df.qf.config(provider="openai") # Configure the global engine

Security

QueryFrame takes security seriously — 52 security test cases verify the sandbox:

  1. AST Validation — All LLM-generated code is parsed and validated before execution. Dangerous operations (import os, exec, eval, open, __import__, etc.) are rejected
  2. Restricted Builtins — Only safe builtins available (no getattr, globals, setattr, vars, dir)
  3. Dunder Blocking — Attribute access to __class__, __subclasses__, __builtins__, __globals__, __code__, __file__, etc. is blocked
  4. Async Blockingasync def, await, async for, async with are all forbidden
  5. Subscript Blocking — Dict-style dunder access like obj["__builtins__"] is caught
  6. Import Whitelist — Only pandas, numpy, math, datetime, and other safe stdlib modules are importable
  7. Execution Timeout — Code that runs too long is killed (default: 30s)
  8. DataFrame Isolation — LLM code operates on a copy, never the original
  9. No Network Access — Sandboxed code cannot make network requests

Architecture

df.ask("show me sales by region")
    │
    ▼
┌─────────────┐     ┌──────────┐     ┌────────────┐
│ Cache Check  │────▸│  Schema  │────▸│  Prompt    │
│ (< 1ms)     │     │ Extract  │     │  Builder   │
└─────────────┘     └──────────┘     └────────────┘
                                          │
                                          ▼
┌─────────────┐     ┌──────────┐     ┌────────────┐
│ Viz Render   │◂────│ Sandbox  │◂────│    LLM     │
│ (auto-pick) │     │ Execute  │     │  Provider  │
└─────────────┘     └──────────┘     └────────────┘
                         │
                    ┌────────────┐
                    │ QueryResult│
                    │ .data      │
                    │ .chart     │
                    │ .code      │
                    └────────────┘

Development

# Clone
git clone https://github.com/yash-td/queryframe.git
cd queryframe

# Install in dev mode
pip install -e ".[dev,all]"

# Run tests (421 tests, 85% coverage)
pytest

# Lint
ruff check src/ tests/
ruff format src/ tests/

# Type check
mypy src/queryframe/

License

MIT License. See LICENSE for details.

Contributing

Contributions welcome! Please read CONTRIBUTING.md before submitting a PR.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

queryframe-0.1.1.tar.gz (59.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

queryframe-0.1.1-py3-none-any.whl (52.1 kB view details)

Uploaded Python 3

File details

Details for the file queryframe-0.1.1.tar.gz.

File metadata

  • Download URL: queryframe-0.1.1.tar.gz
  • Upload date:
  • Size: 59.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.7

File hashes

Hashes for queryframe-0.1.1.tar.gz
Algorithm Hash digest
SHA256 8689598172d892615d1013a7fe89e25f34aacfd27ba97c14484af7ab8036a0bf
MD5 70ab29bc11f1747a8492ba7153c6512e
BLAKE2b-256 12ea5703e3a0c62bae23583bf21d07ceb17850c074af4fcb49d2726b0218521b

See more details on using hashes here.

File details

Details for the file queryframe-0.1.1-py3-none-any.whl.

File metadata

  • Download URL: queryframe-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 52.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.7

File hashes

Hashes for queryframe-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 2748e51f2137dcb10cc9d20495d5ab6345f5c4c56155e8713da4fe5a8aef0028
MD5 68701c3762aa384526e9d7e9590b7a0b
BLAKE2b-256 e06be6c868d512d9823a48300ee7d1af83958aa17432f553bd8a9a85de52da91

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page