Logo Deedle

Tutorial: Analysing stock prices

This is the Deedle equivalent of the classic "pandas stock analysis" tutorial. We load daily prices for four companies, align them into a single frame, compute returns and volatility, smooth them with rolling windows, and downsample to monthly data. The CSV files in docs/data/stocks come from Yahoo Finance (2013).

Load one file per ticker

Each file has Date,Open,High,Low,Close,Volume,Adj Close columns, newest row first. We turn Date into the row index and sort so that time runs forward:

let loadStock (ticker: string) =
  Frame.ReadCsv(root + "stocks/" + ticker + ".csv")
  |> Frame.indexRowsDateTime "Date"
  |> Frame.sortRowsByKey

let msft = loadStock "msft"
msft |> Frame.take 3
val loadStock: ticker: string -> Frame<DateTime,string>
val msft: Frame<DateTime,string> =
  
              Open  High  Low   Close Volume     Adj Close 
03/13/1986 -> 25.50 29.25 25.50 28.00 1031788800 0.07      
03/14/1986 -> 28.00 29.50 28.00 29.00 308160000  0.07      
03/17/1986 -> 29.00 29.75 29.00 29.50 133171200  0.08      
03/18/1986 -> 29.50 29.75 28.50 28.75 67766400   0.07      
03/19/1986 -> 28.75 29.00 28.00 28.25 47894400   0.07      
03/20/1986 -> 28.25 28.25 27.25 27.50 58435200   0.07      
03/21/1986 -> 27.50 28.00 26.25 26.75 59990400   0.07      
03/24/1986 -> 26.75 26.75 25.75 26.00 65289600   0.07      
03/25/1986 -> 26.00 26.50 25.75 26.50 32083200   0.07      
03/26/1986 -> 26.50 27.50 26.25 27.25 22752000   0.07      
03/27/1986 -> 27.25 27.75 27.25 27.75 16848000   0.07      
03/31/1986 -> 27.75 27.75 27.00 27.50 12873600   0.07      
04/01/1986 -> 27.50 27.50 27.25 27.25 11088000   0.07      
04/02/1986 -> 27.25 28.00 27.25 27.50 27014400   0.07      
04/03/1986 -> 27.75 28.50 27.75 27.75 23040000   0.07      
:             ...   ...   ...   ...   ...        ...       
10/18/2013 -> 34.82 34.99 34.33 34.96 41811700   34.96     
10/21/2013 -> 34.98 35.20 34.91 34.99 27433500   34.99     
10/22/2013 -> 35.02 35.10 34.52 34.58 40438500   34.58     
10/23/2013 -> 34.35 34.49 33.67 33.76 58600500   33.76     
10/24/2013 -> 33.82 34.10 33.57 33.72 53209700   33.72     
10/25/2013 -> 35.88 36.29 35.47 35.73 113494000  35.73     
10/28/2013 -> 35.61 35.73 35.27 35.57 38383600   35.57     
10/29/2013 -> 35.63 35.72 35.26 35.52 31702200   35.52     
10/30/2013 -> 35.53 35.79 35.43 35.54 36997700   35.54     
10/31/2013 -> 35.66 35.69 35.34 35.41 41682300   35.41     
11/01/2013 -> 35.67 35.69 35.39 35.53 40264600   35.53     
11/04/2013 -> 35.59 35.98 35.55 35.94 28060700   35.94     
11/05/2013 -> 35.79 36.71 35.77 36.64 51646300   36.64     
11/06/2013 -> 37.24 38.22 37.06 38.18 88615100   38.18     
11/07/2013 -> 37.96 38.01 37.43 37.50 60437400   37.50     

val it: Frame<DateTime,string> =
  
              Open  High  Low   Close Volume     Adj Close 
03/13/1986 -> 25.50 29.25 25.50 28.00 1031788800 0.07      
03/14/1986 -> 28.00 29.50 28.00 29.00 308160000  0.07      
03/17/1986 -> 29.00 29.75 29.00 29.50 133171200  0.08

Combine adjusted closing prices into one frame

Building a frame from named series aligns them by date automatically. Dates where a company did not trade yet (Facebook only listed in 2012) become missing values:

let closes =
  frame [ for t in ["msft"; "goog"; "aapl"; "fb"] ->
            t => (loadStock t)?``Adj Close`` ]

