Header menu logo Deedle

Quick Start

This document is a quick overview of the most important features of Deedle.

The first step is to install Deedle from NuGet. In F# Interactive, reference the library:

// #r "nuget: Deedle,4.0.1"

Creating series and frames

A data frame is a collection of series with unique column names (although these do not actually have to be strings). So, to create a data frame, we first need to create a series:

// Create from sequence of keys and sequence of values
let dates  = 
  [ DateTime(2013,1,1); 
    DateTime(2013,1,4); 
    DateTime(2013,1,8) ]
let values = 
  [ 10.0; 20.0; 30.0 ]
let first = Series(dates, values)

// Create from a single list of observations
Series.ofObservations
  [ DateTime(2013,1,1) => 10.0
    DateTime(2013,1,4) => 20.0
    DateTime(2013,1,8) => 30.0 ]
No value returned by any evaluator
// Shorter alternative to 'Series.ofObservations'
series [ 1 => 1.0; 2 => 2.0 ]

// Create series with implicit (ordinal) keys
Series.ofValues [ 10.0; 20.0; 30.0 ]
No value returned by any evaluator

Note that the series type is generic. Series<K, T> represents a series with keys of type K and values of type T. Let's now generate series with 10 day value range and random values:

/// Generate date range from 'first' with 'count' days
let dateRange (first:System.DateTime) count = (...)

/// Generate 'count' number of random doubles
let rand count = (...)

// A series with values for 10 days 
let second = Series(dateRange (DateTime(2013,1,1)) 10, rand 10)
No value returned by any evaluator

Now we can easily construct a data frame that has two columns - one representing the first series and another representing the second series:

let df1 = Frame(["first"; "second"], [first; second])
No value returned by any evaluator

The type representing a data frame has two generic parameters: Frame<TRowKey, TColumnKey>. The first parameter represents the type of row keys - this can be int if we do not give the keys explicitly or DateTime like in the example above. The second parameter is the type of column keys. This is typically string, but sometimes it is useful to create a transposed frame with dates as column keys. Because a data frame can contain heterogeneous data, there is no type of values - this needs to be specified when getting data from the data frame.

As the output shows, creating a frame automatically combines the indices of the two series (using "outer join" so the result has all the dates that appear in any of the series). The data frame now contains first column with some missing values.

You can also use the following nicer syntax and create frame from rows as well as individual values:

// The same as previously
let df2 = Frame.ofColumns ["first" => first; "second" => second]

// Transposed - here, rows are "first" and "second" & columns are dates
let df3 = Frame.ofRows ["first" => first; "second" => second]

// Create from individual observations (row * column * value)
let df4 = 
  [ ("Monday", "Tomas", 1.0); ("Tuesday", "Adam", 2.1)
    ("Tuesday", "Tomas", 4.0); ("Wednesday", "Tomas", -5.4) ]
  |> Frame.ofValues

Data frame can be also easily created from a collection of F# record types (or of any classes with public readable properties). The Frame.ofRecords function uses reflection to find the names and types of properties of a record and creates a data frame with the same structure.

// Assuming we have a record 'Price' and a collection 'values'
type Price = { Day : DateTime; Open : float }
let prices = 
  [ { Day = DateTime.Now; Open = 10.1 }
    { Day = DateTime.Now.AddDays(1.0); Open = 15.1 }
    { Day = DateTime.Now.AddDays(2.0); Open = 9.1 } ]

// Creates a data frame with columns 'Day' and 'Open'
let df5 = Frame.ofRecords prices

Finally, we can also load data frame from CSV:

let root = __SOURCE_DIRECTORY__ + "/data/"

let msftCsv = Frame.ReadCsv(root + "stocks/msft.csv")
let fbCsv = Frame.ReadCsv(root + "stocks/fb.csv")
No value returned by any evaluator

When loading the data, the data frame analyses the values and automatically converts them to the most appropriate type. However, no conversion is automatically performed for dates and times - the user needs to decide what is the desirable representation of dates (e.g. DateTime, DateTimeOffset or some custom type).

Specifying index and joining

Now we have fbCsv and msftCsv frames containing stock prices, but they are indexed with ordinal numbers. This means that we can get e.g. 4th price. However, we would like to align them using their dates (in case there are some values missing). This can be done by setting the row index to the "Date" column. Once we set the date as the index, we also need to order the index. The Yahoo Finance prices are ordered from the newest to the oldest, but our data-frame requires ascending ordering.

When a frame has ordered index, we can use additional functionality that will be needed later (for example, we can select sub-range by specifying dates that are not explicitly included in the index).

// Use the Date column as the index & order rows
let msftOrd = 
  msftCsv
  |> Frame.indexRowsDate "Date"
  |> Frame.sortRowsByKey

The indexRowsDate function uses a column of type DateTime as a new index. The library provides other functions for common types of indices (like indexRowsInt) and you can also use a generic function - when using the generic function, some type annotations may be needed, so it is better to use a specific function. Next, we sort the rows using another function from the Frame module. The module contains a large number of useful functions that you'll use all the time - it is a good idea to go through the list to get an idea of what is supported.

Now that we have properly indexed stock prices, we can create a new data frame that only has the data we're interested (Open & Close) prices and we add a new column that shows their difference:

// Create data frame with just Open and Close prices
let msft = msftOrd.Columns.[ ["Open"; "Close"] ]

// Add new column with the difference between Open & Close
msft?Difference <- msft?Open - msft?Close

// Do the same thing for Facebook
let fb = 
  fbCsv
  |> Frame.indexRowsDate "Date"
  |> Frame.sortRowsByKey
  |> Frame.sliceCols ["Open"; "Close"]
fb?Difference <- fb?Open - fb?Close

When selecting columns using f.Columns.[ .. ] it is possible to use a list of columns (as we did), a single column key, or a range (if the associated index is ordered). Then we use the df?Column <- (...) syntax to add a new column to the data frame. This is the only mutating operation that is supported on data frames - all other operations create a new data frame and return it as the result.

Next we would like to create a single data frame that contains (properly aligned) data for both Microsoft and Facebook. This is done using the Join method - but before we can do this, we need to rename their columns, because duplicate keys are not allowed:

// Change the column names so that they are unique
let msftNames = ["MsftOpen"; "MsftClose"; "MsftDiff"]
let msftRen = msft |> Frame.indexColsWith msftNames

