Мне было непонятно, намерение ли было сохранить ключи столбцов такими, как они есть, и изменить значения строк на кортежи или столбцы должны были быть кортежами, а значения оставаться плавающими.
Предполагая первый вариант:
//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
*)
По сути, мы реконструировали кадр с нуля. Возможно, лучшим подходом было бы изменить способ построения исходного кадра вместо всего этого.