otters

package module
v1.0.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 25, 2025 License: MIT Imports: 9 Imported by: 0

README

🦦 Otters

Smooth, intelligent data processing for Go.

Otters is a high-performance DataFrame library for Go, inspired by Pandas but designed for Go's strengths: type safety, performance, and simplicity.

Go Version Go Report Card License

✨ Features

  • 🎯 Type-safe - Native Go types (int64, float64, string, bool, time)
  • High performance - Optimized for Go's strengths
  • 🛡️ Memory safe - No shared slices, proper error handling
  • 🐍 Pandas-like API - Familiar for data scientists
  • 🌊 Fluent interface - Chain operations naturally
  • 📁 CSV support - Read/write with automatic type inference
  • 🔍 Rich operations - Filter, sort, select, group, join
  • 📊 Built-in statistics - Sum, mean, std, describe, and more

🚀 Quick Start

Installation
go get github.com/datumbrain/otters
Performance Benchmarks
goos: darwin
goarch: arm64
pkg: github.com/datumbrain/otters
cpu: Apple M2 Pro
BenchmarkDataFrameOperations/Filter-10         	    4258	    283593 ns/op
BenchmarkDataFrameOperations/Sort-10           	    3748	    329145 ns/op
BenchmarkDataFrameOperations/GroupBy-10        	     780	   1544577 ns/op
BenchmarkDataFrameOperations/Statistics-10     	   12150	     99351 ns/op
PASS
ok  	github.com/datumbrain/otters	7.219s
Basic Usage
package main

import (
    "fmt"
    "log"
    "github.com/datumbrain/otters"
)

func main() {
    // Read CSV with automatic type inference
    df, err := otters.ReadCSV("sales.csv")
    if err != nil {
        log.Fatal(err)
    }

    // Chain operations like Pandas
    result := df.
        Filter("amount", ">", 1000).
        Select("region", "amount", "product").
        Sort("amount", false) // descending

    if err := result.Error(); err != nil {
        log.Fatal(err)
    }

    // Get insights
    totalSales, _ := result.Sum("amount")
    avgDeal, _ := result.Mean("amount")
    fmt.Printf("Total sales: $%.2f\n", totalSales)
    fmt.Printf("Average deal: $%.2f\n", avgDeal)
    fmt.Printf("Top deals: %d\n", result.Count())

    // Save results
    err = result.WriteCSV("top_sales.csv")
    if err != nil {
        log.Fatal(err)
    }
}

📊 Examples

Data Exploration
// Load and explore data
df, _ := otters.ReadCSV("employees.csv")

// Basic info
fmt.Println("Shape:", df.Shape())        // (1000, 5)
fmt.Println("Columns:", df.Columns())   // [name, age, department, salary, hired_date]

// Quick look
fmt.Println(df.Head(5))   // First 5 rows
fmt.Println(df.Tail(3))   // Last 3 rows
fmt.Println(df.Describe()) // Summary statistics
Filtering and Selection
// Multiple filters
high_earners := df.
    Filter("salary", ">", 75000).
    Filter("department", "==", "Engineering").
    Filter("age", "<=", 35)

// Select specific columns
summary := high_earners.Select("name", "salary", "age")

// Complex conditions
experienced := df.Filter("age", ">=", 30).Filter("salary", ">", 60000)
Sorting and Ranking
// Sort by single column
top_paid := df.Sort("salary", false) // descending

// Multi-column sort
ranked := df.SortBy(
    []string{"department", "salary"},
    []bool{true, false}, // department ascending, salary descending
)
Aggregations and Statistics
// Basic statistics
avgSalary, _ := df.Mean("salary")
totalPayroll, _ := df.Sum("salary")
minSalary, _ := df.Min("salary")
maxSalary, _ := df.Max("salary")
stdDev, _ := df.Std("salary")

fmt.Printf("Average salary: $%.2f\n", avgSalary)
fmt.Printf("Total payroll: $%.2f\n", totalPayroll)
fmt.Printf("Salary range: $%.2f - $%.2f\n", minSalary, maxSalary)
fmt.Printf("Std deviation: $%.2f\n", stdDev)

// Summary statistics for all numeric columns
summary, _ := df.Describe()
fmt.Println(summary)
Data Transformation
// Create new columns
df_with_bonus := df.Copy()
// Add 10% bonus calculation (implementation coming soon)