let fbNames = ["FbOpen"; "FbClose"; "FbDiff"]
let fbRen = fb |> Frame.indexColsWith fbNames

// Outer join (align & fill with missing values)
let joinedOut = msftRen.Join(fbRen, kind=JoinKind.Outer)

// Inner join (remove rows with missing values)
let joinedIn = msftRen.Join(fbRen, kind=JoinKind.Inner)

Selecting values and slicing

The data frame provides two key properties that we can use to access the data. The Rows property returns a series containing individual rows (as a series) and Columns returns a series containing columns (as a series). We can then use various indexing and slicing operators on the series:

// Look for a row at a specific date
joinedIn.Rows.[DateTime(2013, 1, 2)]

// Get opening Facebook price for 2 Jan 2013
joinedIn.Rows.[DateTime(2013, 1, 2)]?FbOpen

In the previous example, we used an indexer with a single key. You can also specify multiple keys (using a list) or a range (using the slicing syntax):

// Get values for the first three days of January 2013
let janDates = [ for d in 2 .. 4 -> DateTime(2013, 1, d) ]
let jan234 = joinedIn.Rows.[janDates]

// Calculate mean of Open price for 3 days
jan234?MsftOpen |> Stats.mean

// Get values corresponding to entire January 2013
let jan = joinedIn.Rows.[DateTime(2013, 1, 1) .. DateTime(2013, 1, 31)] 
No value returned by any evaluator
// Calculate means over the period
jan?FbOpen |> Stats.mean
jan?MsftOpen |> Stats.mean

Using ordered time series

As already mentioned, if we have an ordered series or an ordered data frame, then we can leverage the ordering in a number of ways. In the previous example, slicing used lower and upper bounds rather than exact matching. Similarly, it is possible to get nearest smaller (or greater) element when using direct lookup.

For example, let's create two series with 10 values for 10 days. The daysSeries contains keys starting from DateTime.Today (12:00 AM) and obsSeries has dates with time component set to the current time:

let daysSeries = Series(dateRange DateTime.Today 10, rand 10)
let obsSeries = Series(dateRange DateTime.Now 10, rand 10)
No value returned by any evaluator
No value returned by any evaluator

The indexing operation written as daysSeries.[date] uses exact semantics so it will fail if the exact date is not available. When using Get method, we can provide an additional parameter to specify the required behaviour:

// This works - we get the value for DateTime.Today (12:00 AM)
daysSeries.Get(DateTime.Now, Lookup.ExactOrSmaller)

Projection and filtering

For filtering and projection, series provides Where and Select methods and corresponding Series.map and Series.filter functions. The following adds a new column that contains the name of the stock with greater price ("FB" or "MSFT"):

joinedOut?Comparison <- joinedOut |> Frame.mapRowValues (fun row -> 
  if row?MsftOpen > row?FbOpen then "MSFT" else "FB")

When projecting or filtering rows, we need to be careful about missing data. The row accessor row?MsftOpen reads the specified column (and converts it to float), but when the column is not available, it throws the MissingValueException exception. Projection functions such as mapRowValues automatically catch this exception and mark the corresponding series value as missing.

Now we can get the number of days when Microsoft stock prices were above Facebook:

joinedOut.GetColumn<string>("Comparison")
|> Series.filterValues ((=) "MSFT") |> Series.countValues

joinedOut.GetColumn<string>("Comparison")
|> Series.filterValues ((=) "FB") |> Series.countValues

Grouping and aggregation

As a last thing, we briefly look at grouping and aggregation. For more information about grouping of time series data, see the time series features tutorial and the data frame features contains more about grouping of unordered frames.

The following snippet groups rows by month and year:

let monthly =
  joinedIn
  |> Frame.groupRowsUsing (fun k _ -> DateTime(k.Year, k.Month, 1))

As you can see, we get back a frame that has a tuple DateTime * DateTime as the row key. This is treated in a special way as a hierarchical (or multi-level) index. A number of operations can be used on hierarchical indices. For example, we can get rows in a specified group (say, May 2013) and calculate means of columns in the group:

monthly.Rows.[DateTime(2013,5,1), *] |> Stats.mean

We can also use Frame.getNumericColumns in combination with Stats.levelMean to get means for all first-level groups:

monthly 
|> Frame.getNumericCols
|> Series.mapValues (Stats.levelMean fst)
|> Frame.ofColumns

Further reading

This quick-start covers the most common Deedle patterns. Dive deeper with:

namespace System
namespace Deedle
val dates: DateTime list
Multiple items
[<Struct>] type DateTime = new: date: DateOnly * time: TimeOnly -> unit + 16 overloads member Add: value: TimeSpan -> DateTime member AddDays: value: float -> DateTime member AddHours: value: float -> DateTime member AddMicroseconds: value: float -> DateTime member AddMilliseconds: value: float -> DateTime member AddMinutes: value: float -> DateTime member AddMonths: months: int -> DateTime member AddSeconds: value: float -> DateTime member AddTicks: value: int64 -> DateTime ...
<summary>Represents an instant in time, typically expressed as a date and time of day.</summary>

--------------------
DateTime ()
   (+0 other overloads)
DateTime(ticks: int64) : DateTime
   (+0 other overloads)
DateTime(date: DateOnly, time: TimeOnly) : DateTime
   (+0 other overloads)
DateTime(ticks: int64, kind: DateTimeKind) : DateTime
   (+0 other overloads)
DateTime(date: DateOnly, time: TimeOnly, kind: DateTimeKind) : DateTime
   (+0 other overloads)
DateTime(year: int, month: int, day: int) : DateTime
   (+0 other overloads)
DateTime(year: int, month: int, day: int, calendar: Globalization.Calendar) : DateTime
   (+0 other overloads)
DateTime(year: int, month: int, day: int, hour: int, minute: int, second: int) : DateTime
   (+0 other overloads)
DateTime(year: int, month: int, day: int, hour: int, minute: int, second: int, kind: DateTimeKind) : DateTime
   (+0 other overloads)
DateTime(year: int, month: int, day: int, hour: int, minute: int, second: int, calendar: Globalization.Calendar) : DateTime
   (+0 other overloads)
