dataframe

package
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Jun 12, 2026 License: Apache-2.0 Imports: 24 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func FileSchema

func FileSchema(f FileScan) (*arrow.Schema, error)

FileSchema opens the file briefly to read its inferred Arrow schema without draining the data. It is used at lazy-plan build time so a file-backed ScanNode can be bound (the binder needs a schema) without an eager full read.

The CSV inferring reader only knows its schema after the first batch is read, so when Schema() is nil up front we advance once to trigger inference, then release. This reads at most one chunk — not the whole file.

func WriteCSV

func WriteCSV(df *DataFrame, path string, opts ...CSVOption) error

func WriteParquet

func WriteParquet(df *DataFrame, path string, opts ...ParquetOption) error

Types

type CSVOption

type CSVOption func(*CSVOptions)

func WithCSVAllowNullable

func WithCSVAllowNullable(value bool) CSVOption

func WithCSVBoolWriter

func WithCSVBoolWriter(writer func(bool) string) CSVOption

func WithCSVCRLF

func WithCSVCRLF(value bool) CSVOption

func WithCSVChunkSize

func WithCSVChunkSize(value int) CSVOption

func WithCSVColumnTypes

func WithCSVColumnTypes(types map[string]arrow.DataType) CSVOption

func WithCSVComma

func WithCSVComma(comma rune) CSVOption

func WithCSVComment

func WithCSVComment(comment rune) CSVOption

func WithCSVHeader

func WithCSVHeader(value bool) CSVOption

func WithCSVIncludeColumns

func WithCSVIncludeColumns(columns []string) CSVOption

func WithCSVLazyQuotes

func WithCSVLazyQuotes(value bool) CSVOption

func WithCSVNullValue

func WithCSVNullValue(value string) CSVOption

func WithCSVNullValues

func WithCSVNullValues(values []string) CSVOption

func WithCSVStringsReplacer

func WithCSVStringsReplacer(replacer *strings.Replacer) CSVOption

type CSVOptions

type CSVOptions struct {
	HasHeader       bool
	ChunkSize       int
	NullValues      []string
	NullValue       string
	ColumnTypes     map[string]arrow.DataType
	IncludeColumns  []string
	Comma           rune
	Comment         rune
	LazyQuotes      bool
	StringsReplacer *strings.Replacer
	UseCRLF         bool
	BoolWriter      func(bool) string
	AllowNullable   bool
}

func DefaultCSVOptions

func DefaultCSVOptions() CSVOptions

type ChunkedColumn

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

func NewChunkedColumn

func NewChunkedColumn(chunked *arrow.Chunked) *ChunkedColumn

func (*ChunkedColumn) Chunked

func (c *ChunkedColumn) Chunked() *arrow.Chunked

func (*ChunkedColumn) DataType

func (c *ChunkedColumn) DataType() arrow.DataType

func (*ChunkedColumn) Len

func (c *ChunkedColumn) Len() int64

type Column

type Column interface {
	Len() int64
	DataType() arrow.DataType
	Chunked() *arrow.Chunked
}

type DataFrame

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

func Concat

func Concat(dfs ...*DataFrame) (*DataFrame, error)

Concat stacks DataFrames vertically (row-wise). Every input must share the same column names, order, and types. The result's columns hold the concatenation of each input's chunks, so it is zero-copy and preserves chunk boundaries. Inputs are unchanged.

func FromRecordBatches

func FromRecordBatches(arrSchema *arrow.Schema, records []arrow.Record) (*DataFrame, error)

func FromRecordBatchesWithOptions

func FromRecordBatchesWithOptions(arrSchema *arrow.Schema, records []arrow.Record, options RecordBatchOptions) (*DataFrame, error)

func New

func New(series []*Series) (*DataFrame, error)

func NewDataFrame

func NewDataFrame(s *schema.Schema, cols []Series) (*DataFrame, error)

func ReadCSV

func ReadCSV(path string, opts ...CSVOption) (*DataFrame, error)

func ReadParquet

func ReadParquet(path string, opts ...ParquetOption) (*DataFrame, error)

func (*DataFrame) ChunkSizes

func (df *DataFrame) ChunkSizes() []int64