// Rename columns
clean_df := df.RenameColumn("hired_date", "start_date")

// Drop columns
essential := df.Drop("internal_id", "notes")

🏗️ API Reference

DataFrame Creation
// From CSV
df, err := otters.ReadCSV("data.csv")
df, err := otters.ReadCSVWithOptions("data.csv", otters.CSVOptions{
    HasHeader: true,
    Delimiter: ',',
    SkipRows:  1,
})

// From data
df, err := otters.NewDataFrameFromMap(map[string]interface{}{
    "name":   []string{"Alice", "Bob", "Carol"},
    "age":    []int64{25, 30, 35},
    "salary": []float64{50000, 60000, 70000},
})
Data Operations
// Filtering
df.Filter("column", "==", value)    // Equal
df.Filter("column", "!=", value)    // Not equal
df.Filter("column", ">", value)     // Greater than
df.Filter("column", ">=", value)    // Greater than or equal
df.Filter("column", "<", value)     // Less than
df.Filter("column", "<=", value)    // Less than or equal

// Selection
df.Select("col1", "col2", "col3")   // Select columns
df.Drop("col1", "col2")             // Drop columns

// Sorting
df.Sort("column", true)             // Single column, ascending
df.Sort("column", false)            // Single column, descending
df.SortBy([]string{"col1", "col2"}, []bool{true, false})
Statistics
// Basic stats
df.Count()                    // Number of rows
sum, _ := df.Sum("column")    // Sum of numeric column
mean, _ := df.Mean("column")  // Average of numeric column
min, _ := df.Min("column")    // Minimum value
max, _ := df.Max("column")    // Maximum value
std, _ := df.Std("column")    // Standard deviation

// Summary
summary, _ := df.Describe()   // Summary statistics for all numeric columns
I/O Operations
// CSV
df, err := otters.ReadCSV("input.csv")
err = df.WriteCSV("output.csv")

// With options
df, err := otters.ReadCSVWithOptions("data.csv", otters.CSVOptions{
    HasHeader: true,
    Delimiter: '\t',
    SkipRows:  2,
    MaxRows:   1000,
})

🎯 Design Philosophy

Pandas-Inspired, Go-Optimized

Otters brings the familiar Pandas API to Go while embracing Go's strengths:

  • Type Safety: No more runtime type errors
  • Performance: Optimized for Go's memory model
  • Simplicity: Clean, readable code
  • Error Handling: Proper Go error handling patterns
Memory Safety

Unlike many DataFrame libraries, Otters ensures:

  • No shared underlying slices
  • Proper deep copying when needed
  • No data races in concurrent usage
  • Explicit error handling, no panics
Performance First
  • Type-specific operations for maximum speed
  • Minimal allocations and copying
  • Efficient sorting and filtering algorithms
  • Memory-conscious design for large datasets

🔄 Pandas Migration

Coming from Pandas? Here's how Otters compares:

Pandas Otters Notes
pd.read_csv() otters.ReadCSV() Automatic type inference
df.head() df.Head(5) Must specify count
df[df.age > 25] df.Filter("age", ">", 25) Explicit syntax
df[['name', 'age']] df.Select("name", "age") Method-based selection
df.sort_values() df.Sort("column", true) Simple sort syntax
df.describe() df.Describe() Similar functionality

🚧 Roadmap

✅ MVP (Current)
  • Core DataFrame with type safety
  • CSV I/O with type inference
  • Basic operations (filter, select, sort)
  • Essential statistics
  • Fluent API with error handling
🔄 Coming Soon
  • GroupBy operations
  • Join operations (inner, left, right, outer)
  • More file formats (JSON, Parquet)
  • Advanced statistics
  • Data visualization helpers
  • Streaming operations for large files
🎯 Future
  • SQL-like query interface
  • Integration with popular Go ML libraries
  • Advanced time series operations
  • Distributed processing capabilities

🤝 Contributing

We welcome contributions! Please see our Contributing Guide for details.

Development Setup
git clone https://github.com/datumbrain/otters.git
cd otters
go mod tidy
go test ./...

📄 License

MIT License - see LICENSE file for details.

🙏 Acknowledgments

  • Inspired by Pandas for the API design
  • Built for the Go community with ❤️

Like an otter in water - smooth, efficient, and playful with data. 🦦

