【问题标题】:Conversion to Logn Python 3.7转换为 Logn Python 3.7
【发布时间】:2019-07-18 06:21:00
【问题描述】:

我有这段代码很好用,可以做我想做的事,但是它以线性形式执行,这会减慢我的数据文件的大小,所以我想将它转换为 Log。我尝试了这段代码和许多其他人在这里发布但仍然没有让它工作的运气。我将发布两组代码并举例说明我的期望。

import pandas
import fileinput

'''This code runs fine and does what I expect removing duplicates from big 
file that are in small file, however it is a linear function.'''

with open('small.txt') as fin:
    exclude = set(line.rstrip() for line in fin)
    for line in fileinput.input('big.txt', inplace=True):
        if line.rstrip() not in exclude:
            print(line, end='')
        else:
            print('')

'''This code is my attempt at conversion to a log function.'''


def log_search(small, big):
    first = 0
    last = len(big.txt) - 1
    while first <= last:
        mid = (first + last) / 2
        if str(mid) == small.txt:
            return True
        elif small.txt < str(mid):
            last = mid - 1
        else:
            first = mid + 1
    with open('small.txt') as fin:
        exclude = set(line.rstrip() for line in fin)
        for line in fileinput.input('big.txt', inplace=True):
            if line.rstrip() not in exclude:
                print(line, end='')
            else:
                print('')
            return log_search(small, big)
  1. 大文件有数百万行 int 数据。
  2. 小文件有数百行 int 数据。
  3. 比较数据并删除大文件中的重复数据,但将行号留空。

运行第一个代码块可以,但是搜索大文件需要很长时间。也许我以错误的方式处理问题。我尝试将其转换为日志运行没有错误,但什么也没做。

【问题讨论】:

  • 不太清楚。您想要将大量数字转换为二进制,还是想要执行二进制搜索来查找匹配项?对于后者,请尝试bisect 模块。
  • 插入数据不是 bisect 吗?我想要做的是使用对数搜索将小文件中的数据与大文件中的数据进行比较,从大文件中删除该数据并将其替换为空行。因为大文件有数百万行,所以线性搜索需要很长时间。
  • 或者我误解了你的意思。您是否要对大文件中小文件的行进行二进制搜索,即将 O(b) 转换为 O(s logb)(s 和 b 分别是小文件和大文件的大小)?我不认为这会起作用,因为在大文件中寻找下一行可能仍然是 O(b),除非你先将它存储在一个列表中,这也是 O(b)。
  • 看起来像file.seek is O(1)(至少在某些系统上?),但这会给你第 i 个字符,而不是第 i 行。您可能仍然可以使用它从该位置读取下一行,并对字符而不是行进行二进制搜索。不过,不确定覆盖行与空白行部分。
  • 好的,我明白了。大列表是整数数据的有序列表。看我的代码。我想将小列表中的每个项目与大列表中点的数据进行比较。如果它是相同的删除它留下空白行。如果不一样,看看它是更小还是更大,然后消除在它不可能的大文件的 50% 中查找,然后再次拆分剩余的 50% 并查看中间点。重复,直到我找到重复删除它留下空白行。然后对小文件中的所有其余数据重复该过程,直到我删除大文件中的所有重复项。

标签: python file search duplicates compare


【解决方案1】:

我认为没有比您目前在第一种方法中所做的更好或更快的方法来做到这一点。 (更新:有,见下文。)将来自small.txt 的行存储在set 中并迭代big.txt 中的行,检查它们是否在该集合中,将具有O(b) 的复杂性,@987654326 @ 是 big.txt 中的行数。

您似乎正在尝试将其减少到O(s*logb)ssmall.txt 中的行数,通过使用二进制搜索检查small.txt 中的每一行是否在@ 987654332@ 然后删除/覆盖它。

