← guides

Go for Data Science

From Prototype to Production · Updated May 2026

You're running a Flask server with a trained model. Production load hits. Memory spikes. The GIL locks. You add more pods. Now you're managing Python dependencies across 20 containers.

Go solves the deployment problem Python created. Train in Python (PyTorch, scikit-learn, Jupyter). Deploy in Go (single binary, 10MB RAM, no interpreter). Read Parquet with Apache Arrow. Serve models via ONNX. Ship one file that runs everywhere.

Why Go for Data Science

The Problem Python Solves Well

Python dominates data science with 92% of the ML market (2026). NumPy, pandas, scikit-learn, PyTorch — the ecosystem is mature, documented, and battle-tested. Jupyter notebooks accelerate exploration. Pre-trained models ship with Python APIs.

For training and exploration, Python wins.

The Problem Python Doesn't Solve

You've trained a model in Python. Now you need to serve it:

You scale horizontally. Now you're managing 20 pods, each running a full Python stack.

What Go Fixes

Deployment

  • Single binary: go build produces one executable with no dependencies
  • Container size: 10-50MB Docker images (FROM scratch works)
  • Deployment: Copy one file. Run it. No interpreter, no packages, no environment.

Performance

  • Memory footprint: 10-50MB per service
  • Cold start: <100ms (no import overhead)
  • Native concurrency: Goroutines handle thousands of concurrent requests

Go is 6x faster than Python on average for CPU-bound tasks (Computer Language Benchmarks Game, 2026). For I/O-bound operations, the gap narrows to 1.4x. For pure computation (inference, numerical processing), the gap reaches 29x.

The Tradeoff

Python has pandas, NumPy, scikit-learn, PyTorch. Go has Gota (less mature than pandas), Gonum (less extensive than NumPy), GoLearn (smaller than scikit-learn).

You don't choose one. You use Python for training, Go for serving. Export models to ONNX. Load them in Go. Ship a single binary.

Quick Win: Deploy a Parquet Reader in 5 Minutes

You have Parquet files in S3. You need to serve aggregated stats via HTTP. Python works, but you're tired of dependency management.

Here's the Go version that compiles to one binary:

package main import ( "fmt" "log" "net/http" "os" "github.com/apache/arrow/go/v18/arrow/memory" "github.com/apache/arrow/go/v18/parquet/pqarrow" ) func main() { http.HandleFunc("/stats", func(w http.ResponseWriter, r *http.Request) { f, err := os.Open("data.parquet") if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } defer f.Close() reader, err := pqarrow.NewFileReader(f, pqarrow.ArrowReadProperties{}, memory.DefaultAllocator) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } table, err := reader.ReadTable(r.Context()) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } defer table.Release() fmt.Fprintf(w, "Rows: %d\nCols: %d\n", table.NumRows(), table.NumCols()) }) log.Println("Listening on :8080") log.Fatal(http.ListenAndServe(":8080", nil)) }

Build and deploy

go mod init parquet-server go get github.com/apache/arrow/go/v18/arrow go get github.com/apache/arrow/go/v18/parquet go build -o parquet-server ./parquet-server

Docker (10MB image)

FROM golang:1.24 AS builder WORKDIR /app COPY . . RUN go mod download RUN CGO_ENABLED=0 go build -o parquet-server FROM scratch COPY --from=builder /app/parquet-server /parquet-server COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ ENTRYPOINT ["/parquet-server"]

The Python equivalent (Flask + pandas + pyarrow) produces a 600MB image. The Go version is 10MB.

The Stack: Core Libraries for Go Data Science

1. Apache Arrow Go — Data Interchange

16,780 stars (entire Arrow project) Last commit: 2026-05-25

Purpose: Columnar in-memory data structures, Parquet I/O, zero-copy interop

Use cases:

2. Gota — DataFrames

3,200 stars (est.) Last commit: 2024-01 (15 months stale)

Purpose: pandas-like DataFrame API for data manipulation

Maintenance warning: Gota hasn't been updated in over a year. Evaluate whether this reflects stability or abandonment before using in production.

3. Gonum — Numerical Computing

8,371 stars

Purpose: Linear algebra, statistics, optimization

Use cases: Matrix operations, statistical functions, optimization, plotting

Performance note: Gonum uses pure Go implementations by default. For maximum performance on large matrices, build with CGo-enabled BLAS/LAPACK bindings (adds C dependency).

4. GoLearn — Machine Learning

9,440 stars Last commit: 2024-01-15 (15 months stale)

Purpose: Scikit-learn-style ML algorithms

