EKB Every once in a while I get a data table with gaps, something like this:
| 10 | 20 | 30 | 40 | 50 | 60 | |
|---|---|---|---|---|---|---|
| Item 1 | 3.2 | 4.7 | 3.7 | |||
| Item 2 | 1.9 | 2.0 | 6.8 | |||
| Item 3 | 0.0 | 0.5 | 7.8 | 2.2 | 1.3 |
What I want to do is to fill in the table using interpolation. For example, using linear interpolation, applying the routine to the table above should give
| 10 | 20 | 30 | 40 | 50 | 60 | |
|---|---|---|---|---|---|---|
| Item 1 | 3.2 | 3.7 | 4.2 | 4.7 | 4.2 | 3.7 |
| Item 2 | 1.9 | 2.0 | 3.2 | 4.4 | 5.6 | 6.8 |
| Item 3 | 0.0 | 0.5 | 7.8 | 5.0 | 2.2 | 1.3 |
Here is my solution, with a demo at the end. A couple of notes:
## Worker procs
proc phi {x xl xu} {
expr {(1.0 * $x - $xl)/(1.0 * $xu - $xl)}
}
proc linear {x xl yl xu yu} {
set phi [phi $x $xl $xu]
expr {$yl + $phi * ($yu - $yl)}
}
proc geometric {x xl yl xu yu} {
set phi [phi $x $xl $xu]
expr {$yl * pow(1.0 * $yu/$yl, $phi)}
}
proc interpolate {xs ys f} {
set ysnew {}
set yl [lindex $ys 0]
set xl [lindex $xs 0]
set yu {}
set xu {}
set i 0
foreach yi $ys xi $xs {
if [string is double -strict $yi] {
set yl $yi
set xl $xi
set yu {}
set xu {}
lappend ysnew $yi
} else {
if {$yu == {}} {
set i1 [expr {$i + 1}]
foreach xj [lrange $xs $i1 end] yj [lrange $ys $i1 end] {
if [string is double -strict $yj] {
set yu $yj
set xu $xj
break
}
}
}
lappend ysnew [$f $xi $xl $yl $xu $yu]
}
incr i
}
set ysnew
}
## Demo
console show
set xs {10 20 30 40 50 60}
puts "\t[join $xs \t]"
foreach n {"Item 1" "Item 2" "Item 3"} ys {
{3.2 {} {} 4.7 5.2 3.7}
{1.9 2.0 {} {} {} 6.8}
{0.0 0.5 7.8 {} 2.2 1.3}
} {
puts "$n\t[join [interpolate $xs $ys linear] \t]"
}