Write F# DataTable to CSV file - f#

I am trying to write an F# DataTable to csv (or output in a txt). The table I have is defined as follows:
let setup_report_tbl ( tbl : DataTable ) =
ignore( tbl.Columns.Add("business_date", typeof<System.Int32>) )
ignore( tbl.Columns.Add("ticker", typeof<System.String>) )
ignore( tbl.Columns.Add("price", typeof<System.String>) )
ignore( tbl.Columns.Add("rate", typeof<System.Boolean>) )
ignore( tbl.Columns.Add("range", typeof<System.Double>) )
My goal is to write this empty table with headers into a csv or txt. I'm new to F# and not quite sure where to start here, any help is appreciated thanks!

To write a DataTable as CSV, I would do something like this:
open System
open System.IO
open System.Data
let writeCsv (wtr : StreamWriter) (tbl : DataTable) =
let writeValues (values : seq<_>) =
String.Join(',', values)
|> wtr.WriteLine
tbl.Columns
|> Seq.cast<DataColumn>
|> writeValues
for row in tbl.Rows do
row.ItemArray |> writeValues
Note that I haven't done anything to check for special characters in the values, such commas or quotes.
Example:
let tbl = new DataTable()
setup_report_tbl tbl
tbl.Rows.Add(1, "moo", "baa", true, 2.0) |> ignore
use wtr = new StreamWriter(Console.OpenStandardOutput())
writeCsv wtr tbl
Output is:
business_date,ticker,price,rate,range
1,moo,baa,True,2
Update
To avoid compiler error, perhaps try this:
let writeValues (values : seq<_>) =
let s = String.Join(',', values)
wtr.WriteLine(s)
Note that s is a string, so there should be no ambiguity in which version of WriteLine is called.

If you wanted to use an existing library rather than writing your own CSV encoding (which may get tricky when you need to escape things), you could use Deedle which has an easy way to create data frame from a DataTable and save it to a CSV file:
#r "nuget: Deedle"
open Deedle
open System.Data
// Setup table using your function and add some data
let tbl = new DataTable()
setup_report_tbl tbl
tbl.Rows.Add(1, "very\",evil'ticker", "$42", false, 1.23)
// Turn it into a dataframe and save it
let df = Frame.ReadReader(tbl.CreateDataReader())
df.SaveCsv("C:/temp/test.csv")
As a bonus point, you could see if the data frame type from Deedle lets you do some of the other things you want to do with the data - but this depends on your scenario.

Related

mselect function of drc package does not work inside a function