closes |> Frame.take 3
val closes: Frame<DateTime,string> =
  
              msft      goog      aapl   fb        
09/07/1984 -> <missing> <missing> 2.92   <missing> 
09/10/1984 -> <missing> <missing> 2.91   <missing> 
09/11/1984 -> <missing> <missing> 2.96   <missing> 
09/12/1984 -> <missing> <missing> 2.88   <missing> 
09/13/1984 -> <missing> <missing> 3.03   <missing> 
09/14/1984 -> <missing> <missing> 3.07   <missing> 
09/17/1984 -> <missing> <missing> 3.16   <missing> 
09/18/1984 -> <missing> <missing> 3.05   <missing> 
09/19/1984 -> <missing> <missing> 2.98   <missing> 
09/20/1984 -> <missing> <missing> 2.99   <missing> 
09/21/1984 -> <missing> <missing> 2.96   <missing> 
09/24/1984 -> <missing> <missing> 2.94   <missing> 
09/25/1984 -> <missing> <missing> 2.88   <missing> 
09/26/1984 -> <missing> <missing> 2.84   <missing> 
09/27/1984 -> <missing> <missing> 2.84   <missing> 
:             ...       ...       ...    ...       
10/18/2013 -> 34.96     1011.41   505.94 54.22     
10/21/2013 -> 34.99     1003.3    518.33 53.85     
10/22/2013 -> 34.58     1007      516.85 52.68     
10/23/2013 -> 33.76     1031.41   521.91 51.9      
10/24/2013 -> 33.72     1025.55   528.82 52.45     
10/25/2013 -> 35.73     1015.2    522.91 51.95     
10/28/2013 -> 35.57     1015      526.8  50.23     
10/29/2013 -> 35.52     1036.24   513.68 49.4      
10/30/2013 -> 35.54     1030.42   521.85 49.01     
10/31/2013 -> 35.41     1030.58   519.67 50.21     
11/01/2013 -> 35.53     1027.04   517.01 49.75     
11/04/2013 -> 35.94     1026.11   523.69 48.22     
11/05/2013 -> 36.64     1021.52   522.4  50.11     
11/06/2013 -> 38.18     1022.75   520.92 49.12     
11/07/2013 -> 37.5      1007.95   512.49 47.56     

val it: Frame<DateTime,string> =
  
              msft      goog      aapl fb        
09/07/1984 -> <missing> <missing> 2.92 <missing> 
09/10/1984 -> <missing> <missing> 2.91 <missing> 
09/11/1984 -> <missing> <missing> 2.96 <missing>

We only want dates where every stock has a price, so drop rows with any missing value:

let prices = closes |> Frame.dropSparseRows
prices.RowCount
val prices: Frame<DateTime,string> =
  
              msft  goog    aapl   fb    
05/18/2012 -> 28.21 600.4   512.83 38.23 
05/21/2012 -> 28.67 614.11  542.71 34.03 
05/22/2012 -> 28.68 600.8   538.54 31    
05/23/2012 -> 28.05 609.46  551.68 32    
05/24/2012 -> 28.01 603.66  546.61 33.03 
05/25/2012 -> 28    591.53  543.68 31.91 
05/29/2012 -> 28.49 594.34  553.33 28.84 
05/30/2012 -> 28.27 588.23  560    28.19 
05/31/2012 -> 28.13 580.86  558.61 29.6  
06/01/2012 -> 27.42 570.98  542.43 27.72 
06/04/2012 -> 27.51 578.59  545.62 26.9  
06/05/2012 -> 27.47 570.41  544.2  25.87 
06/06/2012 -> 28.28 580.57  552.55 26.81 
06/07/2012 -> 28.17 578.23  552.8  26.31 
06/08/2012 -> 28.57 580.45  561.12 27.1  
:             ...   ...     ...    ...   
10/18/2013 -> 34.96 1011.41 505.94 54.22 
10/21/2013 -> 34.99 1003.3  518.33 53.85 
10/22/2013 -> 34.58 1007    516.85 52.68 
10/23/2013 -> 33.76 1031.41 521.91 51.9  
10/24/2013 -> 33.72 1025.55 528.82 52.45 
10/25/2013 -> 35.73 1015.2  522.91 51.95 
10/28/2013 -> 35.57 1015    526.8  50.23 
10/29/2013 -> 35.52 1036.24 513.68 49.4  
10/30/2013 -> 35.54 1030.42 521.85 49.01 
10/31/2013 -> 35.41 1030.58 519.67 50.21 
11/01/2013 -> 35.53 1027.04 517.01 49.75 
11/04/2013 -> 35.94 1026.11 523.69 48.22 
11/05/2013 -> 36.64 1021.52 522.4  50.11 
11/06/2013 -> 38.18 1022.75 520.92 49.12 
11/07/2013 -> 37.5  1007.95 512.49 47.56 