Documentation

Index

Constants

This section is empty.

Variables

View Source
var ErrColumnNotFound = &OtterError{
	Op:      "ColumnAccess",
	Message: "column not found",
	Row:     -1,
}

ErrColumnNotFound is returned when a requested column doesn't exist

View Source
var ErrEmptyDataFrame = &OtterError{
	Op:      "Operation",
	Message: "cannot operate on empty DataFrame",
	Row:     -1,
}

ErrEmptyDataFrame is returned when operating on an empty DataFrame

View Source
var ErrIndexOutOfRange = &OtterError{
	Op:      "IndexAccess",
	Message: "index out of range",
	Row:     -1,
}

ErrIndexOutOfRange is returned when accessing an invalid row index

View Source
var ErrInvalidOperation = &OtterError{
	Op:      "Operation",
	Message: "invalid operation",
	Row:     -1,
}

ErrInvalidOperation is returned for invalid operations

View Source
var ErrTypeMismatch = &OtterError{
	Op:      "TypeConversion",
	Message: "type mismatch",
	Row:     -1,
}

ErrTypeMismatch is returned when there's a type conversion error

Functions

func ConvertValue

func ConvertValue(value string, targetType ColumnType) (interface{}, error)

ConvertValue converts a string value to the specified type

func DetectDelimiter

func DetectDelimiter(filename string) (rune, error)

DetectDelimiter attempts to detect the delimiter used in a CSV file

func MustOperation

func MustOperation(op string, fn func() error)

MustOperation executes an operation and panics if it fails (for testing/debugging)

func SafeOperation

func SafeOperation(op string, fn func() error) (err error)

SafeOperation wraps a function to handle panics and convert them to errors

Types

type CSVInfo

type CSVInfo struct {
	Filename  string
	Delimiter rune
	Rows      int
	Columns   int
	HasHeader bool
}

CSVInfo contains information about a CSV file

func ValidateCSV

func ValidateCSV(filename string) (*CSVInfo, error)

ValidateCSV checks if a CSV file is valid and returns basic info

type CSVOptions

type CSVOptions struct {
	HasHeader bool // Whether the first row contains headers
	Delimiter rune // Field delimiter (default: ',')
	SkipRows  int  // Number of rows to skip at the beginning
	MaxRows   int  // Maximum number of rows to read (0 = unlimited)
}

CSVOptions provides options for CSV reading/writing

type ColumnType

type ColumnType int

ColumnType represents the data type of a column

const (
	StringType ColumnType = iota
	Int64Type
	Float64Type
	BoolType
	TimeType
)

func InferType

func InferType(values []string) ColumnType

InferType attempts to infer the best type for a slice of string values

func (ColumnType) String

func (ct ColumnType) String() string

String returns the string representation of a ColumnType

type DataFrame

type DataFrame struct {
	// contains filtered or unexported fields
}

DataFrame represents a collection of Series with aligned indices

func NewDataFrame

func NewDataFrame() *DataFrame

NewDataFrame creates a new empty DataFrame

func NewDataFrameFromMap

func NewDataFrameFromMap(data map[string]interface{}) (*DataFrame, error)

NewDataFrameFromMap creates a DataFrame from a map of column data

func NewDataFrameFromSeries

func NewDataFrameFromSeries(series ...*Series) (*DataFrame, error)

NewDataFrameFromSeries creates a DataFrame from a collection of Series

func ReadCSV

func ReadCSV(filename string) (*DataFrame, error)

ReadCSV reads a CSV file and returns a DataFrame with automatic type inference

func ReadCSVFromString

func ReadCSVFromString(data string) (*DataFrame, error)

ReadCSVFromString reads CSV data from a string

func ReadCSVFromStringWithOptions

func ReadCSVFromStringWithOptions(data string, options CSVOptions) (*DataFrame, error)

ReadCSVFromStringWithOptions reads CSV data from a string with options

func ReadCSVWithOptions

func ReadCSVWithOptions(filename string, options CSVOptions) (*DataFrame, error)

ReadCSVWithOptions reads a CSV file with custom options

func (*DataFrame) AddColumn

func (df *DataFrame) AddColumn(series *Series) *DataFrame

AddColumn adds a new Series as a column to the DataFrame

func (*DataFrame) Columns

func (df *DataFrame) Columns() []string