I wanted to use mselec function from drc package for selecting the best model of a dose-response dataset. However, mselect does not work if you use it inside a function.
The following code works:
library(drc)
ryegrass.m1 <- drm(rootl~conc, data = ryegrass, fct = LL.4())
mselect(ryegrass.m1,list(LL.5(), LN.4(), W1.4(), W2.4()))
But not this one:
best.fit=function(data){
model1=drm(rootl~conc, data=data, fct=LL.4())
M1=drc::mselect(model1, list(LL.5(), LN.4(), W1.4(), W2.4()))
return(M1)
}
best.fit(ryegrass)
I think the problem is related with the environments in R, but I don't know how to fix it. Someone could help me please?
I manage to solve the problem like this:
best.fit=function(data){
mf <- match.call(expand.dots = FALSE)
m <- match(c("data"), names(mf), 0L)
data.name=as.character(mf[m])
model1=eval(parse(text=paste0('drm(rootl~conc, data=',data.name, ',fct=LL.4())')))
M1=drc::mselect(model1, list(LL.5(), LN.4(), W1.4(), W2.4()))
return(M1)
}
best.fit(ryegrass)
There should be better ways to do it, but at least it works for me now.
It seems that the update function within mselect doesn't access the original dataframe when run from inside a function. My solution was to add a data argument at line 34.
[1] my_mselect <- function(...
...
[33] tempObj <- try(update(object, fct = fctList[[i]],
[34] data = object$origData), # <--- line added here
[35] silent = TRUE)
I also noticed that the reference to the model variables doens't work either if their relative positions are used instead of their original names, for instance when using drm(data[, 1] ~ data[, 2], fct = LL.4()). To avoid this you can use a temporary dataframe in your function, set the variable names as you want, and use these names in the drm call.
best.fit <- function(data){
tmp_data <- data
names(tmp_data) <- c("Var1", "Var2")
model1 <- drm(Var1 ~ Var2, data = tmp_data, fct = LL.4())
M1 <- my_mselect(model1, list(LL.5(), LN.4(), W1.4(), W2.4()))
return(M1)
}

how to use mnesia:select/4 and mnesia:select/1 for paging query

a table named "md" with structure {id,name},I want read records from md use paging query,I tried mnesia:select/4 and mnesia:select/1 as below:
%% first use select/2: "ID < 10",returned [1,2,4,3,8,5,9,7,6]
(ejabberd#localhost)5> mnesia:activity(transaction,fun mnesia:select/2,md, [{{md,'$1','_'},[{'<','$1',10}],['$1']}]).
{atomic,[1,2,4,3,8,5,9,7,6]}
%%but when query with select/4,returned [6], why?
(ejabberd#localhost)7> {atomic,{R1,C1}}=mnesia:activity(transaction,fun mnesia:select/4,md,[{{md,'$1','_'},[{'<','$1',10}],['$1']}],5,read).
{atomic,{[6],
{mnesia_select,md,
{tid,10535470,<0.460.0>},
ejabberd#localhost,disc_only_copies,
{dets_cont,select,5,
<<0,0,0,29,18,52,86,120,0,0,0,21,131,104,3,...>>,
{141720,148792,<<>>},
md,<0.130.0>,<<>>},
[],undefined,undefined,
[{{md,'$1','_'},[{'<','$1',10}],['$1']}]}}}
%% and then use mnesia:select/1 with continuation "C1",got wrong_transaction
(ejabberd#localhost)8> mnesia:activity(transaction,fun mnesia:select/1,C1).
{aborted,wrong_transaction}
how to use mnesia:select/4 and mnesia:select/1 for paging query?
You will have to call select/1 inside the same transaction.
Otherwise the table can change between invocations to select/4 and select/1.
You must use a dirty context if you want to use is as written above.
here is my solution:
use async_dirty instead of transaction
{Record,Cont}=mnesia:activity(async_dirty, fun mnesia:select/4,[md,[{Match_head,[Guard],[Result]}],Limit,read])
then read next Limit number of records:
mnesia:activity(async_dirty, fun mnesia:select/1,[Cont])
full code:
-record(md,{id,name}).
batch_delete(Id,Limit) ->
Match_head = #md{id='$1',name='$2'},
Guard = {'<','$1',Id},
Result = '$_',
{Record,Cont} = mnesia:activity(async_dirty, fun mnesia:select/4,[md,[{Match_head,[Guard],[Result]}],Limit,read]),
delete_next({Record,Cont}).
delete_next('$end_of_table') ->
over;
delete_next({Record,Cont}) ->
delete(Record),
delete_next(mnesia:activity(async_dirty, fun mnesia:select/1,[Cont])).
delete(Records) ->
io:format("delete(~p)~n",[Records]),
F = fun() ->
[ mnesia:delete_object(O) || O <- Records]
end,
mnesia:transaction(F).

F# Excel Range.AutoFilter() fails

I'm trying to turn on AutoFilter for the users who will consume the data.
open Microsoft.Office.Interop.Excel
let xl = ApplicationClass()
xl.Workbooks.OpenText(fileName...)
let wb = xl.Workbooks.Item(1)
let ws = wb.ActiveSheet :?> Worksheet
let rows = string ws.UsedRange.Rows.Count
// AutoFilter method of Range class failed.
ws.Range("A7:I" + rows).AutoFilter() |> ignore
Thanks for any help you can offer.
According to the documentation, you need to pass 5 parameters to AutoFilter.
Unspecified parameters can be filled by System.Reflection.Missing.Value.
Something like
ws.Range("A7:I" + rows).AutoFilter(1, System.Reflection.Missing.Value,
Excel.XlAutoFilterOperator.xlAnd,
System.Reflection.Missing.Value, true)
|> ignore
should work.

kendoui websharper and failed to access method from another model

Learning something new is always following something what already exists. So I reconfigure this example (http://www.websharper.com/samples/KendoChart) which works fine. (only graph on line 12. I saved series into some variable and it looks like this.
So line 12 should be.
let Chart chartType stack =
let data = [|
chart.SeriesConfiguration (
Name = "World",
Data = [|15.7 ; 16.7 ; 20. ; 23.5; 26.6|]
)
chart.SeriesConfiguration (
Name = "United States",
Data = [|67.96 ; 68.93 ; 75. ; 74. ; 78.|]
)
|]
Where data is on line 36 Series = data... and that works fine.
But what if I want my data to be ...
let Chart chartType stack =
let techs = StoneMiner.Charts.technologies()
let data = seq { for i in techs do
yield chart.SeriesConfiguration (
Name = fst i ,
Data =[|snd i|]//; 16.7 ; 20. ; 23.5; 26.6|]
) }
|> Seq.toArray
where let techs = StoneMiner.Charts.technologies() is technologies is in another Namespace and module...
I get some strange error...
Error 1 Failed to translate a method call: technologies(..) [StoneMiner.Charts]. Because it is in another modul.
It seems your function StomeMiner.Charts.technologies doesn't have a [<JavaScript>] annotation. This prevents it from being translated into JavaScript and thus used on client-side.
As a rule of thumb, anything you use from the client-side needs to be annotated with one of the attributes from IntelliFactory.WebSharper.Core.Attributes (which are accessible directly when you open IntelliFactory.WebSharper). Depending on the situation, it can be [<JavaScript>] (for translation to JS), [<Inline "...">] (to inline some JS code), [<Remote>] (for Ajax RPC calls to the server) or less common ones like [<Stub>] or [<Direct>].

How do I create a multi-level TreeView using F#?

I would like to display a directory structure using Gtk# widgets through F#, but I'm having a hard time figuring out how to translate TreeViews into F#. Say I had a directory structure that looks like this:
Directory1
SubDirectory1
SubDirectory2
SubSubDirectory1
SubDirectory3
Directory2
How would I show this tree structure with Gtk# widgets using F#?
EDIT:
gradbot's was the answer I was hoping for with a couple of exceptions. If you use ListStore, you loose the ability to expand levels, if you instead use :
let musicListStore = new Gtk.TreeStore([|typeof<String>; typeof<String>|])
you get a layout with expandable levels. Doing this, however, breaks the calls to AppendValues so you have to add some clues for the compiler to figure out which overloaded method to use:
musicListStore.AppendValues (iter, [|"Fannypack" ; "Nu Nu (Yeah Yeah) (double j and haze radio edit)"|])
Note that the columns are explicitly passed as an array.
Finally, you can nest levels even further by using the ListIter returned by Append Values
let iter = musicListStore.AppendValues ("Dance")
let subiter = musicListStore.AppendValues (iter, [|"Fannypack" ; "Nu Nu (Yeah Yeah) (double j and haze radio edit)"|])
musicListStore.AppendValues (subiter, [|"Some Dude"; "Some Song"|]) |> ignore
I'm not exactly sure what you're looking for but here is a translated example from their tutorials. It may help you get started. Image taken from tutorial site.
I think the key to a multi-level tree view is to append values to values, iter in this line musicListStore.AppendValues (iter, "Fannypack", "Nu Nu (Yeah Yeah) (double j and haze radio edit)") |> ignore
// you will need to add these references gtk-sharp, gtk-sharp, glib-sharp
// and set the projects running directory to
// C:\Program Files (x86)\GtkSharp\2.12\bin\
module SOQuestion
open Gtk
open System
let main() =
Gtk.Application.Init()
// Create a Window
let window = new Gtk.Window("TreeView Example")
window.SetSizeRequest(500, 200)
// Create our TreeView
let tree = new Gtk.TreeView()
// Add our tree to the window
window.Add(tree)
// Create a column for the artist name
let artistColumn = new Gtk.TreeViewColumn()
artistColumn.Title <- "Artist"
// Create the text cell that will display the artist name
let artistNameCell = new Gtk.CellRendererText()
// Add the cell to the column
artistColumn.PackStart(artistNameCell, true)
// Create a column for the song title
let songColumn = new Gtk.TreeViewColumn()
songColumn.Title <- "Song Title"
// Do the same for the song title column
let songTitleCell = new Gtk.CellRendererText()
songColumn.PackStart(songTitleCell, true)
// Add the columns to the TreeView
tree.AppendColumn(artistColumn) |> ignore
tree.AppendColumn(songColumn) |> ignore
// Tell the Cell Renderers which items in the model to display
artistColumn.AddAttribute(artistNameCell, "text", 0)
songColumn.AddAttribute(songTitleCell, "text", 1)
let musicListStore = new Gtk.ListStore([|typeof<String>; typeof<String>|])
let iter = musicListStore.AppendValues ("Dance")
musicListStore.AppendValues (iter, "Fannypack", "Nu Nu (Yeah Yeah) (double j and haze radio edit)") |> ignore
let iter = musicListStore.AppendValues ("Hip-hop")
musicListStore.AppendValues (iter, "Nelly", "Country Grammer") |> ignore
// Assign the model to the TreeView
tree.Model <- musicListStore
// Show the window and everything on it
window.ShowAll()
// add event handler so Gtk will exit
window.DeleteEvent.Add(fun _ -> Gtk.Application.Quit())
Gtk.Application.Run()
[<STAThread>]
main()

Resources