ChunkSizes returns the row count of each chunk in the canonical chunk layout, in row order. The layout is the coarsest partition of rows whose boundaries respect every column's chunk boundaries, so each segment lies within a single chunk of every column and can be materialized as one record batch without crossing a chunk seam. An empty DataFrame returns nil.

func (*DataFrame) Column

func (df *DataFrame) Column(name string) (Series, bool)

func (*DataFrame) Columns

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

Columns returns the column names in schema order.

func (*DataFrame) Count

func (df *DataFrame) Count(name string) (int64, error)

Count returns the number of non-null values in the named column.

func (*DataFrame) Drop

func (df *DataFrame) Drop(names ...string) (*DataFrame, error)

Drop returns a new DataFrame without the named columns, preserving the order of the remaining columns. It is an error to drop an unknown column.

func (*DataFrame) Filter

func (df *DataFrame) Filter(ctx context.Context, predicate expr.Expr) (*DataFrame, error)

Filter returns a new DataFrame containing only the rows for which predicate evaluates to true. predicate must be a boolean expression over the frame's columns; rows where it evaluates to null are dropped. The receiver is unchanged — surviving rows are copied into freshly-allocated columns.

When compute.Parallelism() != 1 and the frame has more than one chunk, the per-chunk eval+filter work is fanned out across compute.Parallelism() goroutines (defaulting to GOMAXPROCS). For a single chunk or parallelism==1 the serial path is taken. ctx is checked between record batches so a cancelled context stops the scan early.

func (*DataFrame) GroupBy

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

GroupBy begins a grouped aggregation over the named key columns.

func (*DataFrame) HStack

func (df *DataFrame) HStack(others ...*DataFrame) (*DataFrame, error)

HStack appends the columns of others to the receiver, horizontally. Every frame must have the same number of rows, and column names must stay unique across the union. Columns are shared (zero-copy); the receiver is unchanged.

func (*DataFrame) Head

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

Head returns a new DataFrame containing the first n rows across all chunks.

n is clamped to [0, NumRows]: a non-positive n yields an empty DataFrame that keeps the schema, and an n larger than the row count yields all rows. The result shares Arrow buffers with the receiver (zero-copy slices); the receiver is left unchanged.

func (*DataFrame) Join

func (df *DataFrame) Join(ctx context.Context, other *DataFrame, on string, how string) (*DataFrame, error)

Join combines df (left) with other (right) on the shared key column on, returning a new DataFrame. how selects the join kind:

  • "inner": emit only rows whose key appears in both frames.
  • "left": emit every left row; right columns are null where no key matches.

Output columns are: all left columns in order, then all non-key right columns in order. The right key column is dropped (it is redundant with the left key). A non-key right column whose name collides with a left column is renamed with a "_right" suffix (Polars default).

Rows are gathered with compute.Take, so the key and all value columns must be of a type Take supports. ctx is checked during the probe phase so a cancelled context stops the join early. The receiver and other are unchanged.

func (*DataFrame) Lazy

func (df *DataFrame) Lazy() *LazyFrame

func (*DataFrame) Limit

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

Limit returns a new DataFrame with at most the first n rows, in row order across chunks. It is the eager-operation spelling of Head: n is clamped to [0, NumRows] and the result shares Arrow buffers with the receiver.

func (*DataFrame) Max

func (df *DataFrame) Max(name string) (any, error)

Max returns the maximum non-null value of the named column, boxed in the column's element type. It is nil when the column is empty or all-null.

func (*DataFrame) Mean

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

Mean returns the arithmetic mean of the named column's non-null values. It is an error to take the mean of an empty or all-null column.

func (*DataFrame) Min

func (df *DataFrame) Min(name string) (any, error)

Min returns the minimum non-null value of the named column, boxed in the column's element type. It is nil when the column is empty or all-null.

func (*DataFrame) NumChunks

func (df *DataFrame) NumChunks() int

NumChunks reports the number of chunks in the DataFrame's canonical chunk layout. When every column shares the same chunk boundaries this is just that shared chunk count; when columns are chunked differently it is the number of aligned row segments (see ChunkSizes).

func (*DataFrame) NumCols

