Interoperating between R and Deedle
The R type provider enables
smooth interoperation between R and F#. The type provider automatically discovers
installed packages and makes them accessible via the RProvider
namespace.
R type provider for F# automatically converts standard data structures between R and F# (such as numerical values, arrays, etc.). However, the conversion mechanism is extensible and so it is possible to support conversion between other F# types.
The Deedle library comes with extension that automatically converts between Deedle
Frame<R, C>
and R data.frame
and also between Deedle Series<K, V>
and the
zoo package (Z's ordered
observations).
This page is a quick overview showing how to pass data between R and Deedle. You can also get this page as an F# script file from GitHub and run the samples interactively.
Getting started
To use Deedle and R provider together, all you need to do is to install the Deedle.RPlugin package, which installes both as dependencies. Alternatively, you can use the FsLab package, which also includes additional data access, data science and visualization libraries.
In a typical project ("F# Tutorial"), the NuGet packages are installed in the ../packages
directory. To use R provider and Deedle, you need to write something like this:
1: 2: 3: 4: 5: 6: |
|
If you're not using NuGet from Visual Studio, then you'll need to manually copy the
file Deedle.RProvider.Plugin.dll
from the package Deedle.RPlugin
to the
directory where RProvider.dll
is located (in RProvider/lib
). Once that's
done, the R provider will automatically find the plugin.
Passing data frames to and from R
From R to Deedle
Let's start by looking at passing data frames from R to Deedle. To test this, we
can use some of the sample data sets available in the datasets
package. The R
makes all packages available under the RProvider
namespace, so we can just
open datasets
and access the mtcars
data set using R.mtcars
(when typing
the code, you'll get automatic completion when you type R
followed by dot):
1: 2: 3: 4: 5: 6: 7: |
|
|
The first sample uses the Value
property to convert the data set to a boxed Deedle
frame of type obj
. This is a great way to explore the data, but when you want to do
some further processing, you need to specify the type of the data frame that you want
to get. This is done on line 7 where we get mtcars
as a Deedle frame with both rows
and columns indexed by string
.
To see that this is a standard Deedle data frame, let's group the cars by the number of gears and calculate the average "miles per galon" value based on the gear. To visualize the data, we use the F# Charting library:
1: 2: 3: 4: 5: 6: 7: 8: |
|
|
From Deedle to R
So far, we looked how to turn R data frame into Deedle Frame<R, C>
, so let's look
at the opposite direction. The following snippet first reads Deedle data frame
from a CSV file (file name is in the airQuality
variable). We can then use the
data frame as argument to standard R functions that expect data frame.
1:
|
|
Ozone |
Solar.R |
Wind |
Temp |
Month |
Day |
|
---|---|---|---|---|---|---|
0 |
N/A |
190 |
7.4 |
67 |
5 |
1 |
1 |
36 |
118 |
8 |
72 |
5 |
2 |
2 |
12 |
149 |
12.6 |
74 |
5 |
3 |
3 |
18 |
313 |
11.5 |
62 |
5 |
4 |
4 |
N/A |
N/A |
14.3 |
56 |
5 |
5 |
5 |
28 |
N/A |
14.9 |
66 |
5 |
6 |
6 |
23 |
299 |
8.6 |
65 |
5 |
7 |
7 |
19 |
99 |
13.8 |
59 |
5 |
8 |
... |
... |
... |
... |
... |
... |
... |
149 |
N/A |
145 |
13.2 |
77 |
9 |
27 |
150 |
14 |
191 |
14.3 |
75 |
9 |
28 |
151 |
18 |
131 |
8 |
76 |
9 |
29 |
152 |
20 |
223 |
11.5 |
68 |
9 |
30 |
Let's first try passing the air
frame to the R as.data.frame
function (which
will not do anything, aside from importing the data into R). To do something
slightly more interesting, we then use the colMeans
R function to calculate averages
for each column (to do this, we need to open the base
package):
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: |
|
As a final example, let's look at the handling of missing values. Unlike R, Deedle does not
distinguish between missing data (NA
) and not a number (NaN
). For example, in the
following simple frame, the Floats
column has missing value for keys 2 and 3 while
Names
has missing value for the row 2:
1: 2: 3: 4: 5: |
|
When we pass the data frame to R, missing values in numeric columns are turned into NaN
and missing data for other columns are turned into NA
. Here, we use R.assign
which
stores the data frame in a varaible available in the current R environment:
1: 2: 3: 4: 5: 6: 7: |
|
Passing time series to and from R
For working with time series data, the Deedle plugin uses the zoo package
(Z's ordered observations). If you do not have the package installed, you can do that
by using the install.packages("zoo")
command from R or using R.install_packages("zoo")
from
F# after opening RProvider.utils
. When running the code from F#, you'll need to restart your
editor and F# interactive after it is installed.
From R to Deedle
Let's start by looking at getting time series data from R. We can again use the datasets
package with samples. For example, the austres
data set gives us access to
quarterly time series of the number of australian residents:
1: 2: 3: 4: 5: 6: 7: 8: 9: |
|
As with data frames, when we want to do any further processing with the time series, we need
to use the generic GetValue
method and specify a type annotation to that tells the F#
compiler that we expect a series where both keys and values are of type float
:
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: |
|
The current version of the Deedle plugin supports only time series with single column.
To access, for example, the EU stock market data, we need to write a short R inline
code to extract the column we are interested in. The following gets the FTSE time
series from EuStockMarkets
:
1: 2: |
|
From Deedle to R
The opposite direction is equally easy. To demonstrate this, we'll generate a simple time series with 3 days of randomly generated values starting today:
1: 2: 3: 4: 5: |
|
Now that we have a time series, we can pass it to R using the R.as_zoo
function or
using R.assign
to store it in an R variable. As previously, the R provider automatically
shows the output that R prints for the value:
1: 2: 3: 4: 5: 6: 7: 8: 9: |
|
Typically, you will not need to assign time series to an R variable, because you can use it directly as an argument to functions that expect time series. For example, the following snippet applies the rolling mean function with a window size 20 to the time series.
1: 2: |
|
This is a simple example - in practice, you can achieve the same thing with Series.window
function from Deedle - but it demonstrates how easy it is to use R packages with
time series (and data frames) from Deedle. As a final example, we create a data frame that
contains the original time series together with the rolling mean (in a separate column)
and then draws a chart showing the results:
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: |
|
Depending on your random number generator, the resulting chart looks something like this:
|
static member AirPassengers : SymbolicExpression
static member BJsales : SymbolicExpression
static member BJsales_lead : SymbolicExpression
static member BOD : SymbolicExpression
static member CO2 : SymbolicExpression
static member ChickWeight : SymbolicExpression
static member DNase : SymbolicExpression
static member EuStockMarkets : SymbolicExpression
static member Formaldehyde : SymbolicExpression
static member HairEyeColor : SymbolicExpression
...
Base R datasets.
module Frame
from Deedle
--------------------
type Frame =
static member ReadCsv : stream:Stream * hasHeaders:Nullable<bool> * inferTypes:Nullable<bool> * inferRows:Nullable<int> * schema:string * separators:string * culture:string * maxRows:Nullable<int> * missingValues:string [] * preferOptions:Nullable<bool> -> Frame<int,string>
static member ReadCsv : location:string * hasHeaders:Nullable<bool> * inferTypes:Nullable<bool> * inferRows:Nullable<int> * schema:string * separators:string * culture:string * maxRows:Nullable<int> * missingValues:string [] * preferOptions:bool -> Frame<int,string>
static member ReadReader : reader:IDataReader -> Frame<int,string>
static member CustomExpanders : Dictionary<Type,Func<obj,seq<string * Type * obj>>>
static member NonExpandableInterfaces : ResizeArray<Type>
static member NonExpandableTypes : HashSet<Type>
--------------------
type Frame<'TRowKey,'TColumnKey (requires equality and equality)> =
interface IDynamicMetaObjectProvider
interface INotifyCollectionChanged
interface IFsiFormattable
interface IFrame
new : names:seq<'TColumnKey> * columns:seq<ISeries<'TRowKey>> -> Frame<'TRowKey,'TColumnKey>
new : rowIndex:IIndex<'TRowKey> * columnIndex:IIndex<'TColumnKey> * data:IVector<IVector> * indexBuilder:IIndexBuilder * vectorBuilder:IVectorBuilder -> Frame<'TRowKey,'TColumnKey>
member AddColumn : column:'TColumnKey * series:ISeries<'TRowKey> -> unit
member AddColumn : column:'TColumnKey * series:seq<'V> -> unit
member AddColumn : column:'TColumnKey * series:ISeries<'TRowKey> * lookup:Lookup -> unit
member AddColumn : column:'TColumnKey * series:seq<'V> * lookup:Lookup -> unit
...
--------------------
new : names:seq<'TColumnKey> * columns:seq<ISeries<'TRowKey>> -> 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 string : value:'T -> string
--------------------
type string = String
namespace FSharp
--------------------
namespace Microsoft.FSharp
static member count : frame:Frame<'R,'C> -> Series<'C,int> (requires equality and equality)
static member count : series:Series<'K,'V> -> int (requires equality)
static member describe : series:Series<'K,'V> -> Series<string,float> (requires equality and equality)
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 expandingMin : series:Series<'K,'V> -> Series<'K,float> (requires equality)
static member expandingSkew : series:Series<'K,'V> -> Series<'K,float> (requires equality)
static member expandingStdDev : series:Series<'K,'V> -> Series<'K,float> (requires equality)
...
module Series
from Deedle
--------------------
type Series =
static member ofNullables : values:seq<Nullable<'a0>> -> Series<int,'a0> (requires default constructor and value type and 'a0 :> ValueType)
static member ofObservations : observations:seq<'c * 'd> -> Series<'c,'d> (requires equality)
static member ofOptionalObservations : observations:seq<'K * 'a1 option> -> Series<'K,'a1> (requires equality)
static member ofValues : values:seq<'a> -> Series<int,'a>
--------------------
type Series<'K,'V (requires equality)> =
interface IFsiFormattable
interface ISeries<'K>
new : pairs:seq<KeyValuePair<'K,'V>> -> Series<'K,'V>
new : keys:'K [] * values:'V [] -> Series<'K,'V>
new : keys:seq<'K> * values:seq<'V> -> Series<'K,'V>
new : index:IIndex<'K> * vector:IVector<'V> * vectorBuilder:IVectorBuilder * indexBuilder:IIndexBuilder -> Series<'K,'V>
member After : lowerExclusive:'K -> Series<'K,'V>
member Aggregate : aggregation:Aggregation<'K> * observationSelector:Func<DataSegment<Series<'K,'V>>,KeyValuePair<'TNewKey,OptionalValue<'R>>> -> Series<'TNewKey,'R> (requires equality)
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)
member AsyncMaterialize : unit -> Async<Series<'K,'V>>
...
--------------------
new : pairs:seq<Collections.Generic.KeyValuePair<'K,'V>> -> Series<'K,'V>
new : keys:seq<'K> * values:seq<'V> -> Series<'K,'V>
new : keys:'K [] * values:'V [] -> Series<'K,'V>
new : index:Indices.IIndex<'K> * vector:IVector<'V> * vectorBuilder:Vectors.IVectorBuilder * indexBuilder:Indices.IIndexBuilder -> Series<'K,'V>
static member Area : data:seq<#value> * ?Name:string * ?Title:string * ?Labels:#seq<string> * ?Color:Color * ?XTitle:string * ?YTitle:string -> GenericChart
static member Area : data:seq<#key * #value> * ?Name:string * ?Title:string * ?Labels:#seq<string> * ?Color:Color * ?XTitle:string * ?YTitle:string -> GenericChart
static member Bar : data:seq<#value> * ?Name:string * ?Title:string * ?Labels:#seq<string> * ?Color:Color * ?XTitle:string * ?YTitle:string -> GenericChart
static member Bar : data:seq<#key * #value> * ?Name:string * ?Title:string * ?Labels:#seq<string> * ?Color:Color * ?XTitle:string * ?YTitle:string -> GenericChart
static member BoxPlotFromData : data:seq<#key * #seq<'a2>> * ?Name:string * ?Title:string * ?Color:Color * ?XTitle:string * ?YTitle:string * ?Percentile:int * ?ShowAverage:bool * ?ShowMedian:bool * ?ShowUnusualValues:bool * ?WhiskerPercentile:int -> GenericChart (requires 'a2 :> value)
static member BoxPlotFromStatistics : data:seq<#key * #value * #value * #value * #value * #value * #value> * ?Name:string * ?Title:string * ?Labels:#seq<string> * ?Color:Color * ?XTitle:string * ?YTitle:string * ?Percentile:int * ?ShowAverage:bool * ?ShowMedian:bool * ?ShowUnusualValues:bool * ?WhiskerPercentile:int -> GenericChart
static member Bubble : data:seq<#value * #value> * ?Name:string * ?Title:string * ?Labels:#seq<string> * ?Color:Color * ?XTitle:string * ?YTitle:string * ?BubbleMaxSize:int * ?BubbleMinSize:int * ?BubbleScaleMax:float * ?BubbleScaleMin:float * ?UseSizeForLabel:bool -> GenericChart
static member Bubble : data:seq<#key * #value * #value> * ?Name:string * ?Title:string * ?Labels:#seq<string> * ?Color:Color * ?XTitle:string * ?YTitle:string * ?BubbleMaxSize:int * ?BubbleMinSize:int * ?BubbleScaleMax:float * ?BubbleScaleMin:float * ?UseSizeForLabel:bool -> GenericChart
static member Candlestick : data:seq<#value * #value * #value * #value> * ?Name:string * ?Title:string * ?Labels:#seq<string> * ?Color:Color * ?XTitle:string * ?YTitle:string -> CandlestickChart
static member Candlestick : data:seq<#key * #value * #value * #value * #value> * ?Name:string * ?Title:string * ?Labels:#seq<string> * ?Color:Color * ?XTitle:string * ?YTitle:string -> CandlestickChart
...
static member Chart.Column : data:seq<#key * #value> * ?Name:string * ?Title:string * ?Labels:#seq<string> * ?Color:Drawing.Color * ?XTitle:string * ?YTitle:string * ?ColumnWidth:float -> ChartTypes.GenericChart
static member Frame.ReadCsv : stream:IO.Stream * ?hasHeaders:bool * ?inferTypes:bool * ?inferRows:int * ?schema:string * ?separators:string * ?culture:string * ?maxRows:int * ?missingValues:string [] * ?preferOptions:bool -> 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 [] * ?preferOptions:bool -> Frame<int,string>
static member Frame.ReadCsv : stream:IO.Stream * hasHeaders:Nullable<bool> * inferTypes:Nullable<bool> * inferRows:Nullable<int> * schema:string * separators:string * culture:string * maxRows:Nullable<int> * missingValues:string [] * preferOptions:Nullable<bool> -> Frame<int,string>
static member Frame.ReadCsv : location:string * hasHeaders:Nullable<bool> * inferTypes:Nullable<bool> * inferRows:Nullable<int> * schema:string * separators:string * culture:string * maxRows:Nullable<int> * missingValues:string [] * preferOptions:bool -> 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 [] * ?preferOptions:bool -> Frame<'R,string> (requires equality)
static member ! :?paramArray: obj [] -> SymbolicExpression + 1 overload
static member != :?paramArray: obj [] -> SymbolicExpression + 1 overload
static member !_hexmode :?a: obj -> SymbolicExpression + 1 overload
static member !_octmode :?a: obj -> SymbolicExpression + 1 overload
static member $ :?paramArray: obj [] -> SymbolicExpression + 1 overload
static member $<- :?paramArray: obj [] -> SymbolicExpression + 1 overload
static member $<-_data_frame :?x: obj *?name: obj *?value: obj -> SymbolicExpression + 1 overload
static member $_DLLInfo :?x: obj *?name: obj -> SymbolicExpression + 1 overload
static member $_data_frame :?x: obj *?name: obj -> SymbolicExpression + 1 overload
static member $_package__version :?x: obj *?name: obj -> SymbolicExpression + 1 overload
...
Base R functions.
R.as_data_frame(paramsByName: Collections.Generic.IDictionary<string,obj>) : SymbolicExpression
R.as_data_frame(?x: obj,?row_names: obj,?optional: obj,?___: obj,?paramArray: obj []) : SymbolicExpression
Coerce to a Data Frame
--------------------
R.as_data_frame(paramsByName: Collections.Generic.IDictionary<string,obj>) : SymbolicExpression
R.as_data_frame(?x: obj,?row_names: obj,?optional: obj,?___: obj,?paramArray: obj []) : SymbolicExpression
Coerce to a Data Frame
R.colMeans(paramsByName: Collections.Generic.IDictionary<string,obj>) : SymbolicExpression
R.colMeans(?x: obj,?na_rm: obj,?dims: obj) : SymbolicExpression
No documentation available
--------------------
R.colMeans(paramsByName: Collections.Generic.IDictionary<string,obj>) : SymbolicExpression
R.colMeans(?x: obj,?na_rm: obj,?dims: obj) : SymbolicExpression
No documentation available
R.assign(paramsByName: Collections.Generic.IDictionary<string,obj>) : SymbolicExpression
R.assign(?x: obj,?value: obj,?pos: obj,?envir: obj,?inherits: obj,?immediate: obj) : SymbolicExpression
Assign a Value to a Name
--------------------
R.assign(paramsByName: Collections.Generic.IDictionary<string,obj>) : SymbolicExpression
R.assign(?x: obj,?value: obj,?pos: obj,?envir: obj,?inherits: obj,?immediate: obj) : SymbolicExpression
Assign a Value to a Name
val float : value:'T -> float (requires member op_Explicit)
--------------------
type float = Double
--------------------
type float<'Measure> = float
type TimeSpan =
struct
new : ticks:int64 -> TimeSpan + 3 overloads
member Add : ts:TimeSpan -> TimeSpan
member CompareTo : value:obj -> int + 1 overload
member Days : int
member Duration : unit -> TimeSpan
member Equals : value:obj -> bool + 1 overload
member GetHashCode : unit -> int
member Hours : int
member Milliseconds : int
member Minutes : int
...
end
--------------------
TimeSpan ()
TimeSpan(ticks: int64) : TimeSpan
TimeSpan(hours: int, minutes: int, seconds: int) : TimeSpan
TimeSpan(days: int, hours: int, minutes: int, seconds: int) : TimeSpan
TimeSpan(days: int, hours: int, minutes: int, seconds: int, milliseconds: int) : TimeSpan
type DateTime =
struct
new : ticks:int64 -> DateTime + 10 overloads
member Add : value:TimeSpan -> DateTime
member AddDays : value:float -> DateTime
member AddHours : 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
member AddYears : value:int -> DateTime
...
end
--------------------
DateTime ()
(+0 other overloads)
DateTime(ticks: int64) : DateTime
(+0 other overloads)
DateTime(ticks: int64, 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)
DateTime(year: int, month: int, day: int, hour: int, minute: int, second: int, millisecond: int) : DateTime
(+0 other overloads)
DateTime(year: int, month: int, day: int, hour: int, minute: int, second: int, millisecond: int, kind: DateTimeKind) : DateTime
(+0 other overloads)
val int : value:'T -> int (requires member op_Explicit)
--------------------
type int = int32
--------------------
type int<'Measure> = int
static member Stats.mean : series:Series<'K,'V> -> float (requires equality)
R.parse(paramsByName: Collections.Generic.IDictionary<string,obj>) : SymbolicExpression
R.parse(?file: obj,?n: obj,?text: obj,?prompt: obj,?keep_source: obj,?srcfile: obj,?encoding: obj) : SymbolicExpression
Parse Expressions
--------------------
R.parse(paramsByName: Collections.Generic.IDictionary<string,obj>) : SymbolicExpression
R.parse(?file: obj,?n: obj,?text: obj,?prompt: obj,?keep_source: obj,?srcfile: obj,?encoding: obj) : SymbolicExpression
Parse Expressions
R.eval(paramsByName: Collections.Generic.IDictionary<string,obj>) : SymbolicExpression
R.eval(?expr: obj,?envir: obj,?enclos: obj) : SymbolicExpression
Evaluate an (Unevaluated) Expression
--------------------
R.eval(paramsByName: Collections.Generic.IDictionary<string,obj>) : SymbolicExpression
R.eval(?expr: obj,?envir: obj,?enclos: obj) : SymbolicExpression
Evaluate an (Unevaluated) Expression
type Random =
new : unit -> Random + 1 overload
member Next : unit -> int + 2 overloads
member NextBytes : buffer:byte[] -> unit
member NextDouble : unit -> float
--------------------
Random() : Random
Random(Seed: int) : Random
static member MATCH :?x: obj *?table: obj *?nomatch: obj *?___: obj *?paramArray: obj [] -> SymbolicExpression + 1 overload
static member MATCH_default :?x: obj *?table: obj *?nomatch: obj *?___: obj *?paramArray: obj [] -> SymbolicExpression + 1 overload
static member MATCH_times :?x: obj *?table: obj *?nomatch: obj *?units: obj *?eps: obj *?___: obj *?paramArray: obj [] -> SymbolicExpression + 1 overload
static member ORDER :?x: obj *?___: obj *?paramArray: obj [] -> SymbolicExpression + 1 overload
static member ORDER_default :?x: obj *?___: obj *?na_last: obj *?decreasing: obj *?paramArray: obj [] -> SymbolicExpression + 1 overload
static member Sys_yearmon :?NULL: obj -> SymbolicExpression + 1 overload
static member Sys_yearqtr :?NULL: obj -> SymbolicExpression + 1 overload
static member as_Date :?x: obj *?___: obj *?paramArray: obj [] -> SymbolicExpression + 1 overload
static member as_Date_numeric :?x: obj *?origin: obj *?___: obj *?paramArray: obj [] -> SymbolicExpression + 1 overload
static member as_Date_ts :?x: obj *?offset: obj *?___: obj *?paramArray: obj [] -> SymbolicExpression + 1 overload
...
An S3 class with methods for totally ordered indexed
observations. It is particularly aimed at irregular time series
of numeric vectors/matrices and factors. zoo's key design goals
are independence of a particular index/date/time class and
consistency with ts and base R by providing methods to extend
standard generics.
R.as_zoo(?x: obj,?___: obj,?paramArray: obj []) : SymbolicExpression
Coercion from and to zoo
R.rollmean(?x: obj,?k: obj,?fill: obj,?na_pad: obj,?align: obj,?___: obj,?paramArray: obj []) : SymbolicExpression
Rolling Means/Maximums/Medians/Sums
static member Chart.Line : data:seq<#key * #value> * ?Name:string * ?Title:string * ?Labels:#seq<string> * ?Color:Drawing.Color * ?XTitle:string * ?YTitle:string -> ChartTypes.GenericChart