Columns returns the column names in their defined order

func (*DataFrame) Copy

func (df *DataFrame) Copy() *DataFrame

Copy creates a deep copy of the DataFrame

func (*DataFrame) Correlation

func (df *DataFrame) Correlation() (*DataFrame, error)

Correlation calculates correlation matrix for numeric columns

func (*DataFrame) Count

func (df *DataFrame) Count() int

Count returns the number of non-null rows in the DataFrame

func (*DataFrame) Describe

func (df *DataFrame) Describe() (*DataFrame, error)

Describe generates summary statistics for all numeric columns (like Pandas describe())

func (*DataFrame) Drop

func (df *DataFrame) Drop(columns ...string) *DataFrame

Drop creates a new DataFrame without the specified columns

func (*DataFrame) DropColumn

func (df *DataFrame) DropColumn(name string) *DataFrame

DropColumn removes a column from the DataFrame

func (*DataFrame) Error

func (df *DataFrame) Error() error

Error returns the current error state of the DataFrame

func (*DataFrame) Filter

func (df *DataFrame) Filter(column, operator string, value interface{}) *DataFrame

Filter creates a new DataFrame with rows that match the condition

func (*DataFrame) Get

func (df *DataFrame) Get(row int, column string) (interface{}, error)

Get returns the value at the specified row and column

func (*DataFrame) GetColumnType

func (df *DataFrame) GetColumnType(name string) (ColumnType, error)

GetColumnType returns the type of the specified column

func (*DataFrame) GetSeries

func (df *DataFrame) GetSeries(name string) (*Series, error)

GetSeries returns a copy of the specified column as a Series

func (*DataFrame) GroupBy

func (df *DataFrame) GroupBy(columns ...string) *GroupBy

GroupBy groups the DataFrame by the specified column(s)

func (*DataFrame) HasColumn

func (df *DataFrame) HasColumn(name string) bool

HasColumn returns true if the specified column exists

func (*DataFrame) Head

func (df *DataFrame) Head(n int) *DataFrame

Head returns the first n rows of the DataFrame

func (*DataFrame) Info

func (df *DataFrame) Info() string

Info returns basic information about the DataFrame

func (*DataFrame) IsEmpty

func (df *DataFrame) IsEmpty() bool

IsEmpty returns true if the DataFrame has no rows or columns

func (*DataFrame) Len

func (df *DataFrame) Len() int

Len returns the number of rows in the DataFrame

func (*DataFrame) Max

func (df *DataFrame) Max(column string) (interface{}, error)

Max finds the maximum value in a numeric column

func (*DataFrame) Mean

func (df *DataFrame) Mean(column string) (float64, error)

Mean calculates the average of a numeric column

func (*DataFrame) Median

func (df *DataFrame) Median(column string) (float64, error)

Median calculates the median of a numeric column

func (*DataFrame) Min

func (df *DataFrame) Min(column string) (interface{}, error)

Min finds the minimum value in a numeric column

func (*DataFrame) NumericSummary

func (df *DataFrame) NumericSummary(column string) (*NumericStats, error)

NumericSummary provides a quick summary of a numeric column

func (*DataFrame) Quantile

func (df *DataFrame) Quantile(column string, q float64) (float64, error)

Quantile calculates the specified quantile of a numeric column

func (*DataFrame) Query

func (df *DataFrame) Query(query string) *DataFrame

Query applies a simple query string to filter the DataFrame

func (*DataFrame) RenameColumn

func (df *DataFrame) RenameColumn(oldName, newName string) *DataFrame

RenameColumn renames a column in the DataFrame

func (*DataFrame) ResetIndex

func (df *DataFrame) ResetIndex() *DataFrame

Reset index (currently a no-op, but maintains Pandas compatibility)

func (*DataFrame) Select

func (df *DataFrame) Select(columns ...string) *DataFrame

Select creates a new DataFrame with only the specified columns

func (*DataFrame) Set

func (df *DataFrame) Set(row int, column string, value interface{}) error

Set updates the value at the specified row and column

func (*DataFrame) Shape

func (df *DataFrame) Shape() (int, int)

Shape returns the dimensions of the DataFrame (rows, columns)

func (*DataFrame) Sort

func (df *DataFrame) Sort(column string, ascending bool) *DataFrame