func (df *DataFrame) NumCols() int

NumCols is the number of columns in the DataFrame.

func (*DataFrame) NumRows

func (df *DataFrame) NumRows() int64

NumRows is the logical row count of the DataFrame across all chunks.

func (*DataFrame) Plan

func (df *DataFrame) Plan() *plan.LogicalPlan

func (*DataFrame) Rechunk

func (df *DataFrame) Rechunk() *DataFrame

Rechunk returns a new DataFrame in which every column is coalesced into a single contiguous chunk. The receiver is left unchanged; the result owns its own (copied) Arrow buffers.

func (*DataFrame) RechunkIfNeeded

func (df *DataFrame) RechunkIfNeeded() *DataFrame

RechunkIfNeeded coalesces the DataFrame only when ShouldRechunk reports that its columns are misaligned; otherwise it returns the receiver unchanged.

func (*DataFrame) RechunkToRows

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

RechunkToRows returns a new DataFrame whose columns are repartitioned into uniform chunks of n rows each (the final chunk may be smaller). A non-positive n coalesces to a single chunk. The receiver is left unchanged.

func (*DataFrame) Rename

func (df *DataFrame) Rename(old, new string) (*DataFrame, error)

Rename returns a new DataFrame with the column named old renamed to new, keeping column order and sharing buffers. It is an error if old is absent, if new is empty, or if new collides with another existing column.

func (*DataFrame) Schema

func (df *DataFrame) Schema() *schema.Schema

func (*DataFrame) Select

func (df *DataFrame) Select(names ...string) (*DataFrame, error)

Select returns a new DataFrame containing only the named columns, in the given order. Columns share Arrow buffers with the receiver (zero-copy); the receiver is unchanged. It is an error to name an unknown or duplicate column.

func (*DataFrame) ShouldRechunk

func (df *DataFrame) ShouldRechunk() bool

ShouldRechunk reports whether the DataFrame's columns are misaligned, meaning at least one column's chunk boundaries differ from the canonical layout. Misaligned columns force per-segment slicing during batch materialization, so coalescing them first is usually worthwhile.

func (*DataFrame) Sort

func (df *DataFrame) Sort(ctx context.Context, keys ...expr.SortKey) (*DataFrame, error)

Sort returns a new DataFrame ordered by the given keys. Keys are applied lexicographically: the first key is most significant, ties are broken by the next, and so on. Each key independently controls direction (Desc) and null placement (NullsFirst); by default order is ascending with nulls last. Every column is reordered by the same permutation (via compute.Take) so rows stay aligned. The sort is stable and the receiver is unchanged.

ctx is checked after the sort permutation is computed and before the (poten- tially large) column gather, so a cancelled context short-circuits the work.

func (*DataFrame) String

func (df *DataFrame) String() string

func (*DataFrame) Sum

func (df *DataFrame) Sum(name string) (any, error)

Sum returns the sum of the named column's non-null values, boxed in the column's element type. It is nil when the column is empty or all-null.

func (*DataFrame) WithColumn

func (df *DataFrame) WithColumn(e expr.Expr) (*DataFrame, error)

WithColumn returns a new DataFrame with a column derived from evaluating e over each row. The output column name is taken, in order of precedence, from:

  • an .As("name") alias on the expression, or
  • the single root column the expression references (replaced in place).

If the expression references more than one column and carries no alias, an error is returned directing the caller to add .As(). The receiver is unchanged; e is evaluated one canonical chunk at a time and the per-segment results are reassembled into a single chunked column aligned to the frame's height.

type DataFrameExecutor

type DataFrameExecutor struct{}

DataFrameExecutor implements plan.Executor by delegating each physical operator to the eager DataFrame methods. It is the concrete bridge that lets the plan package drive execution without importing dataframe (which would be a cycle): plan defines the Executor interface, dataframe satisfies it, and LazyFrame.Collect injects an instance.

func (DataFrameExecutor) Aggregate

func (DataFrameExecutor) Aggregate(ctx context.Context, v any, groupKeys []string, aggs []expr.AggNode) (any, error)

func (DataFrameExecutor) Filter

