【问题标题】:Interpolation of missing values缺失值的插值
【发布时间】:2020-10-19 08:56:53
【问题描述】:

我正在尝试实现一个循环遍历字典的 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 文件中提取的数据,并且在此代码 sn-p 之后有一个代码,它将写入输出 csv 文件。我让字典有两次日期的原因是因为从输入 csv 文件中读取时我正在这样做:dictionary[row['Date']] = row,我可以将其设为列表,但它会使sorted 函数调用复杂化。假定保证第一行和最后一行被完全填充,即不会丢失列值。字典键是 datetime 对象而不是字符串。当我从输入的 csv 文件中读取数据时,我将字符串转换为 datetime 对象并将其分配为字典的键。

【问题讨论】:

  • 你会用熊猫吗?
  • @funnydman 我可以使用 pandas,但我对这个库没有经验。
  • 当心:这是一种糟糕的插值方法,因为如果您有多个缺失的连续值,它是非线性的。线性方法将给出: 3 - 4.67 [3 + (8-5)/3] - 6.33 [3 + 2*(8-5)/3] - 8

标签: python dictionary for-loop


【解决方案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

我编写了很多程序以尝试以良好的格式显示它。但是核心代码自己填的只是用了一点地方。尽情享受吧。

【讨论】:

    【解决方案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
    

    【讨论】:

    • 谢谢。我想我不能使用这种方法,因为我正在将 Date 列转换为 datetime 对象。另外,我想在插值之前根据日期对数据进行排序。我将尝试解决这两个问题,并让您知道它是否有效。另外,我注意到插值不同于前一个已知值和最近的下一个已知值之间的平均值。
    • 您可以通过简单地应用 to_datetime 函数df['Date'] = df['Date'].apply(pd.to_datetime) 将列制作为日期时间对象。此外,插值方法可以使用不同的算法来填充缺失的数据,请查看此处的文档:pandas.pydata.org/pandas-docs/stable/reference/api/…
    猜你喜欢
    • 1970-01-01
    • 2013-07-08
    • 2018-08-14
    • 2018-06-27
    • 2021-07-24
    • 1970-01-01
    • 2018-05-21
    相关资源
    最近更新 更多