val it: int = 371

Slice by date

Row slicing with dates works on ordered indices, and is inclusive at both ends:

prices.Rows.[DateTime(2013, 10, 1) .. DateTime(2013, 10, 4)]
val it: Frame<DateTime,string> =
  
              msft  goog   aapl   fb    
10/01/2013 -> 33.58 887    485.13 50.42 
10/02/2013 -> 33.92 887.99 486.72 50.28 
10/03/2013 -> 33.86 876.09 480.6  49.18 
10/04/2013 -> 33.88 872.35 480.23 51.04

Daily returns

Frame.shift 1 moves every value one row forward, so dividing the frame by its shifted copy gives day-over-day price ratios:

let returns = closes / (closes |> Frame.shift 1) - 1.0 |> Frame.dropSparseRows

returns |> Frame.take 3
val returns: Frame<DateTime,string> =
  
              msft                    goog                    aapl                   fb                     
05/21/2012 -> 0.016306274370790463    0.022834776815456426    0.058264922098941074   -0.10986136541982727   
05/22/2012 -> 0.0003487966515520746   -0.02167364153001916    -0.007683661624071969  -0.089039083161916     
05/23/2012 -> -0.021966527196652708   0.014414114513981557    0.024399301816021124   0.032258064516129004   
05/24/2012 -> -0.0014260249554366444  -0.009516621271289494   -0.009190110208816549  0.032187500000000036   
05/25/2012 -> -0.00035701535166021703 -0.020094092701189448   -0.005360311739631696  -0.03390856796851349   
05/29/2012 -> 0.01749999999999985     0.004750393048535306    0.01774941141848152    -0.09620808523973678   
05/30/2012 -> -0.007722007722007707   -0.010280310933135972   0.012054289483671488   -0.022538141470180295  
05/31/2012 -> -0.004952246197382437   -0.012529112762014849   -0.002482142857142877  0.050017736786094336   
06/01/2012 -> -0.025239957340917107   -0.017009262128568015   -0.028964751794633203  -0.06351351351351364   
06/04/2012 -> 0.003282275711159688    0.01332796245052359     0.0058809431631732245  -0.029581529581529598  
06/05/2012 -> -0.001454016721192386   -0.014137817798441144   -0.002602543895018461  -0.038289962825278745  
06/06/2012 -> 0.02948671277757553     0.01781174944338293     0.01534362366776909    0.036335523772709655   
06/07/2012 -> -0.0038896746817538297  -0.0040305217286460415  0.00045244774228581086 -0.01864975755315179   
06/08/2012 -> 0.014199503017394255    0.0038393026996177415   0.015050651230101408   0.03002660585328787    
06/11/2012 -> -0.02520126006300316    -0.0205874752347317     -0.01577202737382377   -0.0033210332103320583 
:             ...                     ...                     ...                    ...                    
10/18/2013 -> 0.001145475372279492    0.13796284836688089     0.008712642303168039   0.03849837195939476    
10/21/2013 -> 0.0008581235697939782   -0.00801850881442745    0.024489069850179845   -0.006824050165990414  
10/22/2013 -> -0.011717633609602873   0.0036878301604705843   -0.002855323828449108  -0.021727019498607225  
10/23/2013 -> -0.023713128976286857   0.024240317775571008    0.009790074489697043   -0.014806378132118492  
10/24/2013 -> -0.0011848341232226778  -0.005681542742459467   0.013239830622138182   0.010597302504816941   
10/25/2013 -> 0.059608540925266906    -0.010092145677928821   -0.011175825422639285  -0.009532888465204947  
10/28/2013 -> -0.004478029666946437   -0.00019700551615453588 0.007439138666309741   -0.03310875842155936   
10/29/2013 -> -0.0014056789429293115  0.020926108374384356    -0.024905087319665897  -0.016523989647620918  
10/30/2013 -> 0.000563063063062863    -0.0056164595074499335  0.015904843482323683   -0.007894736842105288  
10/31/2013 -> -0.003657850309510491   0.00015527648919833048  -0.004177445626137866  0.024484799020608072   
11/01/2013 -> 0.003388873199661191    -0.003434958955151446   -0.005118632978621029  -0.009161521609241219  
11/04/2013 -> 0.011539544047283856    -0.0009055148777068478  0.012920446413028808   -0.030753768844221097  
11/05/2013 -> 0.019476905954368462    -0.004473204627184146   -0.0024632893505701636 0.03919535462463708    
11/06/2013 -> 0.04203056768558944     0.0012040880256871667   -0.0028330781010720285 -0.019756535621632487  
11/07/2013 -> -0.01781037192247248    -0.014470789538010176   -0.01618290716424775   -0.03175895765472303   