func (DataFrameExecutor) Filter(ctx context.Context, v any, predicate expr.Expr) (any, error)

func (DataFrameExecutor) FilterStream

func (DataFrameExecutor) FilterStream(ctx context.Context, v any, predicate expr.Expr) (any, error)

func (DataFrameExecutor) Join

func (DataFrameExecutor) Join(ctx context.Context, left, right any, on string, how string) (any, error)

func (DataFrameExecutor) Limit

func (DataFrameExecutor) Limit(v any, n int64) (any, error)

func (DataFrameExecutor) LimitStream

func (DataFrameExecutor) LimitStream(v any, n int64) any

func (DataFrameExecutor) Project

func (DataFrameExecutor) Project(v any, cols []string) (any, error)

func (DataFrameExecutor) ProjectStream

func (DataFrameExecutor) ProjectStream(v any, cols []string) (any, error)

func (DataFrameExecutor) Scan

func (DataFrameExecutor) Scan(ctx context.Context, node *plan.ScanNode) (any, error)

Scan materializes the ScanNode's source DataFrame and honors the PushedLimit annotation by truncating during the scan. Other pushdown hints (filters, columns) are advisory and applied by the surviving operator nodes above.

func (DataFrameExecutor) ScanStream

func (DataFrameExecutor) ScanStream(ctx context.Context, node *plan.ScanNode) (any, error)

ScanStream opens a file-backed ScanNode as a streaming RecordReader, honoring the pushed column projection where the source supports it. PushedLimit is applied above by the streaming PhysLimit node and PushedFilters by the streaming PhysFilter node, so the scan itself only prunes columns.

func (DataFrameExecutor) Sort

func (DataFrameExecutor) Sort(ctx context.Context, v any, keys []expr.SortKey) (any, error)

func (DataFrameExecutor) StreamFromDataFrame

func (DataFrameExecutor) StreamFromDataFrame(v any) (any, error)

StreamFromDataFrame adapts a materialized DataFrame into a RecordReader so a pipeline-breaking operator's output (Sort, Aggregate, Join, WithColumn) can be streamed downstream.

func (DataFrameExecutor) WithColumn

func (DataFrameExecutor) WithColumn(v any, e expr.Expr) (any, error)

type FileFormat

type FileFormat int

FileFormat identifies the on-disk encoding of a file-backed scan.

const (
	// FileFormatCSV is a delimited text source read via the CSV ingestion seam.
	FileFormatCSV FileFormat = iota
	// FileFormatParquet is a Parquet source read via the Parquet ingestion seam.
	FileFormatParquet
)

type FileScan

type FileScan struct {
	Path    string
	Format  FileFormat
	CSV     ingest.CSVConfig
	Parquet ingest.ParquetConfig
	// AllowNullable controls schema nullability when (and only when) the scan is
	// materialized into a DataFrame; streaming readers pass batches through
	// unchanged.
	AllowNullable bool
}

FileScan is the opaque ScanNode.Handle for a file-backed (ScanSourceFile) scan. It carries everything the executor needs to open a streaming RecordReader for the file without first materializing it into a DataFrame. scan.LazyScanCSV / scan.LazyScanParquet construct it; the DataFrameExecutor opens it. It lives in package dataframe (not scan) because the executor that consumes it lives here and dataframe cannot import scan (that would close the scan -> dataframe -> plan -> ... cycle).

type GroupBy

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

GroupBy is a grouping over one or more key columns, finalized by Agg.

func (*GroupBy) Agg

func (gb *GroupBy) Agg(ctx context.Context, aggs ...expr.AggNode) (*DataFrame, error)

Agg reduces each group with the given aggregations and returns a new DataFrame whose columns are the key columns (in group-key order) followed by the aggregations (in argument order). Groups appear in first-appearance order. Each aggregation is computed once per value column via a single per-group fold (compute.GroupReduce). ctx is checked once group ids have been assigned (the single linear scan over rows), before the per-group reductions.

type LazyFrame

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

func NewLazyFileScan

func NewLazyFileScan(fs FileScan) *LazyFrame