Algorithms: Classification (KNN, Naive Bayes, Random Forest), Clustering (DBSCAN), Dimensionality reduction (PCA)

Maintenance concern: Same as Gota — 15 months without updates. For production ML, consider training in Python and serving via ONNX.

5. go-echarts — Interactive Visualization

7,613 stars Last commit: 2026-05-11 (actively maintained)

Purpose: Generate interactive HTML charts (via Apache ECharts)

Chart types: 25+ including line, bar, scatter, heatmap, 3D, geo maps

Use case: Web dashboards served by Go HTTP servers. The output is interactive HTML with JavaScript-powered tooltips and zooming.

Patterns: Production-Ready Data Pipelines

Pattern 1: Train in Python, Serve in Go (via ONNX)

Problem: You need scikit-learn's rich algorithm library but want Go's deployment simplicity.

Solution: Train in Python, export to ONNX, load in Go.

Python side (training)

from sklearn.ensemble import RandomForestClassifier from skl2onnx import convert_sklearn from skl2onnx.common.data_types import FloatTensorType # Train model X = [[0, 0], [1, 1], [2, 2], [3, 3]] y = [0, 0, 1, 1] clf = RandomForestClassifier() clf.fit(X, y) # Export to ONNX initial_type = [('float_input', FloatTensorType([None, 2]))] onnx_model = convert_sklearn(clf, initial_types=initial_type) with open("model.onnx", "wb") as f: f.write(onnx_model.SerializeToString())

Go side (serving)

import ( "encoding/json" "log" "net/http" ort "github.com/yalue/onnxruntime_go" ) func main() { ort.SetSharedLibraryPath("onnxruntime.so") ort.InitializeEnvironment() defer ort.DestroyEnvironment() session, _ := ort.NewSession[float32]("model.onnx", nil) defer session.Destroy() http.HandleFunc("/predict", func(w http.ResponseWriter, r *http.Request) { // Parse input, run inference, return result }) log.Fatal(http.ListenAndServe(":8080", nil)) }

Deployment tradeoff: ONNX Runtime requires libonnxruntime.so as a shared library dependency. This contradicts the "single binary" promise—you'll need the .so file alongside your Go binary. For truly dependency-free deployment, train simpler models and implement inference in pure Go, or export model weights and reimplement the forward pass.

Pattern 2: Parallel Parquet Processing

Problem: Process Parquet files in parallel, aggregate statistics, write results to database.

Solution: Use Apache Arrow for efficient columnar processing, goroutines for parallelism.

See the full guide for complete code examples.

Pattern 3: Real-Time Dashboard with go-echarts

Problem: Build a web dashboard showing live metrics from a time-series database.

Solution: HTTP server in Go, generate charts with go-echarts, serve HTML.

Deployment:

Container: 8MB Docker image, <10MB RAM usage, sub-100ms cold start

When to Switch From Python to Go

Signals That You Need Go

Signals That Python Is Still Right

The Hybrid Solution

Most teams end up here:

Communication layer: ONNX models, Parquet files, Arrow IPC, gRPC APIs, message queues

Not recommended: Pickle files for interchange (Python-specific, unsafe deserialization). Use ONNX for models, Parquet/Arrow for data.

Troubleshooting Common Gotchas

1. Gota DataFrames Are Slow

Symptom: Filtering 1M rows takes 10+ seconds

Cause: Gota is pure Go, not optimized like pandas' C/Cython backend

Fix: Use Apache Arrow directly for columnar operations, or write custom Go code with parallel processing

2. GoLearn Models Underperform scikit-learn

Symptom: Same algorithm, worse accuracy/performance in GoLearn

Cause: Smaller algorithm library, less tuned implementations

Fix: Train in Python (scikit-learn, XGBoost), export to ONNX, serve in Go

3. CGo Dependency Hell

Symptom: go build works locally, fails in Docker (missing C libraries)

Cause: ONNX runtime, some Gonum builds, TensorFlow bindings use CGo

Fix: Accept the tradeoff or avoid CGo entirely

Single binary promise: Only achievable with pure Go (no CGo). ONNX, TensorFlow, optimized BLAS all require shared libraries.

4. Memory Leaks with Arrow Tables

Symptom: Memory usage grows over time when reading Parquet files

Cause: Arrow uses manual memory management (C++ under the hood), forgetting Release() leaks

Fix: Always defer table.Release() immediately after loading

Resources

Essential Libraries

Performance Data

Deployment Guides

When to Use This Stack

Use Go for data science when:

Stay in Python when:

Most production systems end up hybrid: Python for training, Go for serving.