如果所有行都在list 中,并且可以随机访问任何数组,那么这将很有效,但是您只有文件,它不允许随机访问任何行。但是,它确实允许随机访问带有file.seek 的任何字符,其中(至少在某些情况下?)seems to be O(1)。但是,您仍然必须先找到该位置的前一个换行符,然后才能真正阅读该行。此外,您不仅可以用空行替换行,还必须用相同数量的字符覆盖数字,例如空格。

所以,是的,理论上可以在O(s*logb) 中完成,如果您执行以下操作:

  • 实现二分查找,不是在行上搜索,而是在大文件的字符上搜索
    • 对于每个位置,回溯到最后一个换行符,然后读取该行以获取编号
    • 像往常一样使用二分搜索在下半部分/上半部分重试
  • 如果找到号码,则替换为与号码中的数字一样多的空格
  • 重复小文件中的下一个数字

在我的系统上,读取和写入包含 1000 万行数字的文件每个只需要 3 秒,而 fileinput.inputprint 大约需要 8 秒。因此,恕我直言,这并不值得付出努力,但这当然可能取决于您必须多久执行一次此操作。


好的,所以我自己很好奇——谁需要午休呢?——所以我尝试实现这个......而且效果出奇的好。这将在文件中找到给定的数字并将其替换为一致的数字-不是只是一个空白行,如果不重写整个文件是不可能的)。请注意,我没有彻底测试二进制搜索算法的边缘情况、非一错误等。

import os

def getlineat(f, pos):
    pos = f.seek(pos)
    while pos > 0 and f.read(1) != "\n":
        pos = f.seek(pos-1)
    return pos+1 if pos > 0 else 0

def bsearch(f, num):
    lower = 0
    upper = os.stat(f.name).st_size - 1
    while lower <= upper:
        mid = (lower + upper) // 2
        pos = getlineat(f, mid)
        line = f.readline()
        if not line: break # end of file
        val = int(line)
        if val == num:
            return (pos, len(line.strip()))
        elif num < val:
            upper = mid - 1
        elif num > val:
            lower = mid + 1 
    return (-1, -1)

def overwrite(filename, to_remove):
    with open(filename, "r+") as f:
        positions = [bsearch(f, n) for n in to_remove]
        for n, (pos, length) in sorted(zip(to_remove, positions)):
            print(n, pos)
            if pos != -1:
                f.seek(pos)
                f.write("-" * length)

import random
to_remove = [random.randint(-500, 1500) for _ in range(10)]
overwrite("test.txt", to_remove)

这将首先收集所有要覆盖的位置,然后在第二个步骤中进行实际覆盖,否则二进制搜索在遇到先前“删除”的行之一时会出现问题。我用一个包含 0 到 1,000 的所有数字(按排序顺序)和一个要删除的随机数列表(包括边界内和边界外)的文件对此进行了测试,效果很好。

更新:还测试了一个随机数从 0 到 100,000,000 的文件(944 MB)并覆盖了 100 个随机数,它立即完成,所以这确实应该是 O(s*logb),至少在我的系统上(file.seek 的复杂性可能取决于文件系统、文件类型等)。

bsearch 函数也可以泛化为接受另一个参数value_function,而不是硬编码val = int(line)。然后它可以用于在任意文件中进行二进制搜索,例如庞大的字典、基因数据库、csv 文件等,只要这些行按相同的值函数排序即可。

【讨论】:

  • 我不知道为什么,但我收到了这个错误。第 24 行 elif num
  • 我尝试更改它,但该错误不断弹出
  • @cyberzyme 提供的代码中肯定没有语法错误。 ^ 指向上面一行中的错误。你有没有改变任何东西,删除,或类似的东西?
  • 啊哈,我复制粘贴了,我不知道你使用的是什么版本的 Python,但我使用的是 3.7,你在上面的行中我也收到一条错误消息,告诉我删除多余的插入语。当我这样做时,错误消息消失了,代码运行良好。感谢您的帮助。
猜你喜欢
  • 1970-01-01
  • 2021-03-26
  • 2019-11-27
  • 2019-09-24
  • 2020-11-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多