val it: Frame<DateTime,string> =
  
              msft                  goog                 aapl                  fb                   
05/21/2012 -> 0.016306274370790463  0.022834776815456426 0.058264922098941074  -0.10986136541982727 
05/22/2012 -> 0.0003487966515520746 -0.02167364153001916 -0.007683661624071969 -0.089039083161916   
05/23/2012 -> -0.021966527196652708 0.014414114513981557 0.024399301816021124  0.032258064516129004

Summary statistics, volatility and correlation

Stats.describe summarises every column at once:

Stats.describe returns
val it: Frame<string,string> =
  
          msft                  goog                   aapl                   fb                     
unique -> 365                   370                    370                    368                    
mean   -> 0.0008848066626554045 0.0014991837393051174  0.00017014982272304036 0.001175904429556346   
std    -> 0.015138334103441365  0.014160309802394213   0.01847604712823422    0.03488665729220036    
min    -> -0.11395282750781466  -0.08006724112827435   -0.12355158453162096   -0.1169459962756052    
0.25   -> -0.006900609187201562 -0.005963396138735033  -0.009181137316708526  -0.016309550280494245  
0.5    -> 0.0003075677067334581 0.00019293620321392257 9.165741497829671E-05  -0.0012261549381270553 
0.75   -> 0.008185903160786268  0.008096426124321765   0.011486644933301537   0.01645885493487048    
max    -> 0.07286199444272912   0.13796284836688089    0.07211267605633798    0.2961146737080347

The standard deviation of daily returns is the usual measure of volatility. Here Facebook is clearly the most volatile:

returns |> Frame.getNumericCols |> Series.mapValues Stats.stdDev
val it: Series<string,float> =
  
msft -> 0.015138334103441365 
goog -> 0.014160309802394213 
aapl -> 0.01847604712823422  
fb   -> 0.03488665729220036

How closely do Microsoft and Google move together?

Stats.corr returns?msft returns?goog
val it: float = 0.287529089

Best days

Sort a series by value to find the largest one-day gains:

returns?msft |> Series.sortBy (fun v -> -v) |> Series.take 3
val it: Series<DateTime,float> =
  
08/23/2013 -> 0.07286199444272912  
10/25/2013 -> 0.059608540925266906 
11/06/2013 -> 0.04203056768558944

Rolling windows

Stats.movingMean computes a trailing average. Comparing a price to its 20-day average is a simple trend signal. Here we count how many days Microsoft traded above its average:

let ma20 = prices?msft |> Stats.movingMean 20
let aboveTrend =
  (prices?msft - ma20)
  |> Series.dropMissing
  |> Series.filterValues (fun v -> v > 0.0)

aboveTrend.KeyCount
val ma20: Series<DateTime,float> =
  
05/18/2012 -> <missing>          
05/21/2012 -> <missing>          
05/22/2012 -> <missing>          
05/23/2012 -> <missing>          
05/24/2012 -> <missing>          
05/25/2012 -> <missing>          
05/29/2012 -> <missing>          
05/30/2012 -> <missing>          
05/31/2012 -> <missing>          
06/01/2012 -> <missing>          
06/04/2012 -> <missing>          
06/05/2012 -> <missing>          
06/06/2012 -> <missing>          
06/07/2012 -> <missing>          
06/08/2012 -> <missing>          
...        -> ...                
10/18/2013 -> 33.6495            
10/21/2013 -> 33.762             
10/22/2013 -> 33.8685            
10/23/2013 -> 33.931             
10/24/2013 -> 33.978500000000004 
10/25/2013 -> 34.1015            
10/28/2013 -> 34.21600000000001  
10/29/2013 -> 34.313             
10/30/2013 -> 34.394000000000005 
10/31/2013 -> 34.471500000000006 
11/01/2013 -> 34.554             
11/04/2013 -> 34.686             
11/05/2013 -> 34.8675            
11/06/2013 -> 35.123             
11/07/2013 -> 35.309999999999995 

