【问题标题】:Implement sliding window on file lines in Python在 Python 中的文件行上实现滑动窗口
【发布时间】:2020-04-10 08:07:45
【问题描述】:

我正在尝试使用 Python 在 csv 文件的行上实现滑动/移动窗口方法。每行可以有一个二进制值yes 或no 的列。基本上,我想要罕见的yes 噪音。这意味着如果说我们有 3 yes 行在 5 个窗口中(最多 5 个),请保留它们。但是如果有 1 或 2 个,让我们将它们更改为 no。我该怎么做?

例如,下面的yes 应该都变成no。

...
1,a1,b1,no,0.75
2,a2,b2,no,0.45
3,a3,b3,yes,0.98
4,a4,b4,yes,0.22
5,a5,b5,no,0.46
6,a6,b6,no,0.20
...

但在下面,我们保持原样(可以有 5 个窗口,其中 3 个是 yes):

...
1,a1,b1,no,0.75
2,a2,b2,no,0.45
3,a3,b3,yes,0.98
4,a4,b4,yes,0.22
5,a5,b5,no,0.46
6,a6,b6,yes,0.20
...

我尝试写一些东西,窗口为 5,但卡住了(不完整):

        window_size = 5 
        filename='C:\\Users\\username\\v3\\And-'+v3file.split("\\")[5]
        with open(filename) as fin:
            with open('C:\\Users\\username\\v4\\And2-'+v3file.split("\\")[5],'w') as finalout:
                line= fin.readline()
                index = 0
                sequence= []
                accs=[]
                while line:
                    print(line)
                    for i in range(window_size):
                        line = fin.readline()
                        sequence.append(line)
                    index = index + 1
                    fin.seek(index)

【问题讨论】:

  • 您是否正在尝试解决,将最近的三行保留为变量/窗口?
  • @wwii 实际上让我们说最大超出 5 个窗口(3 是不一定需要全部按顺序排列)。稍微更新了问题。
  • 文件很大吗?一次读取一行文件重要吗?如果你将整个文件读入内存,你的问题会变得更容易,代码也会变得更干净,你不必做fin.seek之类的事情
  • 能否提供更完整的示例,以及后续输出应该是什么样子?
  • @vasia 文件最大可达 10MB。但是,如果您认为它适合记忆,那很好。

标签: python python-3.x moving-average sliding-window file-traversal


【解决方案1】:

这是一个基于构建连续列表推导的 5 行解决方案:

lines = [
'1,a1,b1,no,0.75',
'2,a2,b2,yes,0.45',
'3,a3,b3,yes,0.98',
'4,a4,b4,yes,0.22',
'5,a5,b5,no,0.46',
'6,a6,b6,no,0.98',
'7,a7,b7,yes,0.22',
'8,a8,b8,no,0.46',
'9,a9,b9,no,0.20']

n = len(lines)

# flag all lines containing 'yes' (add 2 empty lines at boundaries to avoid pbs)
flags = [line.count('yes') for line in ['', '']+lines+['', '']]
# count number of flags in sliding window [p-2,p+2]
counts = [sum(flags[p-2:p+3]) for p in range(2,n+2)]
# tag lines that need to be changed
tags = [flag > 0 and count < 3 for (flag,count) in zip(flags[2:],counts)]
# change tagged lines
for n in range(n):
  if tags[n]: lines[n] = lines[n].replace('yes','no')

print(lines)

结果:

['1,a1,b1,no,0.75',
 '2,a2,b2,yes,0.45',
 '3,a3,b3,yes,0.98',
 '4,a4,b4,yes,0.22',
 '5,a5,b5,no,0.46',
 '6,a6,b6,no,0.98',
 '7,a7,b7,no,0.22',
 '8,a8,b8,no,0.46',
 '9,a9,b9,no,0.20']

编辑:当您从标准文本文件中读取数据时,您所要做的就是:

with file(filename,'r') as f:
  lines = f.read().strip().split('\n')

(strip 删除文件顶部或底部的潜在空行,split(\n) 将文件内容转换为行列表)然后使用上面的代码...

【讨论】:

  • 谢谢。您可以通过读取和写入文件来完成您的代码吗?
  • 确实很愉快的聊天。我也删除了我的 cmets。问候
【解决方案2】:

您可以使用 collections.deque 并将 maxlen 参数设置为所需的窗口大小来实现一个滑动窗口,该窗口跟踪最近 5 行的是/否标志。保持对 yeses 的计数,而不是在每次迭代中计算滑动窗口中 yeses 的总和,以提高效率。当您有一个全尺寸滑动窗口并且 yeses 的计数大于 2 时,将这些 yeses 的行索引添加到应该保持 yeses 原样的集合中。并且在重置输入的文件指针后的第二遍中,如果行索引不在集合中,则将 yeses 更改为 noes:

from collections import deque

window_size = 5
with open(filename) as fin, open(output_filename, 'w') as finalout:
    yeses = 0
    window = deque(maxlen=5)
    preserved = set()
    for index, line in enumerate(fin):
        window.append('yes' in line)
        if window[-1]:
            yeses += 1
        if len(window) == window_size:
            if yeses > 2:
                preserved.update(i for i, f in enumerate(window, index - window_size + 1) if f)
            if window[0]:
                yeses -= 1
    fin.seek(0)
    for index, line in enumerate(fin):
        if index not in preserved:
            line = line.replace('yes', 'no')
        finalout.write(line)

演示:https://repl.it/@blhsing/StripedCleanCopyrightinfringement

【讨论】:

  • 谢谢。有机会不使用csv吗?让我们概括为一个文本文件,其中yes 存在于一行中。我没有在标题中添加 csv 以使其通用。
  • 随后进行了相应的编辑。
  • @TinaJ 因为它实际上是一个 CSV,所以我认为告诉人们进行概括不会帮助您提供正确的解决方案。正确读取 CSV 非常简单,并且不会增加任何复杂性(如果有的话,它会降低复杂性)。
  • 随后进行了相应的编辑。
  • 很好,它有效。 yeses &gt; 2 应该是整个窗口的最大计数,但已经知道如何编辑它。所以谢谢!
猜你喜欢
  • 2017-09-04
  • 2017-01-16
  • 2015-06-16
  • 2022-11-27
  • 2017-09-23
  • 1970-01-01
  • 2015-02-20
  • 2021-08-10
  • 2016-03-06
相关资源
最近更新 更多