与 Deedle 交叉连接
Cross join with Deedle
我正在尝试通过分析我的电费来学习一些 F# 和 Deedle。
假设我有两个框架,其中一个包含我的用电量:
let consumptionsByYear =
[ (2019, "Total", 500); (2019, "Day", 200); (2019, "Night", 300);
(2020, "Total", 600); (2020, "Day", 250); (2020, "Night", 350) ]
|> Frame.ofValues
Total Day Night
2019 -> 500 200 300
2020 -> 600 250 350
另一个包含两个具有不同定价结构的计划(固定费用或费用根据一天中的时间而变化):
let prices =
[ ("Plan A", "Base fee", 50); ("Plan A", "Fixed price", 3); ("Plan A", "Day price", 0); ("Plan A", "Night price", 0);
("Plan B", "Base fee", 40); ("Plan B", "Fixed price", 0); ("Plan B", "Day price", 5); ("Plan B", "Night price", 2) ]
|> Frame.ofValues
Base fee Fixed price Day price Night price
Plan A -> 50 3 0 0
Plan B -> 40 0 5 2
之前,我在 SQL 中使用交叉连接解决了这个问题,在 Excel 中使用嵌套连接解决了这个问题。为了复制这些,我找到了 Frame.mapRows
,但是使用它构建预期的输出似乎非常乏味:
let costs = consumptionsByYear
|> Frame.mapRows (fun _year cols ->
["Total price" => (prices?``Base fee``
+ (prices?``Fixed price`` |> Series.mapValues ((*) (cols.GetAs<float>("Total"))))
+ (prices?``Day price`` |> Series.mapValues ((*) (cols.GetAs<float>("Day"))))
+ (prices?``Night price`` |> Series.mapValues ((*) (cols.GetAs<float>("Night"))))
)]
|> Frame.ofColumns)
|> Frame.unnest
Total price
2019 Plan A -> 1550
Plan B -> 1640
2020 Plan A -> 1850
Plan B -> 1990
是否有更好的方法或者更小的改进?
我不是Deedle专家,但我认为这基本上是:
- 两个矩阵的点积:
consumptionsByYear
和周期性 day/night 价格,
- 随后添加不变的基本价格。
换句话说:
consumptionsByYear periodicPrices basePrices
------------------- ------------------------ ---------------------------
| Day Night | | Plan A Plan B | | Plan A Plan B |
| 2019 -> 200 300 | * | Day -> 3 5 | + | Base fee -> 50 40 |
| 2020 -> 250 350 | | Night -> 3 2 | ---------------------------
------------------- ------------------------
考虑到这种方法,我会这样做:
open Deedle
open Deedle.Math
let consumptionsByYear =
[ (2019, "Day", 200); (2019, "Night", 300)
(2020, "Day", 250); (2020, "Night", 350) ]
|> Frame.ofValues
let basePrices =
[ ("Plan A", "Base fee", 50)
("Plan B", "Base fee", 40) ]
|> Frame.ofValues
|> Frame.transpose
let periodicPrices =
[ ("Plan A", "Day", 3); ("Plan A", "Night", 3)
("Plan B", "Day", 5); ("Plan B", "Night", 2) ]
|> Frame.ofValues
|> Frame.transpose
// repeat the base prices for each year
let basePricesExpanded =
let row = basePrices.Rows.["Base fee"]
consumptionsByYear
|> Frame.mapRowValues (fun _ -> row)
|> Frame.ofRows
let result =
Matrix.dot(consumptionsByYear, periodicPrices) + basePricesExpanded
result.Print()
输出为:
Plan A Plan B
2019 -> 1550 1640
2020 -> 1850 1990
为简单起见,我做了一些更改:
consumptionsByYear
- 为了使矩阵兼容,我将年份从整数映射到字符串。
- 我删除了
Total
列,因为它可以从其他两个导出。
prices
- 我将其分成两个单独的框架:一个用于周期性价格,另一个用于基本价格,然后将它们转置以启用矩阵乘法。
- 我将
Day price
更改为 Day
并将 Night price
更改为 Night
以使矩阵兼容。
- 我删除了
Fixed price
列,因为它可以在 Day
和 Night
列中表示。
更新:从 Deedle 2.4.2 开始,不再需要将年份映射到字符串。我已经相应地修改了我的解决方案。
我正在尝试通过分析我的电费来学习一些 F# 和 Deedle。
假设我有两个框架,其中一个包含我的用电量:
let consumptionsByYear =
[ (2019, "Total", 500); (2019, "Day", 200); (2019, "Night", 300);
(2020, "Total", 600); (2020, "Day", 250); (2020, "Night", 350) ]
|> Frame.ofValues
Total Day Night
2019 -> 500 200 300
2020 -> 600 250 350
另一个包含两个具有不同定价结构的计划(固定费用或费用根据一天中的时间而变化):
let prices =
[ ("Plan A", "Base fee", 50); ("Plan A", "Fixed price", 3); ("Plan A", "Day price", 0); ("Plan A", "Night price", 0);
("Plan B", "Base fee", 40); ("Plan B", "Fixed price", 0); ("Plan B", "Day price", 5); ("Plan B", "Night price", 2) ]
|> Frame.ofValues
Base fee Fixed price Day price Night price
Plan A -> 50 3 0 0
Plan B -> 40 0 5 2
之前,我在 SQL 中使用交叉连接解决了这个问题,在 Excel 中使用嵌套连接解决了这个问题。为了复制这些,我找到了 Frame.mapRows
,但是使用它构建预期的输出似乎非常乏味:
let costs = consumptionsByYear
|> Frame.mapRows (fun _year cols ->
["Total price" => (prices?``Base fee``
+ (prices?``Fixed price`` |> Series.mapValues ((*) (cols.GetAs<float>("Total"))))
+ (prices?``Day price`` |> Series.mapValues ((*) (cols.GetAs<float>("Day"))))
+ (prices?``Night price`` |> Series.mapValues ((*) (cols.GetAs<float>("Night"))))
)]
|> Frame.ofColumns)
|> Frame.unnest
Total price
2019 Plan A -> 1550
Plan B -> 1640
2020 Plan A -> 1850
Plan B -> 1990
是否有更好的方法或者更小的改进?
我不是Deedle专家,但我认为这基本上是:
- 两个矩阵的点积:
consumptionsByYear
和周期性 day/night 价格, - 随后添加不变的基本价格。
换句话说:
consumptionsByYear periodicPrices basePrices
------------------- ------------------------ ---------------------------
| Day Night | | Plan A Plan B | | Plan A Plan B |
| 2019 -> 200 300 | * | Day -> 3 5 | + | Base fee -> 50 40 |
| 2020 -> 250 350 | | Night -> 3 2 | ---------------------------
------------------- ------------------------
考虑到这种方法,我会这样做:
open Deedle
open Deedle.Math
let consumptionsByYear =
[ (2019, "Day", 200); (2019, "Night", 300)
(2020, "Day", 250); (2020, "Night", 350) ]
|> Frame.ofValues
let basePrices =
[ ("Plan A", "Base fee", 50)
("Plan B", "Base fee", 40) ]
|> Frame.ofValues
|> Frame.transpose
let periodicPrices =
[ ("Plan A", "Day", 3); ("Plan A", "Night", 3)
("Plan B", "Day", 5); ("Plan B", "Night", 2) ]
|> Frame.ofValues
|> Frame.transpose
// repeat the base prices for each year
let basePricesExpanded =
let row = basePrices.Rows.["Base fee"]
consumptionsByYear
|> Frame.mapRowValues (fun _ -> row)
|> Frame.ofRows
let result =
Matrix.dot(consumptionsByYear, periodicPrices) + basePricesExpanded
result.Print()
输出为:
Plan A Plan B
2019 -> 1550 1640
2020 -> 1850 1990
为简单起见,我做了一些更改:
consumptionsByYear
- 为了使矩阵兼容,我将年份从整数映射到字符串。
- 我删除了
Total
列,因为它可以从其他两个导出。
prices
- 我将其分成两个单独的框架:一个用于周期性价格,另一个用于基本价格,然后将它们转置以启用矩阵乘法。
- 我将
Day price
更改为Day
并将Night price
更改为Night
以使矩阵兼容。 - 我删除了
Fixed price
列,因为它可以在Day
和Night
列中表示。
更新:从 Deedle 2.4.2 开始,不再需要将年份映射到字符串。我已经相应地修改了我的解决方案。