val aboveTrend: Series<DateTime,float> =
  
06/15/2012 -> 0.7665000000000006  
06/18/2012 -> 0.5595000000000034  
06/19/2012 -> 1.3439999999999976  
06/20/2012 -> 1.5174999999999983  
06/21/2012 -> 0.6979999999999968  
06/22/2012 -> 1.1594999999999942  
06/25/2012 -> 0.3204999999999991  
06/26/2012 -> 0.44849999999999923 
06/27/2012 -> 0.5484999999999971  
06/28/2012 -> 0.2639999999999958  
06/29/2012 -> 0.8209999999999908  
07/02/2012 -> 0.6939999999999884  
07/03/2012 -> 0.7754999999999903  
07/05/2012 -> 0.6504999999999868  
07/06/2012 -> 0.11449999999998184 
...        -> ...                 
10/16/2013 -> 1.1630000000000038  
10/17/2013 -> 1.3790000000000049  
10/18/2013 -> 1.3104999999999976  
10/21/2013 -> 1.2280000000000015  
10/22/2013 -> 0.7115000000000009  
10/25/2013 -> 1.6284999999999954  
10/28/2013 -> 1.353999999999992   
10/29/2013 -> 1.2070000000000007  
10/30/2013 -> 1.1459999999999937  
10/31/2013 -> 0.9384999999999906  
11/01/2013 -> 0.9759999999999991  
11/04/2013 -> 1.2539999999999978  
11/05/2013 -> 1.7725000000000009  
11/06/2013 -> 3.057000000000002   
11/07/2013 -> 2.190000000000005   

val it: int = 225

Expanding functions use all history up to each point. The running maximum gives the maximum drawdown (the worst fall from a previous peak):

let drawdown = prices?msft / Stats.expandingMax prices?msft - 1.0
Stats.min drawdown
val drawdown: Series<DateTime,float> =
  
05/18/2012 -> 0                      
05/21/2012 -> 0                      
05/22/2012 -> 0                      
05/23/2012 -> -0.021966527196652708  
05/24/2012 -> -0.02336122733612267   
05/25/2012 -> -0.023709902370990243  
05/29/2012 -> -0.00662482566248257   
05/30/2012 -> -0.014295676429567639  
05/31/2012 -> -0.019177126917712672  
06/01/2012 -> -0.043933054393305415  
06/04/2012 -> -0.040794979079497806  
06/05/2012 -> -0.04218967921896799   
06/06/2012 -> -0.013947001394700065  
06/07/2012 -> -0.0177824267782426    
06/08/2012 -> -0.0038354253835425345 
...        -> ...                    
10/18/2013 -> -0.029428095502498697  
10/21/2013 -> -0.02859522487506938   
10/22/2013 -> -0.03997779011660196   
10/23/2013 -> -0.06274292059966702   
10/24/2013 -> -0.06385341476957263   
10/25/2013 -> -0.008051082731815873  
10/28/2013 -> -0.01249305941143819   
10/29/2013 -> -0.013881177123820088  
10/30/2013 -> -0.013325930038867395  
10/31/2013 -> -0.01693503609106073   
11/01/2013 -> -0.013603553581343797  
11/04/2013 -> -0.002220988339811325  
11/05/2013 -> 0                      
11/06/2013 -> 0                      
11/07/2013 -> -0.01781037192247248   

val it: float = -0.1547033759

Downsample to monthly data

Series.resampleEquiv groups observations whose keys map to the same value. Mapping every date to the first of its month and taking the last price in each group gives month-end prices:

let monthly =
  prices
  |> Frame.mapCols (fun _ s ->
      s.As<float>()
      |> Series.resampleEquiv (fun d -> DateTime(d.Year, d.Month, 1))
      |> Series.mapValues Series.lastValue
      :> ISeries<_>)

monthly |> Frame.take 3
val monthly: Frame<DateTime,string> =
  
              msft  goog    aapl   fb    
