'Fill missing data in a linear model
I have the following val list = listOf(1, 2, 3, null, null, 6, 7) I'd like to fill the null value for the following result: listOf(1, 2, 3, 4, 5, 6, 7)
I couldn't find any method doing this in Kotlin, do you guys have an idea?
EDIT: I'm adding more details, the list will always have a beginning element and an ending element (ie no list as listOf(null, 2 ,3) or listOf(1, 2, null)) I preprocess this.
In my example I gave Integer element but it could be Float or Double
Solution 1:[1]
I've manage to do a simple function that could interpolate. There is room for improvement.
private fun Double.format(): String = DecimalFormat("#.##").format(this)
private fun List<*>.findNonNullIndex(currentIndex: Int, ascending: Boolean = true): Int {
for (i in currentIndex..lastIndex step if(ascending) 1 else -1) {
if(getOrNull(i) != null) return i
}
return currentIndex
}
fun interpolate(list: MutableList<Double>) {
println("initial : $list")
list.forEachIndexed { index, i ->
if(i == null) {
val nonNullIndex = list.findNonNullIndex(index)
val difference = list[nonNullIndex]?.minus(list[index-1] ?: return) ?: return
val coefficient = difference.div((nonNullIndex - index) + 1)
for(j in index until nonNullIndex) {
list[j] = (list[j-1]?.plus(coefficient))?.format()?.toDouble()
}
}
}
println("result: $list")
}
interpolate(mutableListOf(1.0, null, null, null, 1.4, null, 1.5, 1.7))
Solution 2:[2]
Try this
inline fun <T>List<T?>.replaceIfNull(block:(T) -> T) : List<T>{
val temp = mutableListOf<T>()
for((index, element) in this.withIndex()){
if(element == null){
temp.add(block(this[index - 1] ?: temp[index - 1]))
} else {
temp.add(element)
}
}
return temp
}
fun main() {
val list = listOf(1, 2, 3, null, null, 6, 7)
val foo = list.replaceIfNull { nonNullValue ->
nonNullValue + 1
}
println(foo) // [1, 2, 3, 4, 5, 6, 7]
}
EDIT
Linear Model
fun <T:Number>List<T?>.interpolate() : List<Double> {
val temp = mutableListOf<Double>()
var nullCounter = 0
this.forEach { element ->
if(element != null){
val formatedElement = element.format()
temp.add(formatedElement)
if(nullCounter > 0){
var difference = (formatedElement - temp[temp.size - 2]) / (nullCounter + 1)
for(i in 0..nullCounter - 1){
val newElement = (temp[temp.size - 2] + difference).format()
temp.add((temp.size - 1), newElement)
}
}
nullCounter = 0
}
element ?: nullCounter++
}
return temp
}
Solution 3:[3]
There's no way on how to decide what number you want to replace with null.
Considering what if listOf(1, null, null) or listOf(null, 2, null).
If you know exactly what a list needs to be, you might use IntProgression or IntRange instead.
// 1, 2, 3, 4, 5, 6, 7
val list1 = IntRange(1, 7).toList()
// 1, 3, 5, 7
val list1 = IntProgression(1, 7, 2).toList()
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Biscuit |
| Solution 2 | |
| Solution 3 | lemon |
