Подтвердить что ты не робот

Работа с гетерогенными данными на статически типизированном языке (F #)

Одна из претензий F # заключается в том, что она позволяет создавать интерактивные скрипты и манипулировать данными/исследования. Я играл с F #, пытаясь понять, как он сравнивается с Matlab и R для работы анализа данных. Очевидно, что F # не обладает всей практической функциональностью этих экосистем, но меня больше интересуют общие преимущества/недостатки базового языка.

Для меня самое большое изменение, даже над функциональным стилем, заключается в том, что F # статически типизирован. У этого есть некоторое обращение, но также часто чувствует себя как смирительная рубашка. Например, я не нашел удобного способа работы с разнородными прямоугольными данными - думаю, данные в рамке в R. Предположим, что я читаю CSV файл с именами (строками) и весами (float). Обычно я загружаю данные, выполняю некоторые преобразования, добавляю переменные и т.д., А затем запускаю анализ. В R первая часть может выглядеть так:

df <- read.csv('weights.csv')
df$logweight <- log(df$weight)

В F # не понятно, какую структуру я должен использовать для этого. Насколько я могу судить, у меня есть два варианта: 1) Я могу определить класс, который строго типизирован (Expert F # 9.10) или 2) Я могу использовать гетерогенный контейнер, такой как ArrayList. Статически типизированный класс не представляется возможным, потому что после загрузки данных мне нужно добавить переменные ad hoc (logweight). Неоднородный контейнер также неудобен, потому что каждый раз, когда я обращаюсь к переменной, мне нужно будет ее удалить. В F #:

let df = readCsv("weights.csv")
df.["logweight"] = log(double df.["weight"])

Если это было один или два раза, это может быть хорошо, но указание типа каждый раз, когда я использую переменную, не кажется разумным. Я часто занимаюсь опросами с 100 с переменными, которые добавляются/удаляются, разбиваются на новые подмножества и сливаются с другими файлами данных.

Я пропустил какой-то очевидный третий выбор? Есть ли какой-нибудь интересный и легкий способ взаимодействия и манипулирования разнородными данными? Если мне нужно провести анализ данных на .Net, то мой нынешний смысл в том, что я должен использовать IronPython для всех работ по исследованию/преобразованию данных/взаимодействию данных и использовать только F #/С# для численно-интенсивных деталей. Является ли F # неотвратимым инструментом для быстрой и грязной гетерогенной работы?

4b9b3361

Ответ 1

Я думаю, что есть еще несколько вариантов.

(?) оператор

Как упоминал Брайан, вы можете использовать оператор (?):

type dict<'a,'b> = System.Collections.Generic.Dictionary<'a,'b>

let (?) (d:dict<_,_>) key = unbox d.[key]
let (?<-) (d:dict<_,_>) key value = d.[key] <- box value

let df = new dict<string,obj>()
df?weight <- 50.
df?logWeight <- log(df?weight)

Это использует бокс /unboxing для каждого доступа, и иногда вам может потребоваться добавить аннотации типов:

(* need annotation here, since we could try to unbox at any type *)
let fltVal = (df?logWeight : float)

Идентификаторы верхнего уровня

Другая возможность заключается в том, что вместо динамического определения свойств существующих объектов (которые F # не поддерживает особенно хорошо), вы можете просто использовать идентификаторы верхнего уровня.

let dfLogWeight = log(dfWeight)

Это имеет то преимущество, что вам почти не нужно будет указывать типы, но может помешать вашему пространству имен верхнего уровня.

Объекты свойств

Последняя опция, требующая немного более типизирующего и уродливого синтаксиса, состоит в создании строго типизированных "объектов свойств":

type 'a property = System.Collections.Generic.Dictionary<obj,'a>

let createProp() : property<'a> = new property<'a>()
let getProp o (prop:property<'a>) : 'a = prop.[o]
let setProp o (prop:property<'a>) (value:'a) = prop.[o] <- value

let df = new obj()
let (weight : property<double>) = createProp()
let (logWeight : property<double>) = createProp()

setProp df weight 50.
setProp df logWeight (getProp df weight)
let fltVal = getProp df logWeight

Для этого требуется, чтобы каждое свойство было явно создано (и требует в этом случае аннотации типа), но после этого не потребуется никаких аннотаций типа. Я нахожу это гораздо менее удобочитаемым, чем другие варианты, хотя, возможно, определение оператора для замены getProp немного облегчит его.

Ответ 2

Является ли F # неотвратимым инструментом для быстрые и грязные разнородные данные работать?

Для полностью ad hoc, поисковых интеллектуальных данных, я бы не рекомендовал F #, поскольку типы будут мешать вам.

Однако, если ваши данные очень хорошо определены, вы можете хранить разрозненные типы данных в одном контейнере, сопоставляя все свои типы с общим объединением F #:

> #r "FSharp.PowerPack";;

--> Referenced 'C:\Program Files\FSharp-1.9.6.16\bin\FSharp.PowerPack.dll'

> let rawData =
    "Name: Juliet
     Age: 23
     Sex: F
     Awesome: True"

type csv =
    | Name of string
    | Age of int
    | Sex of char
    | Awesome of bool

let parseData data =
    String.split ['\n'] data
    |> Seq.map (fun s ->
        let parts = String.split [':'] s
        match parts.[0].Trim(), parts.[1].Trim() with
        | "Name", x -> Name(x)
        | "Age", x -> Age(int x)
        | "Sex", x -> Sex(x.[0])
        | "Awesome", x -> Awesome(System.Convert.ToBoolean(x))
        | data, _ -> failwithf "Unknown %s" data)
    |> Seq.to_list;;

val rawData : string =
  "Name: Juliet
     Age: 23
     Sex: F
     Awesome: True"
type csv =
  | Name of string
  | Age of int
  | Sex of char
  | Awesome of bool
val parseData : string -> csv list

> parseData rawData;;
val it : csv list = [Name "Juliet"; Age 23; Sex 'F'; Awesome true]

csv list строго типизирован, и вы можете сопоставить его по шаблону, но вы должны определить все свои конструкторы объединения вперед.

Я лично предпочитаю этот подход, поскольку на порядки лучше, чем работа с нетипизированным ArrayList. Тем не менее, я не совсем уверен, что вы требования, и я не знаю, хороший способ представить ad-hoc переменные (за исключением, возможно, как Map{string, obj}), поэтому YMMV.

Ответ 3

Я не уверен, что F # - отличный инструмент здесь или нет. Но есть третий вариант - оператор вопросительного знака. Я уже давно хотел сообщить об этом в блог; Luca недавний разговор PDC продемонстрировал CSV-ридер с С# 'dynamic', и я хотел закодировать аналогичную вещь с F #, используя (?). См

Оператор F # ""

для краткого описания. Вы можете попытаться вспыхнуть вперед и поиграть с этим самостоятельно, или дождаться, когда я расскажу об этом. Я не пробовал это сам всерьез, поэтому я не уверен, насколько хорошо это сработает.

ИЗМЕНИТЬ

Я должен добавить, что разговор Luca показывает, как "динамический" в С# адресовать хотя бы часть этого вопроса для этого языка.

ИЗМЕНИТЬ

См. также

http://cs.hubfs.net/forums/thread/12622.aspx

где я размещаю некоторый базовый CSV-код стартера.