05/01/2012 -> 28.13 580.86  558.61 29.6  
06/01/2012 -> 29.48 580.07  564.67 31.1  
07/01/2012 -> 28.4  632.97  590.55 21.71 
08/01/2012 -> 29.9  685.09  645.99 18.06 
09/01/2012 -> 28.87 754.5   647.79 21.66 
10/01/2012 -> 27.68 680.3   578.09 21.11 
11/01/2012 -> 26.03 698.37  570.94 28    
12/01/2012 -> 26.12 707.38  519.13 26.62 
01/01/2013 -> 26.85 755.69  444.33 30.98 
02/01/2013 -> 27.41 801.2   433.09 27.25 
03/01/2013 -> 28.21 794.19  434.33 25.58 
04/01/2013 -> 32.64 824.57  434.45 27.77 
05/01/2013 -> 34.66 871.22  444.19 24.35 
06/01/2013 -> 34.3  880.37  391.64 24.88 
07/01/2013 -> 31.62 887.75  446.95 36.8  
08/01/2013 -> 33.4  846.9   484.39 41.29 
09/01/2013 -> 33.28 875.91  473.98 50.23 
10/01/2013 -> 35.41 1030.58 519.67 50.21 
11/01/2013 -> 37.5  1007.95 512.49 47.56 

val it: Frame<DateTime,string> =
  
              msft  goog   aapl   fb    
05/01/2012 -> 28.13 580.86 558.61 29.6  
06/01/2012 -> 29.48 580.07 564.67 31.1  
07/01/2012 -> 28.4  632.97 590.55 21.71

Where next?