val values: float list
val first: Series<DateTime,float>
Multiple items
module Series from Deedle
<summary> The `Series` module provides an F#-friendly API for working with data and time series. The API follows the usual design for collection-processing in F#, so the functions work well with the pipelining (<c>|&gt;</c>) operator. For example, given a series with ages, we can use `Series.filterValues` to filter outliers and then `Stats.mean` to calculate the mean: ages |&gt; Series.filterValues (fun v -&gt; v &gt; 0.0 &amp;&amp; v &lt; 120.0) |&gt; Stats.mean The module provides comprehensive set of functions for working with series. The same API is also exposed using C#-friendly extension methods. In C#, the above snippet could be written as: [lang=csharp] ages .Where(kvp =&gt; kvp.Value &gt; 0.0 &amp;&amp; kvp.Value &lt; 120.0) .Mean() For more information about similar frame-manipulation functions, see the `Frame` module. For more information about C#-friendly extensions, see `SeriesExtensions`. The functions in the `Series` module are grouped in a number of categories and documented below. Accessing series data and lookup -------------------------------- Functions in this category provide access to the values in the series. - The term _observation_ is used for a key value pair in the series. - When working with a sorted series, it is possible to perform lookup using keys that are not present in the series - you can specify to search for the previous or next available value using _lookup behavior_. - Functions such as `get` and `getAll` have their counterparts `lookup` and `lookupAll` that let you specify lookup behavior. - For most of the functions that may fail, there is a `try[Foo]` variant that returns `None` instead of failing. - Functions with a name ending with `At` perform lookup based on the absolute integer offset (and ignore the keys of the series) Series transformations ---------------------- Functions in this category perform standard transformations on series including projections, filtering, taking some sub-series of the series, aggregating values using scanning and so on. Projection and filtering functions generally skip over missing values, but there are variants `filterAll` and `mapAll` that let you handle missing values explicitly. Keys can be transformed using `mapKeys`. When you do not need to consider the keys, and only care about values, use `filterValues` and `mapValues` (which is also aliased as the `$` operator). Series supports standard set of folding functions including `reduce` and `fold` (to reduce series values into a single value) as well as the `scan[All]` function, which can be used to fold values of a series into a series of intermeidate folding results. The functions `take[Last]` and `skip[Last]` can be used to take a sub-series of the original source series by skipping a specified number of elements. Note that this does not require an ordered series and it ignores the index - for index-based lookup use slicing, such as `series.[lo .. hi]`, instead. Finally the `shift` function can be used to obtain a series with values shifted by the specified offset. This can be used e.g. to get previous value for each key using `Series.shift 1 ts`. The `diff` function calculates difference from previous value using `ts - (Series.shift offs ts)`. Processing series with exceptions --------------------------------- The functions in this group can be used to write computations over series that may fail. They use the type <c>tryval&lt;'T&gt;</c> which is defined as a discriminated union with two cases: Success containing a value, or Error containing an exception. The function `tryMap` lets you create <c>Series&lt;'K, tryval&lt;'T&gt;&gt;</c> by mapping over values of an original series. You can then extract values using `tryValues`, which throws `AggregateException` if there were any errors. Functions `tryErrors` and `trySuccesses` give series containing only errors and successes. You can fill failed values with a constant using `fillErrorsWith`. Hierarchical index operations ----------------------------- When the key of a series is tuple, the elements of the tuple can be treated as multiple levels of a index. For example <c>Series&lt;'K1 * 'K2, 'V&gt;</c> has two levels with keys of types <c>'K1</c> and <c>'K2</c> respectively. The functions in this cateogry provide a way for aggregating values in the series at one of the levels. For example, given a series `input` indexed by two-element tuple, you can calculate mean for different first-level values as follows: input |&gt; applyLevel fst Stats.mean Note that the `Stats` module provides helpers for typical statistical operations, so the above could be written just as `input |&gt; Stats.levelMean fst`. Grouping, windowing and chunking -------------------------------- This category includes functions that group data from a series in some way. Two key concepts here are _window_ and _chunk_. Window refers to (overlapping) sliding windows over the input series while chunk refers to non-overlapping blocks of the series. The boundary behavior can be specified using the `Boundary` flags. The value `Skip` means that boundaries (incomplete windows or chunks) should be skipped. The value `AtBeginning` and `AtEnding` can be used to define at which side should the boundary be returned (or skipped). For chunking, `AtBeginning ||| Skip` makes sense and it means that the incomplete chunk at the beginning should be skipped (aligning the last chunk with the end). The behavior may be specified in a number of ways (which is reflected in the name): - `dist` - using an absolute distance between the keys - `while` - using a condition on the first and last key - `size` - by specifying the absolute size of the window/chunk The functions ending with `Into` take a function to be applied to the window/chunk. The functions `window`, `windowInto` and `chunk`, `chunkInto` are simplified versions that take a size. There is also `pairwise` function for sliding window of size two. Missing values -------------- This group of functions provides a way of working with missing values in a series. The `dropMissing` function drops all keys for which there are no values in the series. The `withMissingFrom` function lets you copy missing values from another series. The remaining functions provide different mechanism for filling the missing values. * `fillMissingWith` fills missing values with a specified constant * `fillMissingUsing` calls a specified function for every missing value * `fillMissing` and variants propagates values from previous/later keys Sorting and index manipulation ------------------------------ A series that is sorted by keys allows a number of additional operations (such as lookup using the `Lookp.ExactOrSmaller` lookup behavior). However, it is also possible to sort series based on the values - although the functions for manipulation with series do not guarantee that the order will be preserved. To sort series by keys, use `sortByKey`. Other sorting functions let you sort the series using a specified comparer function (`sortWith`), using a projection function (`sortBy`) and using the default comparison (`sort`). In addition, you can also replace the keys of a series with other keys using `indexWith` or with integers using `indexOrdinally`. To pick and reorder series values using to match a list of keys use `realign`. Sampling, resampling and advanced lookup ---------------------------------------- Given a (typically) time series sampling or resampling makes it possible to get time series with representative values at lower or uniform frequency. We use the following terminology: - `lookup` and `sample` functions find values at specified key; if a key is not available, they can look for value associated with the nearest smaller or the nearest greater key. - `resample` function aggregate values values into chunks based on a specified collection of keys (e.g. explicitly provided times), or based on some relation between keys (e.g. date times having the same date). - `resampleUniform` is similar to resampling, but we specify keys by providing functions that generate a uniform sequence of keys (e.g. days), the operation also fills value for days that have no corresponding observations in the input sequence. Joining, merging and zipping ---------------------------- Given two series, there are two ways to combine the values. If the keys in the series are not overlapping (or you want to throw away values from one or the other series), then you can use `merge` or `mergeUsing`. To merge more than 2 series efficiently, use the `mergeAll` function, which has been optimized for large number of series. If you want to align two series, you can use the _zipping_ operation. This aligns two series based on their keys and gives you tuples of values. The default behavior (`zip`) uses outer join and exact matching. For ordered series, you can specify other forms of key lookups (e.g. find the greatest smaller key) using `zipAlign`. functions ending with `Into` are generally easier to use as they call a specified function to turn the tuple (of possibly missing values) into a new value. For more complicated behaviors, it is often convenient to use joins on frames instead of working with series. Create two frames with single columns and then use the join operation. The result will be a frame with two columns (which is easier to use than series of tuples). </summary>
<category>Frame and series operations</category>


