Вычисление ленивой корреляционной матрицы в F # - PullRequest
3 голосов
/ 01 октября 2010

Мне нужно вычислить матрицу корреляции для векторов, содержащихся в CSV-файле объемом 5 ГБ.Каждая строка содержит одно наблюдение для каждой случайной величины.Чтобы сделать это, я написал следующее:

let getCorrMatrix data =   

    let getMatrixInfo nCol (count,crossProd:float array array,sumVector:float array,sqVector:float array) (newLine:float array)   = 

        for i in 0..(nCol-1) do
                sumVector.[i]<-sumVector.[i]+newLine.[i]
                sqVector.[i]<-sqVector.[i]+(newLine.[i]*newLine.[i])
                for j in (i+1)..(nCol-1)  do
                    crossProd.[i].[j-(i+1)]<-crossProd.[i].[j-(i+1)]+newLine.[i]*newLine.[j] 

        let newCount = count+1
        //(newCount,newMatrix,newSumVector,newSqVector)    
        (newCount,crossProd,sumVector,sqVector)         

    //Get number of columns
    let nCol = data|>Seq.head|>Seq.length

    //Initialize objects for the fold
    let matrixStart = Array.init nCol (fun i -> Array.create (nCol-i-1) 0.0)                    
    let sumVector = Array.init nCol (fun _ -> 0.0)
    let sqVector = Array.init nCol (fun _ -> 0.0)

    let init = (0,matrixStart,sumVector,sqVector)

    //Run the fold and obtain all the elements to build te correlation matrix
    let (count,crossProd,sum,sq) = 
        data
        |>PSeq.fold(getMatrixInfo nCol) init

    //Compute averages standard deviations, and finally correlations
    let averages = sum|>Array.map(fun s ->s/(float count))
    let std = Array.zip3 sum sq averages
              |> Array.map(fun (elemSum,elemSq,av)-> let temp = elemSq-2.0*av*elemSum+float(count)*av*av 
                                                     sqrt (temp/(float count-1.0)))

    //Map allteh elements to correlation                                         
    let rec getCorr i j =
        if i=j then
            1.0
        elif i<j then
            (crossProd.[i].[j-(i+1)]-averages.[i]*sum.[j]-averages.[j]*sum.[i]+(float count*averages.[i]*averages.[j]) )/((float count-1.0)*std.[i]*std.[j])
        else
            getCorr j i

    let corrMatrix =  Array2D.init nCol nCol (fun i j -> getCorr i j)

    corrMatrix 

Я проверил его на R-вычисления и он соответствует.Так как я планирую использовать это снова и снова, если у вас есть какие-либо отзывы (или обнаружите ошибку), это будет с благодарностью.(Обратите внимание, я публикую это, потому что подумал, что это может быть полезно и другим).

Спасибо

1 Ответ

2 голосов
/ 02 октября 2010

Основная проблема в следующем коде:

    //Update crossproduct
    let newMatrix = 
        [| for i in 0..(nCol-1) do
             yield [| for j in (i+1)..(nCol-1)  -> crossProd.[i].[j-(i+1)]+newLine.[i]*newLine.[j] |]
                                   |]

Вы создаете новую матрицу для каждой строки в вашем data. Это неэффективно, вы можете использовать только одну такую ​​матрицу.

Есть несколько незначительных символов F #:

  1. Используйте sqrt в качестве ярлыка для System.Math.Sqrt.

  2. Избегайте использования списков для инициализации простых массивов. Например. твой код

    let matrixStart = [| for i in 0..(nCol-1) do
                         yield [| for j in (i+1)..(nCol-1)  ->  0.0 |]
                                   |]
    

    может быть написано с использованием стандартных процедур:

    let matrixStart = Array.init nCol (fun i -> Array.create (nCol-i-1) 0.0)
    

    другой пример, для

    let corrMatrix = 
        [| for i in 0..(nCol-1) do
           yield [| for j in 0..(nCol-1)  -> getCorr i j |]
                                       |]
    

    вместо float [][] вы можете использовать float [,] и написать

    let corrMatrix = Array2D.init nCol nCol (fun i j -> getCorr i j)
    
...