Группировка столбцов в Deedle - PullRequest
0 голосов
/ 04 мая 2020

Учитывая кадр: Frame<(string * int * int),int>

let df = 
  [ (("N1", 100,1), "C1", 1.0); (("N2",100,2), "C1", 3.1)
    (("N3",100,3), "C1", 4.0); (("N4",100,4), "C1", -6.4);
    (("N1", 200,5), "C2", 1.0); (("N2",200,6), "C2", 7.1)
    (("N3",200,7), "C2", 4.0); (("N4",200,8), "C2", -2.4);
    (("N1", 100,1), "C2", 1.0); (("N2",100,2), "C2", 5.1)
    (("N3",100,3), "C2", 4.0); (("N4",100,4), "C2", -8.4);
    (("N1", 200,5), "C1", 1.0); (("N2",200,6), "C1", 1.1)
    (("N3",200,7), "C1", 4.0); (("N4",200,8), "C1", -9.4)]
  |> Frame.ofValues

Я хотел бы иметь возможность группировать столбцы по второму элементу в кортеже ключа строки - так, по 100 и 200, а затем изменить кадр Frame<(string*int),(int*int)>

Кажется, мне нужно использовать Frame.Transpose, а затем Frame.groupRowsUsing для группировки столбцов, но я не знаю, как получить 100/200 в функции селектора.

Вывод должен выглядеть так:

        (100,C1)   (100,C2)     (200,C1)    (200,C2)
N1 1 -> 1           1           1           1   
N2 2 -> 3.1         5.1         1.1         7.1 
N3 3 -> 4           4           4           4   
N4 4 -> -6.4        -8.4        -9.4        -2.4     


1 Ответ

0 голосов
/ 04 мая 2020

Мне было непонятно, намерение ли было сохранить ключи столбцов такими, как они есть, и изменить значения строк на кортежи или столбцы должны были быть кортежами, а значения оставаться плавающими.

Предполагая первый вариант:

//adding a helper function to the module: transforms row data and replace a given columns
module Frame =
    let mapiReplaceCol col f frame = frame |> Frame.replaceCol col (Frame.mapRows f frame)

(df,df.ColumnKeys)
||> Seq.fold (fun acc elem ->
    acc |> Frame.mapiReplaceCol elem (fun (_,k,_) row -> k,row.GetAs<float>(elem)))
|> Frame.mapRowKeys (fun (a,_,c) -> a,c)

(*output:
            C1          C2          
N1 100 1 -> (100, 1)    (100, 1)    
N2 100 2 -> (100, 3.1)  (100, 5.1)  
N3 100 3 -> (100, 4)    (100, 4)    
N4 100 4 -> (100, -6.4) (100, -8.4) 
N1 200 5 -> (200, 1)    (200, 1)    
N2 200 6 -> (200, 1.1)  (200, 7.1)  
N3 200 7 -> (200, 4)    (200, 4)    
N4 200 8 -> (200, -9.4) (200, -2.4) 
*)

Предполагая второй вариант:

Шаг 1: деконструкция кадра до (строка * столбец * значение) и восстановление

let step1 =
    df |> Frame.mapRows (fun (a,b,c) row ->
        df.ColumnKeys |> Seq.map (fun col ->(a,c),(b,col),row.GetAs<float>(col)))
    |> Series.values |> Seq.concat |> Frame.ofValues

(*
output:
          100                 200                 
          C1        C2        C1        C2        
N1 1 -> 1         1         <missing> <missing> 
   5 -> <missing> <missing> 1         1         
N2 2 -> 3,1       5,1       <missing> <missing> 
   6 -> <missing> <missing> 1,1       7,1       
N3 3 -> 4         4         <missing> <missing> 
   7 -> <missing> <missing> 4         4         
N4 4 -> -6,4      -8,4      <missing> <missing> 
   8 -> <missing> <missing> -9,4      -2,4   
*)

Шаг 2: уменьшение уровней

let step2 = step1 |> Frame.reduceLevel fst (fun (a : float) b -> a + b)

(*
output:
      100       200       
      C1   C2   C1   C2   
N1 -> 1    1    1    1    
N2 -> 3,1  5,1  1,1  7,1  
N3 -> 4    4    4    4    
N4 -> -6,4 -8,4 -9,4 -2,4 
*)

Шаг 3 (необязательно): повторная обработка кортежей в индексе

let step3 = step2 |> Frame.mapRowKeys (fun k -> k,k.Replace("N","") |> int)

(*
output:
        100       200       
        C1   C2   C1   C2   
N1 1 -> 1    1    1    1    
N2 2 -> 3,1  5,1  1,1  7,1  
N3 3 -> 4    4    4    4    
N4 4 -> -6,4 -8,4 -9,4 -2,4 
*)

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

...