NewLazyFileScan builds a LazyFrame whose source is a file-backed scan. The physical plan opens the file as a streaming RecordReader under CollectStream (no full materialization) and as an eager scan under Collect. It is the dataframe-side constructor behind scan.LazyScanCSV / scan.LazyScanParquet, which cannot build it directly because plan/schema wiring and the FileScan handle live here. The file's schema is read up front so the lazy plan can be bound; the data is not drained.

func (*LazyFrame) Collect

func (lf *LazyFrame) Collect(ctx context.Context) (*DataFrame, error)

Collect executes the lazy plan: bind, optimize, lower to a physical plan, and run it against the eager dataframe operators.

func (*LazyFrame) CollectStream

func (lf *LazyFrame) CollectStream(ctx context.Context) (RecordReader, error)

CollectStream executes the lazy plan in streaming mode: bind, optimize, lower, then run the physical plan batch-by-batch, returning a RecordReader instead of a materialized DataFrame. Streamable operators (file scan, Filter, Project, Limit) transform one batch at a time, so a filter/limit pipeline over a file scan never loads the whole file. Pipeline-breaking operators (Sort, GroupBy, Join, WithColumn) materialize their input into a DataFrame first and then stream the result downstream. The returned reader is also a scan.RecordReader (identical method set), so file scans and streamed plans are interchangeable. The caller owns the reader and must Release it.

func (*LazyFrame) Filter

func (lf *LazyFrame) Filter(predicate expr.Expr) *LazyFrame

func (*LazyFrame) GroupBy

func (lf *LazyFrame) GroupBy(keys ...string) *LazyGroupBy

func (*LazyFrame) Join

func (lf *LazyFrame) Join(other *LazyFrame, on string, how string) *LazyFrame

func (*LazyFrame) Limit

func (lf *LazyFrame) Limit(n int64) *LazyFrame

func (*LazyFrame) Plan

func (lf *LazyFrame) Plan() (*plan.LogicalPlan, error)

func (*LazyFrame) Select

func (lf *LazyFrame) Select(cols ...string) *LazyFrame

func (*LazyFrame) Sort

func (lf *LazyFrame) Sort(keys ...expr.SortKey) *LazyFrame

func (*LazyFrame) WithColumn

func (lf *LazyFrame) WithColumn(e expr.Expr) *LazyFrame

type LazyGroupBy

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

LazyGroupBy is the intermediate handle returned by LazyFrame.GroupBy; call Agg to finish the grouped aggregation and return to a LazyFrame.

func (*LazyGroupBy) Agg

func (g *LazyGroupBy) Agg(aggs ...expr.AggNode) *LazyFrame

type ParquetOption

type ParquetOption func(*ParquetOptions)

func WithParquetAllocator

func WithParquetAllocator(allocator memory.Allocator) ParquetOption

func WithParquetAllowNullable

func WithParquetAllowNullable(value bool) ParquetOption

func WithParquetArrowReadProps

func WithParquetArrowReadProps(props pqarrow.ArrowReadProperties) ParquetOption

func WithParquetArrowWriterProps

func WithParquetArrowWriterProps(props pqarrow.ArrowWriterProperties) ParquetOption

func WithParquetReadOptions

func WithParquetReadOptions(options ...file.ReadOption) ParquetOption

func WithParquetWriterProps

func WithParquetWriterProps(props *parquet.WriterProperties) ParquetOption

type ParquetOptions

type ParquetOptions struct {
	AllowNullable    bool
	Allocator        memory.Allocator
	ReadOptions      []file.ReadOption
	ArrowReadProps   *pqarrow.ArrowReadProperties
	WriterProps      *parquet.WriterProperties
	ArrowWriterProps *pqarrow.ArrowWriterProperties
}

func DefaultParquetOptions

func DefaultParquetOptions() ParquetOptions

type RecordBatchIter

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

RecordBatchIter yields Arrow record batches from a DataFrame, one per segment of the canonical chunk layout (see DataFrame.ChunkSizes). Each segment lies within a single chunk of every column, so columns with differing chunk boundaries are materialized by zero-copy slicing rather than rejected.

func NewRecordBatchIter

func NewRecordBatchIter(df *DataFrame) (*RecordBatchIter, error)

func NewRecordBatchIterWithSchema