namespace System
namespace Deedle
val root: string
val fsi: FSharp.Compiler.Interactive.InteractiveSession
member FSharp.Compiler.Interactive.InteractiveSession.AddPrinter: ('T -> string) -> unit
val o: obj
type obj = Object
val iface: Type
Object.GetType() : Type
val fmt: Reflection.MethodInfo
Type.GetMethod(name: string) : Reflection.MethodInfo
   (+0 other overloads)
Type.GetMethod(name: string, types: Type array) : Reflection.MethodInfo
   (+0 other overloads)
Type.GetMethod(name: string, bindingAttr: Reflection.BindingFlags) : Reflection.MethodInfo
   (+0 other overloads)
Type.GetMethod(name: string, types: Type array, modifiers: Reflection.ParameterModifier array) : Reflection.MethodInfo
   (+0 other overloads)
Type.GetMethod(name: string, bindingAttr: Reflection.BindingFlags, types: Type array) : Reflection.MethodInfo
   (+0 other overloads)
Type.GetMethod(name: string, genericParameterCount: int, types: Type array) : Reflection.MethodInfo
   (+0 other overloads)
Type.GetMethod(name: string, genericParameterCount: int, types: Type array, modifiers: Reflection.ParameterModifier array) : Reflection.MethodInfo
   (+0 other overloads)
Type.GetMethod(name: string, genericParameterCount: int, bindingAttr: Reflection.BindingFlags, types: Type array) : Reflection.MethodInfo
   (+0 other overloads)
Type.GetMethod(name: string, bindingAttr: Reflection.BindingFlags, binder: Reflection.Binder, types: Type array, modifiers: Reflection.ParameterModifier array) : Reflection.MethodInfo
   (+0 other overloads)
Type.GetMethod(name: string, bindingAttr: Reflection.BindingFlags, binder: Reflection.Binder, callConvention: Reflection.CallingConventions, types: Type array, modifiers: Reflection.ParameterModifier array) : Reflection.MethodInfo
   (+0 other overloads)
Reflection.MethodBase.Invoke(obj: obj, parameters: obj array) : obj
Reflection.MethodBase.Invoke(obj: obj, invokeAttr: Reflection.BindingFlags, binder: Reflection.Binder, parameters: obj array, culture: Globalization.CultureInfo) : obj
Multiple items
val string: value: 'T -> string

--------------------
type string = String
val loadStock: ticker: string -> Frame<DateTime,string>
val ticker: 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 * hasHeaders: Nullable<bool> * inferTypes: Nullable<bool> * inferRows: Nullable<int> * schema: string * separators: string * culture: string * maxRows: Nullable<int> * missingValues: string array * preferOptions: bool * 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>> static member NonExpandableInterfaces: ResizeArray<Type> static member NonExpandableTypes: HashSet<Type>
<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 After: lowerExclusive: 'TRowKey -> Frame<'TRowKey,'TColumnKey> member AggregateRowsBy: groupBy: 'TColumnKey seq * aggBy: 'TColumnKey seq * aggFunc: Func<Series<'TRowKey,'a>,'b> -> Frame<int,'TColumnKey> member Before: upperExclusive: 'TRowKey -> Frame<'TRowKey,'TColumnKey> ...
<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>
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 * hasHeaders: Nullable<bool> * inferTypes: Nullable<bool> * inferRows: Nullable<int> * schema: string * separators: string * culture: string * maxRows: Nullable<int> * missingValues: string array * preferOptions: Nullable<bool> * encoding: Text.Encoding -> 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 array * preferOptions: bool * 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 indexRowsDateTime: column: 'C -> frame: Frame<'R1,'C> -> Frame<DateTime,'C> (requires equality and equality)
<summary> Returns a data frame whose rows are indexed based on the specified column of the original data frame. This function casts (or converts) the column key to values of type `DateTime` (a generic variant that may require some type annotation is `Frame.indexRows`) The specified column is removed from the resulting frame. </summary>
<param name="frame">Source data frame whose row index is to be replaced.</param>
<param name="column">The name of a column in the original data frame that will be used for the new index. Note that the values in the column need to be unique.</param>
<category>Sorting and index manipulation</category>
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>
val take: count: int -> frame: Frame<'R,'C> -> Frame<'R,'C> (requires equality and equality)
<summary> Returns a frame that contains the specified `count` of rows from the original frame; `count` must be smaller or equal to the original number of rows. &lt;category&gt;Frame transformations&lt;/category&gt; </summary>
val closes: Frame<DateTime,string>
val frame: columns: ('a * #ISeries<'c>) seq -> Frame<'c,'a> (requires equality and equality)
<summary> A function for constructing data frame from a sequence of name - column pairs. This provides a nicer syntactic sugar for `Frame.ofColumns`. </summary>
<example> To create a simple frame with two columns, you can write: <code> frame [ "A" =&gt; series [ 1 =&gt; 30.0; 2 =&gt; 35.0 ] "B" =&gt; series [ 1 =&gt; 30.0; 3 =&gt; 40.0 ] ] </code></example>
<category>Frame construction</category>
val t: string
val prices: Frame<DateTime,string>
val dropSparseRows: frame: Frame<'R,'C> -> Frame<'R,'C> (requires equality and equality)
<summary> Creates a new data frame that contains only those rows of the original data frame that are _dense_, meaning that they have a value for each column. The resulting data frame has the same number of columns, but may have fewer rows (or no rows at all). </summary>
<category>Missing values</category>
property Frame.RowCount: int with get
property Frame.Rows: RowSeries<DateTime,string> with get
<category>Accessors and slicing</category>
Multiple items
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 returns: Frame<DateTime,string>
val shift: offset: int -> frame: Frame<'R,'C> -> Frame<'R,'C> (requires equality and equality)
<summary> Returns a frame with columns shifted by the specified offset. When the offset is positive, the values are shifted forward and first `offset` keys are dropped. When the offset is negative, the values are shifted backwards and the last `offset` keys are dropped. Expressed in pseudo-code: result[k] = series[k - offset] </summary>
<param name="offset">Can be both positive and negative number.</param>
<param name="frame">The input frame whose columns are to be shifted.</param>
<remarks> If you want to calculate the difference, e.g. `df - (Frame.shift 1 df)`, you can use `Frame.diff` which will be a little bit faster. </remarks>
<category>Frame transformations</category>
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.describe: frame: Frame<'R,'C> -> Frame<string,'C> (requires equality and equality)
static member Stats.describe: series: Series<'K,'V> -> Series<string,float> (requires equality and equality)
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>
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>
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.stdDev: frame: Frame<'R,'C> -> Series<'C,float> (requires equality and equality)
static member Stats.stdDev: series: Series<'K,'V> -> float (requires equality)
static member Stats.corr: series1: Series<'K,'V1> -> series2: Series<'K,'V2> -> float (requires equality)
val sortBy: proj: ('T -> 'V) -> series: Series<'K,'T> -> Series<'K,'T> (requires comparison and equality)
<summary> Returns a new series, containing the observations of the original series sorted by values returned by the specified projection function. Sorting by value reads the whole series and materializes a linear result, including on virtual series. To keep a virtual series, use <c>Series.sortByKey</c> when the index is already ordered (a no-op). </summary>
<param name="series">An input series whose values are sorter</param>
<param name="proj">A projection function that returns a value to be compared for each value contained in the original input series.</param>
<category>Sorting and index manipulation</category>
val v: float
val take: count: int -> series: Series<'K,'T> -> Series<'K,'T> (requires equality)
<summary> Returns a series that contains the specified number of keys from the original series. </summary>
<param name="count">Number of keys to take; must be smaller or equal to the original number of keys</param>
<param name="series">Input series from which the keys are taken</param>
<category>Series transformations</category>
val ma20: Series<DateTime,float>
static member Stats.movingMean: size: int -> series: Series<'K,'V> -> Series<'K,float> (requires equality)
val aboveTrend: Series<DateTime,float>
val dropMissing: series: Series<'K,'T> -> Series<'K,'T> (requires equality)
<summary> Drop missing values from the specified series. The returned series contains only those keys for which there is a value available in the original one. On a virtual series this scans once for present addresses, then keeps a virtual sub-vector (it does not copy values into a linear array). </summary>
<param name="series">An input series to be filtered</param>
<example><code> let s = series [ 1 =&gt; 1.0; 2 =&gt; Double.NaN ] s |&gt; Series.dropMissing // val it : Series&lt;int,float&gt; = series [ 1 =&gt; 1] </code></example>
<category>Missing values</category>
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>
property Series.KeyCount: int with get
<summary> Returns the total number of keys in the specified series. This returns the total length of the series, including keys for which there is no value available. </summary>
<category>Series data</category>
val drawdown: Series<DateTime,float>
static member Stats.expandingMax: series: Series<'K,'V> -> Series<'K,float> (requires equality)
static member Stats.min: frame: Frame<'R,'C> -> Series<'C,float> (requires equality and equality)
static member Stats.min: series: Series<'K,'V> -> float (requires equality)
val monthly: Frame<DateTime,string>
val mapCols: f: ('C -> ObjectSeries<'R> -> #ISeries<'b>) -> frame: Frame<'R,'C> -> Frame<'b,'C> (requires equality and equality and equality)
<summary> Builds a new data frame whose columns are the results of applying the specified function on the columns of the input data frame. The function is called with the column key and object series that represents the column data. </summary>
<param name="frame">Input data frame to be transformed</param>
<param name="f">Function of two arguments that defines the column mapping</param>
<category>Frame transformations</category>
val s: ObjectSeries<DateTime>
member ObjectSeries.As: unit -> Series<'K,'R>
Multiple items
val float: value: 'T -> float (requires member op_Explicit)

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

--------------------
type float<'Measure> = float
val resampleEquiv: keyProj: ('K1 -> 'K2) -> series: Series<'K1,'V1> -> Series<'K2,Series<'K1,'V1>> (requires equality and equality)
<summary> Resample the series based on equivalence class on the keys. A specified function `keyProj` is used to project keys to another space and the observations for which the projected keys are equivalent are grouped into chunks. The chunks are then returned as nested series. </summary>
<param name="series">An input series to be resampled</param>
<param name="keyProj">A function that transforms keys from original space to a new space (which is then used for grouping based on equivalence)</param>
<remarks> This function is similar to `Series.chunkBy`, with the exception that it transforms keys to a new space. This operation is only supported on ordered series. The method throws `InvalidOperationException` when the series is not ordered. For unordered series, similar functionality can be implemented using `Series.groupBy`. </remarks>
<category>Sampling, resampling and advanced lookup</category>
val d: 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>
val lastValue: series: Series<'K,'V> -> 'V (requires equality)
<summary> Returns the last value of the series. This fails if the last value is missing. </summary>
<category>Accessing series data and lookup</category>
type ISeries<'K (requires equality)> = abstract TryGetObject: 'K -> OptionalValue<obj> abstract Index: IIndex<'K> abstract Vector: IVector abstract VectorBuilder: IVectorBuilder
<summary> Represents an untyped series with keys of type `K` and values of some unknown type (This type should not generally be used directly, but it can be used when you need to write code that works on a sequence of series of heterogeneous types). </summary>
<category>Core frame and series types</category>

Type something to start searching.