缺失值的插值

3

我正在尝试实现一个for循环,它可以遍历一个字典。这个字典的值是从csv文件中提取的。某些行的一些值丢失了。我考虑要做的是取前一个和最近可用的下一个条目的平均值,并将其赋给字典。有时连续行都会缺少列值。

以下是一个例子:

input.csv:

Date,Column_1,Column_2,Column_3
2020-06-26,1,3,5
2020-06-27,2,,4
2020-06-28,5,,6
2020-06-29,7,8,10

预期行为是:
输出.csv:
Date,Column_1,Column_2,Column_3
2020-06-26,1,3,5
2020-06-27,2,5.5,4
2020-06-28,5,6.75,6
2020-06-29,7,8,10

(3 + 8) / 2 = 5.5

(5.5 + 8) / 2 = 6.75

这是我尝试的:


def neighborhood(iterable):
    iterator = iter(iterable)
    previous_item = None
    current_item = next(iterator)
    for next_item in iterator:
        yield previous_item, current_item, next_item
        previous_item = current_item
        current_item = next_item
    yield previous_item, current_item, None

dictionary = {
    '2020-06-26': {'Date': '2020-06-26', 'Column_1': 1, 'Column_2': 3, 'Column_3': 5},
    '2020-06-27': {'Date': '2020-06-27', 'Column_1': 2, 'Column_3': 4},
    '2020-06-28': {'Date': '2020-06-28', 'Column_1': 5, 'Column_3': 6},
    '2020-06-29': {'Date': '2020-06-29', 'Column_1': 7, 'Column_2': 8, 'Column_3': 10}
}

field_names = {'Column_1', 'Column_2', 'Column_3'}

for previous_date, current_date, next_date in neighborhood(sorted(dictionary)):
    for field_name in field_names:
        if field_name not in dictionary[current_date]:
            dictionary[current_date][field_name] = (dictionary[previous_date][field_name] + dictionary[next_date][field_name]) / 2

注意:此问题不涉及如何从csv文件读取或写入csv文件。将有一个带有数据的字典,我已经从输入csv文件中提取了它们,并且在此代码片段之后有一些代码将写入输出csv文件。我之所以让字典中的日期出现两次,是因为当我从输入csv文件中读取时,我正在执行以下操作:dictionary[row['Date']] = row,我可以将其变成列表,但这会使sorted函数调用复杂化。已知第一行和最后一行保证完全填满,即没有缺少列值。字典键是一个datetime对象而不是字符串。当我从输入csv文件中读取时,我会将字符串转换为datetime对象,并将其分配为字典的键。


你会使用pandas吗? - funnydman
@funnydman 我可以使用 pandas,但我对这个库没有经验。 - Ambitions
注意:这是一种较差的插值方法,因为如果您有多个连续缺失的值,则它是非线性的。线性方法将给出:3-4.67 [3 +(8-5)/ 3] -6.33 [3 + 2 *(8-5)/ 3] -8。 - Serge Ballesta
2个回答

2
使用Pandas,您可以使用interpolate()方法。点击此处了解更多信息。
import pandas as pd                                                                                                                                                                                                                                                                                                                    

df = pd.read_csv("input.csv")                                                                                                                                                                                                                                                                                                                  

数据框现在看起来像这样:
         Date  Column_1  Column_2  Column_3
0  2020-06-26         1       3.0         5
1  2020-06-27         2       NaN         4
2  2020-06-28         5       NaN         6
3  2020-06-29         7       8.0        10

在具有缺失数据的列上使用interpolate()函数会填补空缺。

df['Column_2'].interpolate()                                                                                                                                                                                                                                                                                                            
0    3.000000
1    4.666667
2    6.333333
3    8.000000
Name: Column_2, dtype: float64

现在我们可以将其分配回数据框中。
df['Column_2'] = df['Column_2'].interpolate()                                                                                                                                                                                                                                                                                          

结果在

         Date  Column_1  Column_2  Column_3
0  2020-06-26         1  3.000000         5
1  2020-06-27         2  4.666667         4
2  2020-06-28         5  6.333333         6
3  2020-06-29         7  8.000000        10

谢谢。我认为我不能使用这种方法,因为我正在将“日期”列转换为“datetime”对象。此外,我想在插值之前根据日期对数据进行排序。我将尝试解决这两个问题,并告诉您是否有效。另外,我注意到插值后的值与前一个已知值和最近的下一个已知值之间的平均值不同。 - Ambitions
你可以通过简单地应用to_datetime函数将列转换为日期时间对象 df['Date'] = df['Date'].apply(pd.to_datetime)。此外,interpolate方法可以使用不同的算法来填充缺失的数据,请查看这里的文档:https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.interpolate.html。 - firelynx

1

Python 是一种面向对象的语言,所以使用 class 来解决一些稍微复杂的问题是可行的。

解决方案:

class frame(object):
    def __init__(self, data:"list of list of object"= None):
        self._data = data
        self._init_str_size()

    def _init_str_size(self):
        # assert that it have at least one object.
        assert self._data[0]
        self._str_size = [0 for i in range(len(self._data[0]))]
        for index, col in enumerate(self._data):
            self._str_size[index] = max(self._str_size[index], len(str(col[index])))

    def __str__(self):
        result = []
        for col in self._data:
            result.append(" | ".join([
                f"{str(item):>{self._str_size[index]}}" for index, item in enumerate(col)
            ]))
        return "\n".join(result)

    def _before_num(self, i, j):
        return self._before_num(i - 1, j) if self._data[i][j] == None else self._data[i][j]

    def _next_num(self, i, j):
        return self._next_num(i + 1, j) if self._data[i][j] == None else self._data[i][j]

    def fill_num(self):
        for r in range(len(self._data)):
            for c in range(len(self._data[r])):
                if self._data[r][c] == None:
                    print(self._before_num(r, c), self._next_num(r, c))
                    self._data[r][c] = (
                        (self._before_num(r, c) + self._next_num(r, c)) / 2
                    )
        self._init_str_size()

f = frame([
    ["2020-06-26", 1,  3.0,  5],
    ["2020-06-27", 2, None,  4],
    ["2020-06-28", 5, None,  6],
    ["2020-06-29", 7,  8.0, 10]
])

print(f)
# output:
#   | 2020-06-26 | 1 |  3.0 |  5
#   | 2020-06-27 | 2 | None |  4
#   | 2020-06-28 | 5 | None |  6
#   | 2020-06-29 | 7 |  8.0 | 10

# the 2-row and 3-col number's before number and next number
print(f._before_num(2 - 1, 3 - 1))
print(f._next_num(2 - 1, 3 - 1))
# output:
#   | 3.0
#   | 8.0

f.fill_num()
print(f)
# output:
#   | 2020-06-26 | 1 |   3.0 |  5
#   | 2020-06-27 | 2 |   5.5 |  4
#   | 2020-06-28 | 5 |  6.75 |  6
#   | 2020-06-29 | 7 |   8.0 | 10

我经常编写程序,以便将其呈现出良好的格式。但填充自身的核心代码只占用了很少的空间。尽情享受吧。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接