--------------------
type Series = static member ofNullables: values: Nullable<'a> seq -> Series<int,'a> (requires default constructor and value type and 'a :> ValueType) static member ofObservations: observations: ('a * 'b) seq -> Series<'a,'b> (requires equality) static member ofOptionalObservations: observations: ('K * 'a option) seq -> Series<'K,'a> (requires equality) static member ofValues: values: 'a seq -> Series<int,'a>

--------------------
type Series<'K,'V (requires equality)> = interface ISeriesFormattable interface IFsiFormattable interface ISeries<'K> new: index: IIndex<'K> * vector: IVector<'V> * vectorBuilder: IVectorBuilder * indexBuilder: IIndexBuilder -> Series<'K,'V> + 3 overloads member After: lowerExclusive: 'K -> Series<'K,'V> member Aggregate: aggregation: Aggregation<'K> * keySelector: Func<DataSegment<Series<'K,'V>>,'TNewKey> * valueSelector: Func<DataSegment<Series<'K,'V>>,OptionalValue<'R>> -> Series<'TNewKey,'R> (requires equality) + 1 overload member AsyncMaterialize: unit -> Async<Series<'K,'V>> member Before: upperExclusive: 'K -> Series<'K,'V> member Between: lowerInclusive: 'K * upperInclusive: 'K -> Series<'K,'V> member Compare: another: Series<'K,'V> -> Series<'K,Diff<'V>> ...
<summary> The type <c>Series&lt;K, V&gt;</c> represents a data series consisting of values `V` indexed by keys `K`. The keys of a series may or may not be ordered </summary>
<category>Core frame and series types</category>


--------------------
new: pairs: Collections.Generic.KeyValuePair<'K,'V> seq -> Series<'K,'V>
new: keys: 'K seq * values: 'V seq -> Series<'K,'V>
new: keys: 'K array * values: 'V array -> Series<'K,'V>
new: index: Indices.IIndex<'K> * vector: IVector<'V> * vectorBuilder: Vectors.IVectorBuilder * indexBuilder: Indices.IIndexBuilder -> Series<'K,'V>
static member Series.ofObservations: observations: ('a * 'b) seq -> Series<'a,'b> (requires equality)
val series: observations: ('a * 'b) seq -> Series<'a,'b> (requires equality)
<summary> Create a series from a sequence of key-value pairs that represent the observations of the series. This function can be used together with the `=&gt;` operator to create key-value pairs. </summary>
<example> // Creates a series with squares of numbers let sqs = series [ 1 =&gt; 1.0; 2 =&gt; 4.0; 3 =&gt; 9.0 ] </example>
static member Series.ofValues: values: 'a seq -> Series<int,'a>
val dateRange: first: DateTime -> count: int -> DateTime seq
 Generate date range from 'first' with 'count' days
val first: DateTime
val count: int
seq { for i in 0 .. (count - 1) -> first.AddDays(float i) }
val rand: count: int -> float seq
 Generate 'count' number of random doubles
let rnd = System.Random()
  seq { for i in 0 .. (count - 1) -> rnd.NextDouble() }
val second: Series<DateTime,float>
val df1: Frame<DateTime,string>
Multiple items
module Frame from Deedle
<summary> The `Frame` module provides an F#-friendly API for working with data frames. The module follows the usual desing for collection-processing in F#, so the functions work well with the pipelining operator (`|&gt;`). For example, given a frame with two columns representing prices, we can use `Frame.pctChange` to calculate daily returns like this: let df = frame [ "MSFT" =&gt; prices1; "AAPL" =&gt; prices2 ] let rets = df |&gt; Frame.pctChange 1 rets |&gt; Stats.mean Note that the `Stats.mean` operation is overloaded and works both on series (returning a number) and on frames (returning a series). You can also use `Frame.diff` if you need absolute differences rather than relative changes. The functions in this module are designed to be used from F#. For a C#-friendly API, see the `FrameExtensions` type. For working with individual series, see the `Series` module. The functions in the `Frame` module are grouped in a number of categories and documented below. Accessing frame data and lookup ------------------------------- Functions in this category provide access to the values in the fame. You can also add and remove columns from a frame (which both return a new value). - `addCol`, `replaceCol` and `dropCol` can be used to create a new data frame with a new column, by replacing an existing column with a new one, or by dropping an existing column - `cols` and `rows` return the columns or rows of a frame as a series containing objects; `getCols` and `getRows` return a generic series and cast the values to the type inferred from the context (columns or rows of incompatible types are skipped); `getNumericCols` returns columns of a type convertible to `float` for convenience. - You can get a specific row or column using `get[Col|Row]` or `lookup[Col|Row]` functions. The `lookup` variant lets you specify lookup behavior for key matching (e.g. find the nearest smaller key than the specified value). There are also `[try]get` and `[try]Lookup` functions that return optional values and functions returning entire observations (key together with the series). - `sliceCols` and `sliceRows` return a sub-frame containing only the specified columns or rows. Finally, `toArray2D` returns the frame data as a 2D array. Grouping, windowing and chunking -------------------------------- The basic grouping functions in this category can be used to group the rows of a data frame by a specified projection or column to create a frame with hierarchical index such as <c>Frame&lt;'K1 * 'K2, 'C&gt;</c>. The functions always aggregate rows, so if you want to group columns, you need to use `Frame.transpose` first. The function `groupRowsBy` groups rows by the value of a specified column. Use `groupRowsBy[Int|Float|String...]` if you want to specify the type of the column in an easier way than using type inference; `groupRowsUsing` groups rows using the specified _projection function_ and `groupRowsByIndex` projects the grouping key just from the row index. More advanced functions include: `aggregateRowsBy` which groups the rows by a specified sequence of columns and aggregates each group into a single value; `pivotTable` implements the pivoting operation [as documented in the tutorials](../frame.html#pivot). The `melt` and `unmelt` functions turn the data frame into a single data frame containing columns `Row`, `Column` and `Value` containing the data of the original frame; `unmelt` can be used to turn this representation back into an original frame. The `stack` and `unstack` functions implement pandas-style reshape operations. `stack` converts `Frame&lt;'R,'C&gt;` to a long-format `Frame&lt;'R*'C, string&gt;` where each cell becomes a row keyed by `(rowKey, colKey)` with a single `"Value"` column. `unstack` promotes the inner row-key level to column keys, producing `Frame&lt;'R1, 'C*'R2&gt;` from `Frame&lt;'R1*'R2,'C&gt;`. A simple windowing functions that are exposed for an entire frame operations are `window` and `windowInto`. For more complex windowing operations, you currently have to use `mapRows` or `mapCols` and apply windowing on individual series. Sorting and index manipulation ------------------------------ A frame is indexed by row keys and column keys. Both of these indices can be sorted (by the keys). A frame that is sorted allows a number of additional operations (such as lookup using the `Lookp.ExactOrSmaller` lookup behavior). The functions in this category provide ways for manipulating the indices. It is expected that most operations are done on rows and so more functions are available in a row-wise way. A frame can alwyas be transposed using `Frame.transpose`. Index operations: The existing row/column keys can be replaced by a sequence of new keys using the `indexColsWith` and `indexRowsWith` functions. Row keys can also be replaced by ordinal numbers using `indexRowsOrdinally`. The function `indexRows` uses the specified column of the original frame as the index. It removes the column from the resulting frame (to avoid this, use overloaded `IndexRows` method). This function infers the type of row keys from the context, so it is usually more convenient to use `indexRows[Date|String|Int|...]` functions. Finally, if you want to calculate the index value based on multiple columns of the row, you can use `indexRowsUsing`. Sorting frame rows: Frame rows can be sorted according to the value of a specified column using the `sortRows` function; `sortRowsBy` takes a projection function which lets you transform the value of a column (e.g. to project a part of the value). The functions `sortRowsByKey` and `sortColsByKey` sort the rows or columns using the default ordering on the key values. The result is a frame with ordered index. Expanding columns: When the frame contains a series with complex .NET objects such as F# records or C# classes, it can be useful to "expand" the column. This operation looks at the type of the objects, gets all properties of the objects (recursively) and generates multiple series representing the properties as columns. The function `expandCols` expands the specified columns while `expandAllCols` applies the expansion to all columns of the data frame. Frame transformations --------------------- Functions in this category perform standard transformations on data frames including projections, filtering, taking some sub-frame of the frame, aggregating values using scanning and so on. Projection and filtering functions such as `[map|filter][Cols|Rows]` call the specified function with the column or row key and an <c>ObjectSeries&lt;'K&gt;</c> representing the column or row. You can use functions ending with `Values` (such as `mapRowValues`) when you do not require the row key, but only the row series; `mapRowKeys` and `mapColKeys` can be used to transform the keys. You can use `reduceValues` to apply a custom reduction to values of columns. Other aggregations are available in the `Stats` module. You can also get a row with the greaterst or smallest value of a given column using `[min|max]RowBy`. The functions `take[Last]` and `skip[Last]` can be used to take a sub-frame of the original source frame by skipping a specified number of rows. Note that this does not require an ordered frame and it ignores the index - for index-based lookup use slicing, such as `df.Rows.[lo .. hi]`, instead. Finally the `shift` function can be used to obtain a frame with values shifted by the specified offset. This can be used e.g. to get previous value for each key using `Frame.shift 1 df`. The `diff` function calculates difference from previous value using `df - (Frame.shift offs df)`. Processing frames with exceptions --------------------------------- The functions in this group can be used to write computations over frames that may fail. They use the type <c>tryval&lt;'T&gt;</c> which is defined as a discriminated union with two cases: Success containing a value, or Error containing an exception. Using <c>tryval&lt;'T&gt;</c> as a value in a data frame is not generally recommended, because the type of values cannot be tracked in the type. For this reason, it is better to use <c>tryval&lt;'T&gt;</c> with individual series. However, `tryValues` and `fillErrorsWith` functions can be used to get values, or fill failed values inside an entire data frame. The `tryMapRows` function is more useful. It can be used to write a transformation that applies a computation (which may fail) to each row of a data frame. The resulting series is of type <c>Series&lt;'R, tryval&lt;'T&gt;&gt;</c> and can be processed using the <c>Series</c> module functions. Missing values -------------- This group of functions provides a way of working with missing values in a data frame. The category provides the following functions that can be used to fill missing values: * `fillMissingWith` fills missing values with a specified constant * `fillMissingUsing` calls a specified function for every missing value * `fillMissing` and variants propagates values from previous/later keys We use the terms _sparse_ and _dense_ to denote series that contain some missing values or do not contain any missing values, respectively. The functions `denseCols` and `denseRows` return a series that contains only dense columns or rows and all sparse rows or columns are replaced with a missing value. The `dropSparseCols` and `dropSparseRows` functions drop these missing values and return a frame with no missing values. Joining, merging and zipping ---------------------------- The simplest way to join two frames is to use the `join` operation which can be used to perform left, right, outer or inner join of two frames. When the row keys of the frames do not match exactly, you can use `joinAlign` which takes an additional parameter that specifies how to find matching key in left/right join (e.g. by taking the nearest smaller available key). Frames that do not contian overlapping values can be combined using `merge` (when combining just two frames) or using `mergeAll` (for larger number of frames). Tha latter is optimized to work well for a large number of data frames. Finally, frames with overlapping values can be combined using `zip`. It takes a function that is used to combine the overlapping values. A `zipAlign` function provides a variant with more flexible row key matching (as in `joinAlign`) Hierarchical index operations ----------------------------- A data frame has a hierarchical row index if the row index is formed by a tuple, such as <c>Frame&lt;'R1 * 'R2, 'C&gt;</c>. Frames of this kind are returned, for example, by the grouping functions such as <c>Frame.groupRowsBy</c>. The functions in this category provide ways for working with data frames that have hierarchical row keys. The functions <c>applyLevel</c> and <c>reduceLevel</c> can be used to reduce values according to one of the levels. The <c>applyLevel</c> function takes a reduction of type <c>Series&lt;'K, 'T&gt; -&gt; 'T</c> while <c>reduceLevel</c> reduces individual values using a function of type <c>'T -&gt; 'T -&gt; 'T</c>. The functions <c>nest</c> and <c>unnest</c> can be used to convert between frames with hierarchical indices (<c>Frame&lt;'K1 * 'K2, 'C&gt;</c>) and series of frames that represent individual groups (<c>Series&lt;'K1, Frame&lt;'K2, 'C&gt;&gt;</c>). The <c>nestBy</c> function can be used to perform group by operation and return the result as a series of frems. </summary>
<category>Frame and series operations</category>


--------------------
type Frame = static member ReadCsv: location: string * [<Optional>] hasHeaders: Nullable<bool> * [<Optional>] inferTypes: Nullable<bool> * [<Optional>] inferRows: Nullable<int> * [<Optional>] schema: string * [<Optional>] separators: string * [<Optional>] culture: string * [<Optional>] maxRows: Nullable<int> * [<Optional>] missingValues: string array * [<Optional>] preferOptions: bool * [<Optional>] encoding: Encoding -> Frame<int,string> + 1 overload static member ReadReader: reader: IDataReader -> Frame<int,string> static member CustomExpanders: Dictionary<Type,Func<obj,(string * Type * obj) seq>> with get static member NonExpandableInterfaces: ResizeArray<Type> with get static member NonExpandableTypes: HashSet<Type> with get
<summary> Provides static methods for creating frames, reading frame data from CSV files and database (via IDataReader). The type also provides global configuration for reflection-based expansion. </summary>
<category>Frame and series operations</category>


--------------------
type Frame<'TRowKey,'TColumnKey (requires equality and equality)> = interface IDynamicMetaObjectProvider interface INotifyCollectionChanged interface IFrameFormattable interface IFsiFormattable interface IFrame new: rowIndex: IIndex<'TRowKey> * columnIndex: IIndex<'TColumnKey> * data: IVector<IVector> * indexBuilder: IIndexBuilder * vectorBuilder: IVectorBuilder -> Frame<'TRowKey,'TColumnKey> + 1 overload member AddColumn: column: 'TColumnKey * series: 'V seq -> unit + 3 overloads member AggregateRowsBy: groupBy: 'TColumnKey seq * aggBy: 'TColumnKey seq * aggFunc: Func<Series<'TRowKey,'a>,'b> -> Frame<int,'TColumnKey> member Clone: unit -> Frame<'TRowKey,'TColumnKey> member ColumnApply: f: Func<Series<'TRowKey,'T>,ISeries<'TRowKey>> -> Frame<'TRowKey,'TColumnKey> + 1 overload ...
<summary> A frame is the key Deedle data structure (together with series). It represents a data table (think spreadsheet or CSV file) with multiple rows and columns. The frame consists of row index, column index and data. The indices are used for efficient lookup when accessing data by the row key `'TRowKey` or by the column key `'TColumnKey`. Deedle frames are optimized for the scenario when all values in a given column are of the same type (but types of different columns can differ). </summary>
<remarks><para>Joining, zipping and appending:</para><para> More info </para></remarks>
<category>Core frame and series types</category>


--------------------
new: names: 'TColumnKey seq * columns: ISeries<'TRowKey> seq -> Frame<'TRowKey,'TColumnKey>
new: rowIndex: Indices.IIndex<'TRowKey> * columnIndex: Indices.IIndex<'TColumnKey> * data: IVector<IVector> * indexBuilder: Indices.IIndexBuilder * vectorBuilder: Vectors.IVectorBuilder -> Frame<'TRowKey,'TColumnKey>
val df2: Frame<DateTime,string>
static member Frame.ofColumns: cols: Series<'C,#ISeries<'R>> -> Frame<'R,'C> (requires equality and equality)
static member Frame.ofColumns: cols: ('C * #ISeries<'R>) seq -> Frame<'R,'C> (requires equality and equality)
val df3: Frame<string,DateTime>
static member Frame.ofRows: rows: ('R * #ISeries<'C>) seq -> Frame<'R,'C> (requires equality and equality)
static member Frame.ofRows: rows: Series<'R,#ISeries<'C>> -> Frame<'R,'C> (requires equality and equality)
val df4: Frame<string,string>
static member Frame.ofValues: values: ('R * 'C * 'V) seq -> Frame<'R,'C> (requires equality and equality)
type Price = { Day: DateTime Open: float }
Multiple items
val float: value: 'T -> float (requires member op_Explicit)

--------------------
type float = Double

--------------------
type float<'Measure> = float
val prices: Price list
property DateTime.Now: DateTime with get
<summary>Gets a <see cref="T:System.DateTime" /> object that is set to the current date and time on this computer, expressed as the local time.</summary>
<returns>An object whose value is the current local date and time.</returns>
DateTime.AddDays(value: float) : DateTime
val df5: Frame<int,string>
static member Frame.ofRecords: series: Series<'K,'R> -> Frame<'K,string> (requires equality)
static member Frame.ofRecords: values: 'T seq -> Frame<int,string>
static member Frame.ofRecords: values: Collections.IEnumerable * indexCol: string -> Frame<'R,string> (requires equality)
val root: string
val msftCsv: Frame<int,string>
static member Frame.ReadCsv: reader: IO.TextReader * ?hasHeaders: bool * ?inferTypes: bool * ?inferRows: int * ?schema: string * ?separators: string * ?culture: string * ?maxRows: int * ?missingValues: string array * ?preferOptions: bool * ?typeResolver: (string -> string option) -> Frame<int,string>
static member Frame.ReadCsv: stream: IO.Stream * [<Runtime.InteropServices.Optional>] hasHeaders: Nullable<bool> * [<Runtime.InteropServices.Optional>] inferTypes: Nullable<bool> * [<Runtime.InteropServices.Optional>] inferRows: Nullable<int> * [<Runtime.InteropServices.Optional>] schema: string * [<Runtime.InteropServices.Optional>] separators: string * [<Runtime.InteropServices.Optional>] culture: string * [<Runtime.InteropServices.Optional>] maxRows: Nullable<int> * [<Runtime.InteropServices.Optional>] missingValues: string array * [<Runtime.InteropServices.Optional>] preferOptions: Nullable<bool> * [<Runtime.InteropServices.Optional>] encoding: Text.Encoding -> Frame<int,string>
static member Frame.ReadCsv: location: string * [<Runtime.InteropServices.Optional>] hasHeaders: Nullable<bool> * [<Runtime.InteropServices.Optional>] inferTypes: Nullable<bool> * [<Runtime.InteropServices.Optional>] inferRows: Nullable<int> * [<Runtime.InteropServices.Optional>] schema: string * [<Runtime.InteropServices.Optional>] separators: string * [<Runtime.InteropServices.Optional>] culture: string * [<Runtime.InteropServices.Optional>] maxRows: Nullable<int> * [<Runtime.InteropServices.Optional>] missingValues: string array * [<Runtime.InteropServices.Optional>] preferOptions: bool * [<Runtime.InteropServices.Optional>] encoding: Text.Encoding -> Frame<int,string>
static member Frame.ReadCsv: path: string * ?hasHeaders: bool * ?inferTypes: bool * ?inferRows: int * ?schema: string * ?separators: string * ?culture: string * ?maxRows: int * ?missingValues: string array * ?preferOptions: bool * ?typeResolver: (string -> string option) * ?encoding: Text.Encoding -> Frame<int,string>
static member Frame.ReadCsv: stream: IO.Stream * ?hasHeaders: bool * ?inferTypes: bool * ?inferRows: int * ?schema: string * ?separators: string * ?culture: string * ?maxRows: int * ?missingValues: string array * ?preferOptions: bool * ?typeResolver: (string -> string option) * ?encoding: Text.Encoding -> Frame<int,string>
static member Frame.ReadCsv: path: string * indexCol: string * ?hasHeaders: bool * ?inferTypes: bool * ?inferRows: int * ?schema: string * ?separators: string * ?culture: string * ?maxRows: int * ?missingValues: string array * ?preferOptions: bool * ?typeResolver: (string -> string option) * ?encoding: Text.Encoding -> Frame<'R,string> (requires equality)
val fbCsv: Frame<int,string>
val msftOrd: Frame<DateTime,string>
val sortRowsByKey: frame: Frame<'R,'C> -> Frame<'R,'C> (requires equality and equality)
<summary> Returns a data frame that contains the same data as the input, but whose rows are an ordered series. This allows using operations that are only available on indexed series such as alignment and inexact lookup. &lt;category&gt;Sorting and index manipulation&lt;/category&gt; </summary>
val msft: Frame<DateTime,string>
property Frame.Columns: ColumnSeries<DateTime,string> with get
<category>Accessors and slicing</category>
val fb: Frame<DateTime,string>
val sliceCols: columns: 'C seq -> frame: Frame<'R,'C> -> Frame<'R,'C> (requires equality and equality)
<summary> Returns a frame consisting of the specified columns from the original data frame. The function uses exact key matching semantics. &lt;category&gt;Accessing frame data and lookup&lt;/category&gt; </summary>
val msftNames: string list
val msftRen: Frame<DateTime,string>
val indexColsWith: keys: 'C2 seq -> frame: Frame<'R,'C1> -> Frame<'R,'C2> (requires equality and equality and equality)
<summary> Replace the column index of the frame with the provided sequence of column keys. The columns of the frame are assigned keys according to the provided order. The specified column is removed from the resulting frame. </summary>
<param name="frame">Source data frame whose column index are to be replaced.</param>
<param name="keys">A collection of new column keys.</param>
<category>Sorting and index manipulation</category>
val fbNames: string list
val fbRen: Frame<DateTime,string>
val joinedOut: Frame<DateTime,string>
member Frame.Join: otherFrame: Frame<'TRowKey,'TColumnKey> -> Frame<'TRowKey,'TColumnKey>
member Frame.Join: colKey: 'TColumnKey * series: Series<'TRowKey,'V> -> Frame<'TRowKey,'TColumnKey>
member Frame.Join: otherFrame: Frame<'TRowKey,'TColumnKey> * kind: JoinKind -> Frame<'TRowKey,'TColumnKey>
member Frame.Join: colKey: 'TColumnKey * series: Series<'TRowKey,'V> * kind: JoinKind -> Frame<'TRowKey,'TColumnKey>
member Frame.Join: otherFrame: Frame<'TRowKey,'TColumnKey> * kind: JoinKind * lookup: Lookup -> Frame<'TRowKey,'TColumnKey>
member Frame.Join: colKey: 'TColumnKey * series: Series<'TRowKey,'V> * kind: JoinKind * lookup: Lookup -> Frame<'TRowKey,'TColumnKey>
[<Struct>] type JoinKind = | Outer = 0 | Inner = 1 | Left = 2 | Right = 3
<summary> This enumeration specifies joining behavior for `Join` method provided by `Series` and `Frame`. Outer join unions the keys (and may introduce missing values), inner join takes the intersection of keys; left and right joins take the keys of the first or the second series/frame. </summary>
<category>Parameters and results of various operations</category>
JoinKind.Outer: JoinKind = 0
<summary> Combine the keys available in both structures, align the values that are available in both of them and mark the remaining values as missing. </summary>
val joinedIn: Frame<DateTime,string>
JoinKind.Inner: JoinKind = 1
<summary> Take the intersection of the keys available in both structures and align the values of the two structures. The resulting structure cannot contain missing values. </summary>
property Frame.Rows: RowSeries<DateTime,string> with get
<category>Accessors and slicing</category>
val janDates: DateTime list
val d: int
val jan234: Frame<DateTime,string>
type Stats = static member corr: series1: Series<'K,'V1> -> series2: Series<'K,'V2> -> float (requires equality) static member corrFrame: frame: Frame<'R,'C> -> Frame<'C,'C> (requires equality and equality) static member count: series: Series<'K,'V> -> int (requires equality) + 1 overload static member cov: series1: Series<'K,'V1> -> series2: Series<'K,'V2> -> float (requires equality) static member covFrame: frame: Frame<'R,'C> -> Frame<'C,'C> (requires equality and equality) static member describe: series: Series<'K,'V> -> Series<string,float> (requires equality and equality) + 1 overload static member expandingCount: series: Series<'K,'V> -> Series<'K,float> (requires equality) static member expandingKurt: series: Series<'K,'V> -> Series<'K,float> (requires equality) static member expandingMax: series: Series<'K,'V> -> Series<'K,float> (requires equality) static member expandingMean: series: Series<'K,'V> -> Series<'K,float> (requires equality) ...
static member Stats.mean: frame: Frame<'R,'C> -> Series<'C,float> (requires equality and equality)
static member Stats.mean: series: Series<'K,'V> -> float (requires equality)
val jan: Frame<DateTime,string>
val daysSeries: Series<DateTime,float>
property DateTime.Today: DateTime with get
<summary>Gets the current date.</summary>
<returns>An object that is set to today's date, with the time component set to 00:00:00.</returns>
val obsSeries: Series<DateTime,float>
member Series.Get: key: 'K -> 'V
member Series.Get: key: 'K * lookup: Lookup -> 'V
[<Struct>] type Lookup = | Exact = 1 | ExactOrGreater = 3 | ExactOrSmaller = 5 | Greater = 2 | Smaller = 4
<summary> Represents different behaviors of key lookup in series. For unordered series, the only available option is `Lookup.Exact` which finds the exact key - methods fail or return missing value if the key is not available in the index. For ordered series `Lookup.Greater` finds the first greater key (e.g. later date) with a value. `Lookup.Smaller` searches for the first smaller key. The options `Lookup.ExactOrGreater` and `Lookup.ExactOrSmaller` finds the exact key (if it is present) and otherwise search for the nearest larger or smaller key, respectively. </summary>
<category>Parameters and results of various operations</category>
Lookup.ExactOrSmaller: Lookup = 5
<summary> Lookup a value associated with the specified key or with the nearest smaller key that has a value available. Fails (or returns missing value) only when the specified key is smaller than all available keys. </summary>
type Comparison<'T (allows ref struct)> = new: object: obj * method: nativeint -> unit member BeginInvoke: x: 'T * y: 'T * callback: AsyncCallback * object: obj -> IAsyncResult member EndInvoke: result: IAsyncResult -> int member Invoke: x: 'T * y: 'T -> int
<summary>Represents the method that compares two objects of the same type.</summary>
<param name="x">The first object to compare.</param>
<param name="y">The second object to compare.</param>
<typeparam name="T">The type of the objects to compare.</typeparam>
<returns>A signed integer that indicates the relative values of <paramref name="x" /> and <paramref name="y" />, as shown in the following table. <list type="table"><listheader><term> Value</term><description> Meaning</description></listheader><item><term> Less than 0</term><description><paramref name="x" /> is less than <paramref name="y" />.</description></item><item><term> 0</term><description><paramref name="x" /> equals <paramref name="y" />.</description></item><item><term> Greater than 0</term><description><paramref name="x" /> is greater than <paramref name="y" />.</description></item></list></returns>
val mapRowValues: f: (ObjectSeries<'C> -> 'V) -> frame: Frame<'R,'C> -> Series<'R,'V> (requires equality and equality)
<summary> Builds a new data frame whose rows are the results of applying the specified function on the rows of the input data frame. The function is called with an object series that represents the row data (use `mapRows` if you need to access the row key). </summary>
<param name="frame">Input data frame to be transformed</param>
<param name="f">Function of one argument that defines the row mapping</param>
<category>Frame transformations</category>
val row: ObjectSeries<string>
member Frame.GetColumn<'R> : column: 'TColumnKey -> Series<'TRowKey,'R>
member Frame.GetColumn<'R> : column: 'TColumnKey * lookup: Lookup -> Series<'TRowKey,'R>
Multiple items
val string: value: 'T -> string

--------------------
type string = String
val filterValues: f: ('T -> bool) -> series: Series<'K,'T> -> Series<'K,'T> (requires equality)
<summary> Returns a new series containing only the elements for which the specified predicate returns `true`. The function skips over missing values and calls the predicate with just the value. See also `filterAll` and `filter` for more options. </summary>
<category>Series transformations</category>
val countValues: series: Series<'K,'T> -> int (requires equality)
<summary> Returns the total number of values in the specified series. This excludes missing values or not available values (such as values created from `null`, `Double.NaN`, or those that are missing due to outer join etc.). </summary>
<category>Accessing series data and lookup</category>
val monthly: Frame<(DateTime * DateTime),string>
val groupRowsUsing: selector: ('R -> ObjectSeries<'C> -> 'K) -> frame: Frame<'R,'C> -> Frame<('K * 'R),'C> (requires equality and equality and equality)
<summary> Group rows of a data frame using the specified `selector`. The selector is called with a row key and object series representing the row and should return a new key. The result is a frame with multi-level index, where the first level is formed by the newly created keys. </summary>
<category>Grouping, windowing and chunking</category>
val k: DateTime
property DateTime.Year: int with get
<summary>Gets the year component of the date represented by this instance.</summary>
<returns>The year, between 1 and 9999.</returns>
property DateTime.Month: int with get
<summary>Gets the month component of the date represented by this instance.</summary>
<returns>The month component, expressed as a value between 1 and 12.</returns>
property Frame.Rows: RowSeries<(DateTime * DateTime),string> with get
<category>Accessors and slicing</category>
val getNumericCols: frame: Frame<'R,'C> -> Series<'C,Series<'R,float>> (requires equality and equality)
<summary> Returns a series of columns of the data frame indexed by the column keys, which contains those series whose values are convertible to float, and with missing values where the conversion fails. </summary>
<category>Accessing frame data and lookup</category>
val mapValues: f: ('T -> 'R) -> series: Series<'K,'T> -> Series<'K,'R> (requires equality)
<summary> Returns a new series whose values are the results of applying the given function to values of the original series. This function skips over missing values and call the function with just values. It is also aliased using the `$` operator so you can write `series $ func` for `series |&gt; Series.mapValues func`. </summary>
<category>Series transformations</category>
static member Stats.levelMean: level: ('K -> 'L) -> series: Series<'K,'V> -> Series<'L,float> (requires equality and equality)
val fst: tuple: ('T1 * 'T2) -> 'T1

Type something to start searching.