Sort creates a new DataFrame sorted by the specified column

func (*DataFrame) SortBy

func (df *DataFrame) SortBy(columns []string, ascending []bool) *DataFrame

SortBy creates a new DataFrame sorted by multiple columns

func (*DataFrame) Std

func (df *DataFrame) Std(column string) (float64, error)

Std calculates the standard deviation of a numeric column

func (*DataFrame) String

func (df *DataFrame) String() string

String returns a string representation of the DataFrame

func (*DataFrame) Sum

func (df *DataFrame) Sum(column string) (float64, error)

Sum calculates the sum of a numeric column

func (*DataFrame) Tail

func (df *DataFrame) Tail(n int) *DataFrame

Tail returns the last n rows of the DataFrame

func (*DataFrame) Unique

func (df *DataFrame) Unique(column string) ([]interface{}, error)

Unique returns unique values from a specified column

func (*DataFrame) ValueCounts

func (df *DataFrame) ValueCounts(column string) (*DataFrame, error)

ValueCounts returns the frequency of each unique value in a column

func (*DataFrame) Var

func (df *DataFrame) Var(column string) (float64, error)

Var calculates the variance of a numeric column

func (*DataFrame) Where

func (df *DataFrame) Where(column, operator string, value interface{}) *DataFrame

Where is an alias for Filter (Pandas compatibility)

func (*DataFrame) Width

func (df *DataFrame) Width() int

Width returns the number of columns in the DataFrame

func (*DataFrame) WriteCSV

func (df *DataFrame) WriteCSV(filename string) error

WriteCSV writes a DataFrame to a CSV file

func (*DataFrame) WriteCSVWithOptions

func (df *DataFrame) WriteCSVWithOptions(filename string, options CSVOptions) error

WriteCSVWithOptions writes a DataFrame to CSV with custom options

type GroupBy

type GroupBy struct {
	// contains filtered or unexported fields
}

GroupBy represents a grouped DataFrame for aggregation operations

func (*GroupBy) Count

func (gb *GroupBy) Count() (*DataFrame, error)

Count calculates the count for each group

func (*GroupBy) Max

func (gb *GroupBy) Max() (*DataFrame, error)

Max calculates the maximum for each group

func (*GroupBy) Mean

func (gb *GroupBy) Mean() (*DataFrame, error)

Mean calculates the average for each group

func (*GroupBy) Min

func (gb *GroupBy) Min() (*DataFrame, error)

Min calculates the minimum for each group

func (*GroupBy) Sum

func (gb *GroupBy) Sum() (*DataFrame, error)

Sum calculates the sum for each group

type NumericStats

type NumericStats struct {
	Column string
	Count  int
	Sum    float64
	Mean   float64
	Min    float64
	Max    float64
	Std    float64
	Median float64
}

NumericStats holds summary statistics for a numeric column

func (*NumericStats) String

func (ns *NumericStats) String() string

String returns a formatted string representation of NumericStats

type OtterError

type OtterError struct {
	Op      string // Operation that caused the error
	Column  string // Column name (if applicable)
	Row     int    // Row number (if applicable, -1 if not applicable)
	Message string // Human-readable error message
	Cause   error  // Underlying error (if any)
}

OtterError represents an error that occurred during DataFrame operations

func (*OtterError) Error

func (e *OtterError) Error() string

Error implements the error interface

func (*OtterError) Is

func (e *OtterError) Is(target error) bool

Is checks if the error matches a target error (for Go 1.13+ error handling)

func (*OtterError) Unwrap

func (e *OtterError) Unwrap() error

Unwrap returns the underlying error (for Go 1.13+ error wrapping)

type Series

type Series struct {
	Name   string      // Column name
	Type   ColumnType  // Data type
	Data   interface{} // Actual data: []string, []int64, []float64, []bool, []time.Time
	Length int         // Number of elements
}

Series represents a single column of data with a specific type

func NewSeries

func NewSeries(name string, data interface{}) (*Series, error)

NewSeries creates a new Series with the given name and data

func (*Series) Copy

func (s *Series) Copy() *Series

Copy creates a deep copy of the Series

func (*Series) Get

func (s *Series) Get(index int) (interface{}, error)

Get returns the value at the specified index

func (*Series) Set

func (s *Series) Set(index int, value interface{}) error

Set updates the value at the specified index

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL