Clustering with FSharp.Stats III: DBSCAN

Back to the index

BinderScriptNotebook

Clustering with FSharp.Stats III: DBSCAN

Summary: This tutorial demonstrates DBSCAN with FSharp.Stats and how to visualize the results with Plotly.NET.

In the previous article of this series hierarchical clustering using FSharp.Stats was introduced.

Introduction

Clustering methods can be used to group elements of a huge data set based on their similarity. Elements sharing similar properties cluster together and can be reported as coherent group. Density-Based Spatial Clustering of Applications with Noise (DBSCAN) was developed to identify clusters with similar density and allows the exclusion of noise points.

Two global parameters have to be defined:

  • ε (eps): radius in which the neighbourhood of each point is checked
  • minPts: minimal number of data points, that must fall into the neighbourhood of a region to be defined as dense

Data points are classified as:

  • Core point: Within a radius of eps there are more (or equal) data points than minPts present.
  • Border point: Within a radius of eps there are less data points than minPts present, but a core point is within the neighbourhood.
  • Noise point: None of the conditions above apply.

For demonstration of DBSCAN, the classic iris data set is used, which consists of 150 records, each of which contains four measurements and a species identifier. In this tutorial we are going to perform DBSCAN on two- and three-dimensional data.

Referencing packages

// Packages hosted by the Fslab community
#r "nuget: Deedle"
#r "nuget: FSharp.Stats"
// third party .net packages 
#r "nuget: Plotly.NET, 2.0.0-preview.12"
#r "nuget: Plotly.NET.Interactive, 2.0.0-preview.12"
#r "nuget: FSharp.Data"

Loading data

open FSharp.Data
open FSharp.Stats
open Deedle

// Retrieve data using the FSharp.Data package and read it as dataframe using the Deedle package
let rawData = Http.RequestString @"https://raw.githubusercontent.com/fslaborg/datasets/main/data/iris.csv"
let df = Frame.ReadCsvString(rawData)

df.Print()
sepal_length sepal_width petal_length petal_width species    
0   -> 5.5          2.4         3.8          1.1         versicolor 
1   -> 4.9          3.1         1.5          0.1         setosa     
2   -> 7.6          3           6.6          2.1         virginica  
3   -> 5.6          2.8         4.9          2           virginica  
4   -> 6.1          3           4.9          1.8         virginica  
5   -> 6.3          3.4         5.6          2.4         virginica  
6   -> 6.2          2.8         4.8          1.8         virginica  
7   -> 7.2          3.2         6            1.8         virginica  
8   -> 6.9          3.2         5.7          2.3         virginica  
9   -> 4.9          3           1.4          0.2         setosa     
10  -> 5.4          3.9         1.7          0.4         setosa     
11  -> 7            3.2         4.7          1.4         versicolor 
12  -> 6.1          3           4.6          1.4         versicolor 
13  -> 5.4          3.7         1.5          0.2         setosa     
14  -> 7.2          3.6         6.1          2.5         virginica  
:      ...          ...         ...          ...         ...        
135 -> 7.7          3.8         6.7          2.2         virginica  
136 -> 5.7          3           4.2          1.2         versicolor 
137 -> 6.4          2.7         5.3          1.9         virginica  
138 -> 5.8          2.7         3.9          1.2         versicolor 
139 -> 5            2.3         3.3          1           versicolor 
140 -> 6.7          3.1         4.4          1.4         versicolor 
141 -> 6.3          3.3         6            2.5         virginica  
142 -> 4.9          2.4         3.3          1           versicolor 
143 -> 5.8          2.8         5.1          2.4         virginica  
144 -> 5            3.3         1.4          0.2         setosa     
145 -> 7.7          2.6         6.9          2.3         virginica  
146 -> 5.7          2.6         3.5          1           versicolor 
147 -> 5.9          3           5.1          1.8         virginica  
148 -> 6.8          3.2         5.9          2.3         virginica  
149 -> 5            3.6         1.4          0.2         setosa

Let's take a first look at the data with 2D and 3D scatter plots using Plotly.NET. Each of the 150 records consists of four measurements and a species identifier. Since the species identifier occur several times (Iris-virginica, Iris-versicolor, and Iris-setosa), we create unique labels by adding the rows index to the species identifier.

open Plotly.NET
open FSharp.Stats.ML.Unsupervised

let header2D = ["petal_length";"petal_width"]
let header3D = ["sepal_length";"petal_length";"petal_width"]

//extract petal length and petal width
let data2D = 
    Frame.sliceCols header2D df
    |> Frame.toJaggedArray

//extract sepal length, petal length, and petal width
let data3D = 
    Frame.sliceCols header3D df
    |> Frame.toJaggedArray

let labels = 
    Frame.getCol "species" df
    |> Series.values
    |> Seq.mapi (fun i s -> sprintf "%s_%i" s i)

let rawChart2D =
    let unzippedData =
        data2D
        |> Array.map (fun x -> x.[0],x.[1])
    Chart.Scatter(unzippedData,mode=StyleParam.Mode.Markers,Labels=labels)
    |> Chart.withXAxisStyle header2D.[0]
    |> Chart.withYAxisStyle header2D.[1]
    |> Chart.withTitle "rawChart2D"

let rawChart3D =
    let unzippedData =
        data3D
        |> Array.map (fun x -> x.[0],x.[1],x.[2])
    Chart.Scatter3d(unzippedData,mode=StyleParam.Mode.Markers,Labels=labels)
    |> Chart.withXAxisStyle header3D.[0]
    |> Chart.withYAxisStyle header3D.[1]
    |> Chart.withZAxisStyle header3D.[2]
    |> Chart.withTitle "rawChart3D"

Clustering

The function that performs DBSCAN can be found at FSharp.Stats.ML.Unsupervised.DbScan.compute. It requires four input parameters:

  1. Distance measure (from FSharp.Stats.ML.DistanceMetrics) (seq<'T> -> seq<'T> -> float)
  2. minPts (int)
  3. eps (float)
  4. data points as sequence of coordinate sequences (seq<#seq<'T>>)

The clustering result consists of a sequence of noise point coordinates and a sequence of clusters containing all related point coordinates.

open FSharp.Stats.ML
open FSharp.Stats.ML.Unsupervised


let eps2D = 0.5
let eps3D = 0.7

let minPts = 20

let result2D = DbScan.compute DistanceMetrics.Array.euclidean minPts eps2D data2D
"{ Clusterlist =
   seq
     [seq [[|1.5; 0.1|]; [|1.4; 0.2|]; [|1.7; 0.4|]; [|1.5; 0.2|]; ...];
      seq [[|4.9; 2.0|]; [|4.9; 1.8|]; [|4.8; 1.8|]; [|5.0; 2.0|]; ...]]
  Noisepoints =
   seq [[|6.6; 2.1|]; [|3.0; 1.1|]; [|6.7; 2.0|]; [|6.4; 2.0|]; ...] }"
let result3D = DbScan.compute DistanceMetrics.Array.euclidean minPts eps3D data3D
"{ Clusterlist =
   seq
     [seq
        [[|5.5; 3.8; 1.1|]; [|5.6; 4.1; 1.3|]; [|5.6; 3.9; 1.1|];
         [|5.6; 3.6; 1.3|]; ...];
      seq
        [[|4.9; 1.5; 0.1|]; [|4.9; 1.4; 0.2|]; [|5.4; 1.7; 0.4|];
         [|5.4; 1.5; 0.2|]; ...]]
  Noisepoints =
   seq
     [[|7.6; 6.6; 2.1|]; [|7.2; 6.1; 2.5|]; [|7.7; 6.1; 2.3|]; [|5.1; 3.0; 1.1|];
      ...] }"

Visualization of clustering result

To visualize the clustering result coordinates of each cluster and noise points are visualized separately and combined in a single scatter plot.

2D clustering result visualization

//to create a chart with two dimensional data use the following function
    
let chartCluster2D = 
    result2D.Clusterlist
    |> Seq.mapi (fun i l ->
        l
        |> Seq.map (fun x -> x.[0],x.[1])
        |> Seq.distinct //more efficient visualization; no difference in plot but in point numbers
        |> Chart.Point
        |> Chart.withTraceName (sprintf "Cluster %i" i))
    |> Chart.combine

let chartNoise2D = 
    result2D.Noisepoints
    |> Seq.map (fun x -> x.[0],x.[1])  
    |> Seq.distinct //more efficient visualization; no difference in plot but in point numbers
    |> Chart.Point
    |> Chart.withTraceName "Noise"

let chartTitle2D = 
    let noiseCount   = result2D.Noisepoints |> Seq.length
    let clusterCount = result2D.Clusterlist |> Seq.length
    let clPtsCount   = result2D.Clusterlist |> Seq.sumBy Seq.length
    $"eps: %.1f{eps2D} minPts: %i{minPts} pts: %i{noiseCount + clPtsCount} cluster: %i{clusterCount} noisePts: %i{noiseCount}" 

let chart2D =
    [chartNoise2D;chartCluster2D]
    |> Chart.combine
    |> Chart.withTitle chartTitle2D
    |> Chart.withXAxisStyle header2D.[0]
    |> Chart.withYAxisStyle header2D.[1]

3D clustering result visualization

let chartCluster3D = 
    result3D.Clusterlist
    |> Seq.mapi (fun i l ->
        l
        |> Seq.map (fun x -> x.[0],x.[1],x.[2])
        |> Seq.distinct //faster visualization; no difference in plot but in point number
        |> fun x -> Chart.Scatter3d (x,StyleParam.Mode.Markers)
        |> Chart.withTraceName (sprintf "Cluster_%i" i))
    |> Chart.combine

let chartNoise3D =
    result3D.Noisepoints
    |> Seq.map (fun x -> x.[0],x.[1],x.[2])  
    |> Seq.distinct //faster visualization; no difference in plot but in point number
    |> fun x -> Chart.Scatter3d (x,StyleParam.Mode.Markers)
    |> Chart.withTraceName "Noise"

let chartname3D = 
    let noiseCount   = result3D.Noisepoints |> Seq.length
    let clusterCount = result3D.Clusterlist |> Seq.length
    let clPtsCount   = result3D.Clusterlist |> Seq.sumBy Seq.length
    $"eps: %.1f{eps3D} minPts: %i{minPts} pts: %i{noiseCount + clPtsCount} cluster: %i{clusterCount} noisePts: %i{noiseCount}" 
   
let chart3D = 
    [chartNoise3D;chartCluster3D]
    |> Chart.combine
    |> Chart.withTitle chartname3D
    |> Chart.withXAxisStyle header3D.[0]
    |> Chart.withYAxisStyle header3D.[1]
    |> Chart.withZAxisStyle header3D.[2]
    
//for faster computation you can use the squaredEuclidean distance and set your eps to its square
let clusteredChart3D() = DbScan.compute DistanceMetrics.Array.euclideanNaNSquared 20 (0.7**2.) data3D 

Limitations

  1. The selection of minPts and eps is critical and even small deviations can severely influence the final results
  2. When data points are of varying density, DBSCAN is not appropriate

Notes

  • Please note that depending on what data you want to cluster, a column wise z-score normalization may be required. In the presented example differences in sepal width have a reduced influence because the absolute variation is low.

References

  • FSharp.Stats documentation, fslaborg,
  • Shinde and Sankhe, Comparison of Enhanced DBSCAN Algorithms: A Review, International Journal of Engeneering Research & Technology, 2017
  • Nagaraju et al., An effective density based approach to detect complex data clusters using notion of neighborhood difference, Int. J. Autom. Comput., 2017, https://doi.org/10.1007/s11633-016-1038-7
Multiple items
namespace FSharp

--------------------
namespace Microsoft.FSharp
Multiple items
namespace FSharp.Data

--------------------
namespace Microsoft.FSharp.Data
namespace FSharp.Stats
namespace Deedle
val rawData : string
type Http = private new : unit -> Http static member private AppendQueryToUrl : url:string * query:(string * string) list -> string static member AsyncRequest : url:string * ?query:(string * string) list * ?headers:seq<string * string> * ?httpMethod:string * ?body:HttpRequestBody * ?cookies:seq<string * string> * ?cookieContainer:CookieContainer * ?silentHttpErrors:bool * ?silentCookieErrors:bool * ?responseEncodingOverride:string * ?customizeHttpRequest:(HttpWebRequest -> HttpWebRequest) * ?timeout:int -> Async<HttpResponse> static member AsyncRequestStream : url:string * ?query:(string * string) list * ?headers:seq<string * string> * ?httpMethod:string * ?body:HttpRequestBody * ?cookies:seq<string * string> * ?cookieContainer:CookieContainer * ?silentHttpErrors:bool * ?silentCookieErrors:bool * ?customizeHttpRequest:(HttpWebRequest -> HttpWebRequest) * ?timeout:int -> Async<HttpResponseWithStream> static member AsyncRequestString : url:string * ?query:(string * string) list * ?headers:seq<string * string> * ?httpMethod:string * ?body:HttpRequestBody * ?cookies:seq<string * string> * ?cookieContainer:CookieContainer * ?silentHttpErrors:bool * ?silentCookieErrors:bool * ?responseEncodingOverride:string * ?customizeHttpRequest:(HttpWebRequest -> HttpWebRequest) * ?timeout:int -> Async<string> static member private EncodeFormData : query:string -> string static member private InnerRequest : url:string * toHttpResponse:(string -> int -> string -> string -> 'a0 option -> Map<string,string> -> Map<string,string> -> Stream -> Async<'a1>) * ?query:(string * string) list * ?headers:seq<string * string> * ?httpMethod:string * ?body:HttpRequestBody * ?cookies:seq<string * string> * ?cookieContainer:CookieContainer * ?silentHttpErrors:bool * ?silentCookieErrors:bool * ?responseEncodingOverride:'a0 * ?customizeHttpRequest:(HttpWebRequest -> HttpWebRequest) * ?timeout:int -> Async<'a1> static member Request : url:string * ?query:(string * string) list * ?headers:seq<string * string> * ?httpMethod:string * ?body:HttpRequestBody * ?cookies:seq<string * string> * ?cookieContainer:CookieContainer * ?silentHttpErrors:bool * ?silentCookieErrors:bool * ?responseEncodingOverride:string * ?customizeHttpRequest:(HttpWebRequest -> HttpWebRequest) * ?timeout:int -> HttpResponse static member RequestStream : url:string * ?query:(string * string) list * ?headers:seq<string * string> * ?httpMethod:string * ?body:HttpRequestBody * ?cookies:seq<string * string> * ?cookieContainer:CookieContainer * ?silentHttpErrors:bool * ?silentCookieErrors:bool * ?customizeHttpRequest:(HttpWebRequest -> HttpWebRequest) * ?timeout:int -> HttpResponseWithStream static member RequestString : url:string * ?query:(string * string) list * ?headers:seq<string * string> * ?httpMethod:string * ?body:HttpRequestBody * ?cookies:seq<string * string> * ?cookieContainer:CookieContainer * ?silentHttpErrors:bool * ?silentCookieErrors:bool * ?responseEncodingOverride:string * ?customizeHttpRequest:(HttpWebRequest -> HttpWebRequest) * ?timeout:int -> string
<summary> Utilities for working with network via HTTP. Includes methods for downloading resources with specified headers, query parameters and HTTP body </summary>
Multiple items
static member Http.RequestString : url:string * ?query:(string * string) list * ?headers:seq<string * string> * ?httpMethod:string * ?body:HttpRequestBody * ?cookies:seq<string * string> * ?cookieContainer:System.Net.CookieContainer * ?silentHttpErrors:bool * ?silentCookieErrors:bool * ?responseEncodingOverride:string * ?customizeHttpRequest:(System.Net.HttpWebRequest -> System.Net.HttpWebRequest) * ?timeout:int -> string

--------------------
static member Http.RequestString : url:string * ?query:(string * string) list * ?headers:seq<string * string> * ?httpMethod:string * ?body:HttpRequestBody * ?cookies:seq<string * string> * ?cookieContainer:System.Net.CookieContainer * ?silentHttpErrors:bool * ?silentCookieErrors:bool * ?responseEncodingOverride:string * ?customizeHttpRequest:(System.Net.HttpWebRequest -> System.Net.HttpWebRequest) * ?timeout:int -> string
val df : Frame<int,string>
Multiple items
module Frame from Deedle

--------------------
type Frame = 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> + 1 overload 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 : rowIndex:IIndex<'TRowKey> * columnIndex:IIndex<'TColumnKey> * data:IVector<IVector> * indexBuilder:IIndexBuilder * vectorBuilder:IVectorBuilder -> Frame<'TRowKey,'TColumnKey> + 1 overload member AddColumn : column:'TColumnKey * series:seq<'V> -> unit + 3 overloads member AggregateRowsBy : groupBy:seq<'TColumnKey> * aggBy:seq<'TColumnKey> * 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 member DropColumn : column:'TColumnKey -> 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>
static member Frame.ReadCsvString : csvString:string * ?hasHeaders:bool * ?inferTypes:bool * ?inferRows:int * ?schema:string * ?separators:string * ?culture:string * ?maxRows:int * ?missingValues:string [] * ?preferOptions:bool -> Frame<int,string>
static member FrameExtensions.Print : frame:Frame<'K,'V> -> unit (requires equality and equality)
static member FrameExtensions.Print : frame:Frame<'K,'V> * printTypes:bool -> unit (requires equality and equality)
namespace Plotly
namespace Plotly.NET
namespace FSharp.Stats.ML
namespace FSharp.Stats.ML.Unsupervised
val header2D : string list
val header3D : string list
val data2D : float [] []
Multiple items
module Frame from Deedle

--------------------
type Frame = inherit DynamicObj new : unit -> Frame

--------------------
type Frame<'TRowKey,'TColumnKey (requires equality and equality)> = interface IDynamicMetaObjectProvider interface INotifyCollectionChanged 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:seq<'V> -> unit + 3 overloads member AggregateRowsBy : groupBy:seq<'TColumnKey> * aggBy:seq<'TColumnKey> * 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 member DropColumn : column:'TColumnKey -> unit ...

--------------------
new : unit -> Frame

--------------------
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 sliceCols : columns:seq<'C> -> frame:Frame<'R,'C> -> Frame<'R,'C> (requires equality and equality)
val toJaggedArray : frame:Frame<'R,'C> -> float [] [] (requires equality and equality)
val data3D : float [] []
val labels : seq<string>
val getCol : column:'C -> frame:Frame<'R,'C> -> Series<'R,'V> (requires equality and equality)
Multiple items
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 : 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>> member Convert : forward:Func<'V,'R> * backward:Func<'R,'V> -> Series<'K,'R> ...

--------------------
new : pairs:seq<System.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>
val values : series:Series<'K,'T> -> seq<'T> (requires equality)
Multiple items
module Seq from Plotly.NET

--------------------
module Seq from FSharp.Stats
<summary> Module to compute common statistical measure </summary>

--------------------
module Seq from Microsoft.FSharp.Collections
<summary>Contains operations for working with values of type <see cref="T:Microsoft.FSharp.Collections.seq`1" />.</summary>
val mapi : mapping:(int -> 'T -> 'U) -> source:seq<'T> -> seq<'U>
<summary>Builds a new collection whose elements are the results of applying the given function to each of the elements of the collection. The integer index passed to the function indicates the index (from 0) of element being transformed.</summary>
<param name="mapping">A function to transform items from the input sequence that also supplies the current index.</param>
<param name="source">The input sequence.</param>
<returns>The result sequence.</returns>
<exception cref="T:System.ArgumentNullException">Thrown when the input sequence is null.</exception>
val i : int
val s : string
val sprintf : format:Printf.StringFormat<'T> -> 'T
<summary>Print to a string using the given format.</summary>
<param name="format">The formatter.</param>
<returns>The formatted result.</returns>
val rawChart2D : GenericChart.GenericChart
val unzippedData : (float * float) []
Multiple items
module Array from FSharp.Stats
<summary> Module to compute common statistical measure on array </summary>

--------------------
module Array from Microsoft.FSharp.Collections
<summary>Contains operations for working with arrays.</summary>
<remarks> See also <a href="https://docs.microsoft.com/dotnet/fsharp/language-reference/arrays">F# Language Guide - Arrays</a>. </remarks>
val map : mapping:('T -> 'U) -> array:'T [] -> 'U []
<summary>Builds a new array whose elements are the results of applying the given function to each of the elements of the array.</summary>
<param name="mapping">The function to transform elements of the array.</param>
<param name="array">The input array.</param>
<returns>The array of transformed elements.</returns>
<exception cref="T:System.ArgumentNullException">Thrown when the input array is null.</exception>
val x : float []
type Chart = static member Area : x:seq<#IConvertible> * y:seq<#IConvertible> * ?Name:string * ?ShowMarkers:bool * ?ShowLegend:bool * ?MarkerSymbol:MarkerSymbol * ?Color:Color * ?Opacity:float * ?Labels:seq<#IConvertible> * ?TextPosition:TextPosition * ?TextFont:Font * ?Dash:DrawingStyle * ?Width:float -> GenericChart + 1 overload static member Bar : values:seq<#IConvertible> * ?Keys:seq<#IConvertible> * ?Name:string * ?ShowLegend:bool * ?Color:Color * ?PatternShape:PatternShape * ?MultiPatternShape:seq<PatternShape> * ?Pattern:Pattern * ?Base:#IConvertible * ?Width:'a3 * ?MultiWidth:seq<'a3> * ?Opacity:float * ?MultiOpacity:seq<float> * ?Text:'a4 * ?MultiText:seq<'a4> * ?TextPosition:TextPosition * ?MultiTextPosition:seq<TextPosition> * ?TextFont:Font * ?Marker:Marker -> GenericChart (requires 'a3 :> IConvertible and 'a4 :> IConvertible) + 1 overload static member BoxPlot : ?x:seq<#IConvertible> * ?y:seq<#IConvertible> * ?Name:string * ?ShowLegend:bool * ?Text:'a2 * ?MultiText:seq<'a2> * ?Fillcolor:Color * ?MarkerColor:Color * ?OutlierColor:Color * ?OutlierWidth:int * ?Opacity:float * ?WhiskerWidth:float * ?BoxPoints:BoxPoints * ?BoxMean:BoxMean * ?Jitter:float * ?PointPos:float * ?Orientation:Orientation * ?Marker:Marker * ?Line:Line * ?AlignmentGroup:string * ?Offsetgroup:string * ?Notched:bool * ?NotchWidth:float * ?QuartileMethod:QuartileMethod -> GenericChart (requires 'a2 :> IConvertible) + 1 overload static member Bubble : x:seq<#IConvertible> * y:seq<#IConvertible> * sizes:seq<int> * ?Name:string * ?ShowLegend:bool * ?MarkerSymbol:MarkerSymbol * ?Color:Color * ?Opacity:float * ?Labels:seq<#IConvertible> * ?TextPosition:TextPosition * ?TextFont:Font * ?StackGroup:string * ?Orientation:Orientation * ?GroupNorm:GroupNorm * ?UseWebGL:bool -> GenericChart + 1 overload static member Candlestick : open:seq<#IConvertible> * high:seq<#IConvertible> * low:seq<#IConvertible> * close:seq<#IConvertible> * x:seq<#IConvertible> * ?Increasing:Line * ?Decreasing:Line * ?WhiskerWidth:float * ?Line:Line * ?XCalendar:Calendar -> GenericChart + 1 overload static member Column : values:seq<#IConvertible> * ?Keys:seq<#IConvertible> * ?Name:string * ?ShowLegend:bool * ?Color:Color * ?Pattern:Pattern * ?PatternShape:PatternShape * ?MultiPatternShape:seq<PatternShape> * ?Base:#IConvertible * ?Width:'a3 * ?MultiWidth:seq<'a3> * ?Opacity:float * ?MultiOpacity:seq<float> * ?Text:'a4 * ?MultiText:seq<'a4> * ?TextPosition:TextPosition * ?MultiTextPosition:seq<TextPosition> * ?TextFont:Font * ?Marker:Marker -> GenericChart (requires 'a3 :> IConvertible and 'a4 :> IConvertible) + 1 overload static member Contour : data:seq<#seq<'a1>> * ?X:seq<#IConvertible> * ?Y:seq<#IConvertible> * ?Name:string * ?ShowLegend:bool * ?Opacity:float * ?Colorscale:Colorscale * ?Showscale:'a4 * ?zSmooth:SmoothAlg * ?ColorBar:'a5 -> GenericChart (requires 'a1 :> IConvertible) static member Funnel : x:seq<#IConvertible> * y:seq<#IConvertible> * ?Name:string * ?ShowLegend:bool * ?Opacity:float * ?Labels:seq<#IConvertible> * ?TextPosition:TextPosition * ?TextFont:Font * ?Color:Color * ?Line:Line * ?x0:'a3 * ?dX:float * ?y0:'a4 * ?dY:float * ?Width:float * ?Offset:float * ?Orientation:Orientation * ?Alignmentgroup:string * ?Offsetgroup:string * ?Cliponaxis:bool * ?Connector:FunnelConnector * ?Insidetextfont:Font * ?Outsidetextfont:Font -> GenericChart static member Heatmap : data:seq<#seq<'a1>> * ?ColNames:seq<#IConvertible> * ?RowNames:seq<#IConvertible> * ?Name:string * ?ShowLegend:bool * ?Opacity:float * ?Colorscale:Colorscale * ?Showscale:'a4 * ?Xgap:'a5 * ?Ygap:'a6 * ?zSmooth:SmoothAlg * ?ColorBar:'a7 * ?UseWebGL:bool -> GenericChart (requires 'a1 :> IConvertible) static member Histogram : ?X:seq<#IConvertible> * ?Y:seq<#IConvertible> * ?Orientation:Orientation * ?Name:string * ?ShowLegend:bool * ?Opacity:float * ?Text:'a2 * ?MultiText:seq<'a2> * ?HistFunc:HistFunc * ?HistNorm:HistNorm * ?AlignmentGroup:string * ?OffsetGroup:string * ?NBinsX:int * ?NBinsY:int * ?BinGroup:string * ?XBins:Bins * ?YBins:Bins * ?MarkerColor:Color * ?Marker:Marker * ?Line:Line * ?ErrorX:Error * ?ErrorY:Error * ?Cumulative:Cumulative * ?HoverLabel:Hoverlabel -> GenericChart (requires 'a2 :> IConvertible) + 1 overload ...
static member Chart.Scatter : xy:seq<#System.IConvertible * #System.IConvertible> * mode:StyleParam.Mode * ?Name:string * ?ShowLegend:bool * ?MarkerSymbol:StyleParam.MarkerSymbol * ?Color:Color * ?Opacity:float * ?Labels:seq<#System.IConvertible> * ?TextPosition:StyleParam.TextPosition * ?TextFont:Font * ?Dash:StyleParam.DrawingStyle * ?Width:float * ?StackGroup:string * ?Orientation:StyleParam.Orientation * ?GroupNorm:StyleParam.GroupNorm * ?UseWebGL:bool -> GenericChart.GenericChart
static member Chart.Scatter : x:seq<#System.IConvertible> * y:seq<#System.IConvertible> * mode:StyleParam.Mode * ?Name:string * ?ShowLegend:bool * ?MarkerSymbol:StyleParam.MarkerSymbol * ?Color:Color * ?Opacity:float * ?Labels:seq<#System.IConvertible> * ?TextPosition:StyleParam.TextPosition * ?TextFont:Font * ?Dash:StyleParam.DrawingStyle * ?Width:float * ?StackGroup:string * ?Orientation:StyleParam.Orientation * ?GroupNorm:StyleParam.GroupNorm * ?UseWebGL:bool -> GenericChart.GenericChart
module StyleParam from Plotly.NET
type Mode = | None | Lines | Lines_Markers | Lines_Text | Lines_Markers_Text | Markers | Markers_Text | Text member Convert : unit -> obj override ToString : unit -> string static member convert : (Mode -> obj) static member toString : (Mode -> string)
union case StyleParam.Mode.Markers: StyleParam.Mode
static member Chart.withXAxisStyle : title:string * ?TitleFont:Font * ?MinMax:(float * float) * ?ShowGrid:bool * ?ShowLine:bool * ?Side:StyleParam.Side * ?Overlaying:StyleParam.LinearAxisId * ?Id:StyleParam.SubPlotId * ?Domain:(float * float) * ?Position:float * ?Zeroline:bool * ?Anchor:StyleParam.LinearAxisId -> (GenericChart.GenericChart -> GenericChart.GenericChart)
static member Chart.withYAxisStyle : title:string * ?TitleFont:Font * ?MinMax:(float * float) * ?ShowGrid:bool * ?ShowLine:bool * ?Side:StyleParam.Side * ?Overlaying:StyleParam.LinearAxisId * ?Id:StyleParam.SubPlotId * ?Domain:(float * float) * ?Position:float * ?ZeroLine:bool * ?Anchor:StyleParam.LinearAxisId -> (GenericChart.GenericChart -> GenericChart.GenericChart)
static member Chart.withTitle : title:string * ?TitleFont:Font -> (GenericChart.GenericChart -> GenericChart.GenericChart)
val rawChart3D : GenericChart.GenericChart
val unzippedData : (float * float * float) []
static member Chart.Scatter3d : xyz:seq<#System.IConvertible * #System.IConvertible * #System.IConvertible> * mode:StyleParam.Mode * ?Name:string * ?ShowLegend:bool * ?MarkerSymbol:StyleParam.MarkerSymbol * ?Color:Color * ?Opacity:float * ?Labels:seq<#System.IConvertible> * ?TextPosition:StyleParam.TextPosition * ?TextFont:Font * ?Dash:StyleParam.DrawingStyle * ?Width:float -> GenericChart.GenericChart
static member Chart.Scatter3d : x:seq<#System.IConvertible> * y:seq<#System.IConvertible> * z:seq<#System.IConvertible> * mode:StyleParam.Mode * ?Name:string * ?ShowLegend:bool * ?MarkerSymbol:StyleParam.MarkerSymbol * ?Color:Color * ?Opacity:float * ?Labels:seq<#System.IConvertible> * ?TextPosition:StyleParam.TextPosition * ?TextFont:Font * ?Dash:StyleParam.DrawingStyle * ?Width:float -> GenericChart.GenericChart
static member Chart.withZAxisStyle : title:string * ?TitleFont:Font * ?MinMax:(float * float) * ?ShowGrid:bool * ?ShowLine:bool * ?Domain:(float * float) * ?Anchor:StyleParam.LinearAxisId -> (GenericChart.GenericChart -> GenericChart.GenericChart)
module GenericChart from Plotly.NET
<summary> Module to represent a GenericChart </summary>
val toChartHTML : gChart:GenericChart.GenericChart -> string
<summary> Converts a GenericChart to it HTML representation. The div layer has a default size of 600 if not specified otherwise. </summary>
val eps2D : float
val eps3D : float
val minPts : int
val result2D : DbScan.DbscanResult<float array>
module DbScan from FSharp.Stats.ML.Unsupervised
val compute : dfu:('a array -> 'a array -> float) -> minPts:int -> eps:float -> input:seq<#seq<'a>> -> DbScan.DbscanResult<'a array>
module DistanceMetrics from FSharp.Stats.ML
<summary> Functions for computing distances of elements or sets </summary>
module Array from FSharp.Stats.ML.DistanceMetrics
val euclidean : a1:'a array -> a2:'a array -> float (requires member get_Zero and member ( - ) and member ( + ) and member op_Explicit and member ( * ))
<summary> Euclidean distance of two coordinate arrays </summary>
val printClusters2D : string
System.Object.ToString() : string
val result3D : DbScan.DbscanResult<float array>
val printClusters3D : string
val chartCluster2D : GenericChart.GenericChart
DbScan.DbscanResult.Clusterlist: seq<seq<float array>>
val l : seq<float array>
val map : mapping:('T -> 'U) -> source:seq<'T> -> seq<'U>
<summary>Builds a new collection whose elements are the results of applying the given function to each of the elements of the collection. The given function will be applied as elements are demanded using the <c>MoveNext</c> method on enumerators retrieved from the object.</summary>
<remarks>The returned sequence may be passed between threads safely. However, individual IEnumerator values generated from the returned sequence should not be accessed concurrently.</remarks>
<param name="mapping">A function to transform items from the input sequence.</param>
<param name="source">The input sequence.</param>
<returns>The result sequence.</returns>
<exception cref="T:System.ArgumentNullException">Thrown when the input sequence is null.</exception>
val x : float array
val distinct : source:seq<'T> -> seq<'T> (requires equality)
<summary>Returns a sequence that contains no duplicate entries according to generic hash and equality comparisons on the entries. If an element occurs multiple times in the sequence then the later occurrences are discarded.</summary>
<param name="source">The input sequence.</param>
<returns>The result sequence.</returns>
<exception cref="T:System.ArgumentNullException">Thrown when the input sequence is null.</exception>
static member Chart.Point : xy:seq<#System.IConvertible * #System.IConvertible> * ?Name:string * ?ShowLegend:bool * ?MarkerSymbol:StyleParam.MarkerSymbol * ?Color:Color * ?Opacity:float * ?Labels:seq<#System.IConvertible> * ?TextPosition:StyleParam.TextPosition * ?TextFont:Font * ?StackGroup:string * ?Orientation:StyleParam.Orientation * ?GroupNorm:StyleParam.GroupNorm * ?UseWebGL:bool -> GenericChart.GenericChart
static member Chart.Point : x:seq<#System.IConvertible> * y:seq<#System.IConvertible> * ?Name:string * ?ShowLegend:bool * ?MarkerSymbol:StyleParam.MarkerSymbol * ?Color:Color * ?Opacity:float * ?Labels:seq<#System.IConvertible> * ?TextPosition:StyleParam.TextPosition * ?TextFont:Font * ?StackGroup:string * ?Orientation:StyleParam.Orientation * ?GroupNorm:StyleParam.GroupNorm * ?UseWebGL:bool -> GenericChart.GenericChart
static member Chart.withTraceName : ?Name:string * ?ShowLegend:bool * ?LegendGroup:string * ?Visible:StyleParam.Visible -> (GenericChart.GenericChart -> GenericChart.GenericChart)
static member Chart.combine : gCharts:seq<GenericChart.GenericChart> -> GenericChart.GenericChart
val chartNoise2D : GenericChart.GenericChart
DbScan.DbscanResult.Noisepoints: seq<float array>
val chartTitle2D : string
val noiseCount : int
val length : source:seq<'T> -> int
<summary>Returns the length of the sequence</summary>
<param name="source">The input sequence.</param>
<returns>The length of the sequence.</returns>
<exception cref="T:System.ArgumentNullException">Thrown when the input sequence is null.</exception>
val clusterCount : int
val clPtsCount : int
val sumBy : projection:('T -> 'U) -> source:seq<'T> -> 'U (requires member ( + ) and member get_Zero)
<summary>Returns the sum of the results generated by applying the function to each element of the sequence.</summary>
<remarks>The generated elements are summed using the <c>+</c> operator and <c>Zero</c> property associated with the generated type.</remarks>
<param name="projection">A function to transform items from the input sequence into the type that will be summed.</param>
<param name="source">The input sequence.</param>
<returns>The computed sum.</returns>
val chart2D : GenericChart.GenericChart
val chartCluster3D : GenericChart.GenericChart
val x : seq<float * float * float>
val chartNoise3D : GenericChart.GenericChart
val chartname3D : string
val chart3D : GenericChart.GenericChart
val clusteredChart3D : unit -> DbScan.DbscanResult<float array>
val euclideanNaNSquared : a1:float array -> a2:float array -> float
<summary> Squared Euclidean distance of two coordinate float arrays (ignores nan) </summary>