func NewRecordBatchIterWithSchema(df *DataFrame, arrSchema *arrow.Schema) (*RecordBatchIter, error)

func (*RecordBatchIter) Next

func (it *RecordBatchIter) Next() (arrow.Record, bool, error)

func (*RecordBatchIter) Schema

func (it *RecordBatchIter) Schema() *arrow.Schema

type RecordBatchOptions

type RecordBatchOptions struct {
	AllowNullable bool
}

type RecordReader

type RecordReader interface {
	Schema() *arrow.Schema
	Next() bool
	Record() arrow.Record
	Err() error
	Release()
}

RecordReader is the streaming contract for dataframe streaming output. It is structurally identical to scan.RecordReader, so a value of either type satisfies the other (both are interfaces with the same method set) — this is what makes file scans and lazy stream outputs interchangeable at the type level (CONTEXT.md) without dataframe importing scan, which would close the scan -> dataframe -> plan cycle. CollectStream returns this type; a caller that imports scan may treat it as a scan.RecordReader directly.

type Series

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

Series is the core column type in Cosma. Polars concept: Series ~= name + Column (chunked arrays).

func NewSeries

func NewSeries(name string, values any) (*Series, error)

func NewSeriesBinary

func NewSeriesBinary(name string, values [][]byte) (*Series, error)

func NewSeriesDayTimeInterval

func NewSeriesDayTimeInterval(name string, values []arrow.DayTimeInterval) (*Series, error)

func NewSeriesDecimal128

func NewSeriesDecimal128(name string, values []decimal128.Num, precision, scale int32) (*Series, error)

func NewSeriesDecimal256

func NewSeriesDecimal256(name string, values []decimal256.Num, precision, scale int32) (*Series, error)

func NewSeriesDuration

func NewSeriesDuration(name string, values []arrow.Duration, unit arrow.TimeUnit) (*Series, error)

func NewSeriesFixedSizeBinary

func NewSeriesFixedSizeBinary(name string, values [][]byte, byteWidth int) (*Series, error)

func NewSeriesFromArray

func NewSeriesFromArray(name string, arr arrow.Array) (*Series, error)

func NewSeriesFromChunked

func NewSeriesFromChunked(name string, chunked *arrow.Chunked) *Series

NewSeriesFromChunked creates a Series backed by chunked. The caller transfers ownership: do not call chunked.Release() after this call. The Series holds the only live reference. Column has no Release method, so adding Retain here without a corresponding Release path would permanently leak the array.

func NewSeriesFromColumn

func NewSeriesFromColumn(name string, col Column) *Series

func NewSeriesLargeBinary

func NewSeriesLargeBinary(name string, values [][]byte) (*Series, error)

func NewSeriesLargeUtf8

func NewSeriesLargeUtf8(name string, values []string) (*Series, error)

func NewSeriesMonthDayNanoInterval

func NewSeriesMonthDayNanoInterval(name string, values []arrow.MonthDayNanoInterval) (*Series, error)

func NewSeriesMonthInterval

func NewSeriesMonthInterval(name string, values []arrow.MonthInterval) (*Series, error)

func NewSeriesNull

func NewSeriesNull(name string, length int) (*Series, error)

func NewSeriesTime32

func NewSeriesTime32(name string, values []arrow.Time32, unit arrow.TimeUnit) (*Series, error)

func NewSeriesTime64

func NewSeriesTime64(name string, values []arrow.Time64, unit arrow.TimeUnit) (*Series, error)

func NewSeriesTimestamp

func NewSeriesTimestamp(name string, values []time.Time, unit arrow.TimeUnit, tz string) (*Series, error)

func NewSeriesTimestampValues

func NewSeriesTimestampValues(name string, values []arrow.Timestamp, unit arrow.TimeUnit, tz string) (*Series, error)

func (*Series) Chunked

func (s *Series) Chunked() *arrow.Chunked

func (*Series) DataType

func (s *Series) DataType() arrow.DataType

func (*Series) Len

func (s *Series) Len() int

Len is the logical length across chunks.

func (*Series) Name

func (s *Series) Name() string

func (*Series) String

func (s *Series) String() string

Jump to

Keyboard shortcuts

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