【问题标题】:How to Identify Missing Indices如何识别缺失的索引
【发布时间】:2019-05-14 23:49:52
【问题描述】:

我有一个包含数百万个索引点的文本文件,这些索引点都被解释为字符串并且是制表符分隔的。但是,可能会丢失一些索引点。这是我的文本文件的示例:

1       0       4         0d 07:00:37.0400009155273   
2       0       4         0d 07:00:37.0400009155273   
3       0       4         0d 07:00:37.0400009155273   
5       0       4         0d 07:00:37.0400009155273   
7       0       4         0d 07:00:37.0400009155273   
9       0       4         0d 07:00:37.0400009155273

请注意,缺少第 4、6 和 8 行。我的目标是创建一个可以解析文本文件、识别可能丢失的索引点并返回包含所有丢失索引点(如果有)的列表或不返回任何内容的函数。

我在 Spyder IDE Windows10 操作系统中使用 Python 3.7。我对 Python 和 Stackoverflow 比较陌生。

这是我到目前为止所得到的。这适用于 ID 1 缺失索引,但如果有多个缺失索引点则失败。

错误在第一个 else 行之后开始。我不确定如何使用 for 循环的索引 (0, 1, 2, 3...) 跟踪文档中观察到的索引 (1, 2, 3, 5...),因为丢失的索引点会随着时间的推移而复合.

注意,文本文档的前 4 行包含我在解析过程中忽略的标题信息,这就是为什么 data = f.readlines()[4:]

  def check_sorted_file(fileName):
        missing_idx = []
        count = 1
            with open(fileName, 'r') as f:
                data = f.readlines()[4:]
                for x, line in enumerate(data):
                    idx = int(line.split()[0])
                    if idx == (count + x): 
                        pass
                    else: 
                        missing_idx.append(count + x)
                        count += 1
                if missing_idx != []:
                    print('\nThe following idicie(s) are missing: ')
                    print(*missing_idx, sep=", ")
                else:   
                    print('\nAll indices are accounted for. ')
                return missing_idx

...

感谢您的帮助!

【问题讨论】:

  • 您是否可以使用库,或者您需要/更喜欢纯 Python?
  • 理想情况下,我更喜欢纯 Python。但是,如果有使用库的更优雅的解决方案,我也可以。 @ClarenceLeung

标签: python python-3.x indexing


【解决方案1】:

另一个答案为您提供了更好的整体解决方案,但我只想帮助您引导您正确的方向,以便您了解如何改变您的工作:

def check_sorted_file(fileName):
    missing_idx = []
    last_index = 0
    with open(fileName, 'r') as f:
        data = f.readlines()[4:]

    for line in data:
        idx = int(line.split()[0])
        if idx == last_index+1:
            pass
        else:
            missing_idx.extend(list(range(last_index+1, idx)))
        last_index = idx

    if missing_idx:
        print('\nThe following idicie(s) are missing: ')
        print(*missing_idx, sep=", ")
    else:
        print('\nAll indices are accounted for. ')
    return missing_idx

因此,我们将使用传入索引作为我们所在位置的指南,而不是使用枚举。

为了解决多个缺失问题,我们使用range 来获取最后一个索引和当前索引之间的所有数字,并用这组新数字扩展我们的列表。

【讨论】:

    【解决方案2】:

    您可以单独使用 Python 做到这一点:

    with open(filename) as f:
        indices = [int(row.split('\t')[0]) for row in f.read().split('\n')[4:]]
    
    missing_indices = [index 
                       for index in range(1, len(indices) + 1)
                       if index not in indices]
    

    这会将您的数据转换为嵌套的list,其中每个外部list 包含一行,每个内部list 包含一个元素。由于我们只关心索引,因此我们获取第一个元素并忽略其他元素。

    然后,由于索引从 1 开始按运行顺序排列,我们构造了一个跨越 预期索引范围的 range,并获取存在于该范围内但不在文件中的索引.

    假设索引是唯一的(这似乎是合理的),我们也可以使用 DYZ 的建议使用sets

    missing_indices = set(range(1, len(indices) + 1) - set(indices)
    

    pandas 也可以正常工作:

    import pandas as pd
    
    df = pd.read_csv(filename, sep='\t').iloc[4:]
    
    range_index = pd.RangeIndex(1, len(df) + 1)
    print(range_index[~range_index.isin(df.iloc[:, 0])]
    

    这会从您的数据中创建一个pandas DataFrame,切断前四行。遵循与另一个答案相同的原则,它创建一个包含所有预期值的索引,并获取它的子集,该子集在 DataFrame 的第一列中不存在。

    【讨论】:

    • 您的代码不起作用。 missing_indices 成为一个向上计数的列表。看起来它计算行数?但不确定,即 [1, 2, 3, 4, 5... end]
    • @Tim51 新手错误;我忘记将索引转换为int。再试一次;我已经编辑了代码。
    • 运气不好。这是我看到的错误代码:indices = [int(row.split('\t')[0]) for row in f.read().split('\n')[4:]] ValueError : int() 以 10 为底的无效文字:''
    • @Tim51 在将空格转换为制表符后,我无法在您的示例数据上重现错误。您确定元素是制表符分隔的吗?
    • 我 99% 确定它的制表符分隔。有没有有效的方法可以验证?
    【解决方案3】:

    由于您有大量行,您可能希望以一种懒惰的方式执行此操作,而无需制作大型列表或使用in 来测试每个值是否都在百万行列表中。您可以混合一些 itertools 作为迭代器来执行此操作,并将列表保存到最后(如果您甚至需要它)。

    基本上,您将teemap 制作成两个迭代器来获取索引,用next() 敲掉其中一个的值,然后压缩它们以检查差异:

    from itertools import chain, tee
    lines = ["1       0       4         0d 07:00:37.0400009155273",
    "2       0       4         0d 07:00:37.0400009155273",
    "3       0       4         0d 07:00:37.0400009155273",
    "5       0       4         0d 07:00:37.0400009155273",
    "7       0       4         0d 07:00:37.0400009155273",
    "9       0       4         0d 07:00:37.0400009155273"
    ]
    
    #two iterators going over indexes 
    i1, i2 = tee(map(lambda x: int(x.split()[0]), lines), 2)
    
    # move one forward
    next(i2) 
    
    # chain.from_iterable will be an iterator producing missing indexes: 
    list(chain.from_iterable(range(i+1, j) for i, j in zip(i1, i2) if j-i!=1))
    

    结果:

    [4, 6, 8]
    

    【讨论】:

      【解决方案4】:

      这是一个紧凑、强大、基于集合的核心 Python 解决方案。读取文件,将每一行拆分为字段,将第一个字段转换为 int,并构建一组实际索引:

      skip = 4 # Skip that many lines
      with open(yourfile) as f:
          for _ in range(skip):
              next(f)
          actual = {int(line.split()[0]) for line in f}
      

      创建一组预期索引并取集差:

      expected = set(range(min(actual), max(actual) + 1))
      sorted(expected - actual)
      #[4, 6, 8]
      

      即使索引不是从 1 开始,该解决方案也有效。

      【讨论】:

      • 是的。它查看从最小存在到最大存在的所有索引的集合。
      • 一个小问题是我的文本文件的前 4 行包含标题信息。所以索引检查器需要在第 4 行之后启动。@DYZ
      • 所以,跳过前四行。
      • 新手问题 - 如何使用您的方法在前 4 行之后开始?我尝试了“实际 = {int(line.split()[0]) for line[4:] in f} 和实际 = {int(line.split()[0]) for line in f[4:]}我不确定如何在这里表达语法。@DYZ
      猜你喜欢
      • 1970-01-01
      • 2019-09-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-11-11
      • 1970-01-01
      相关资源
      最近更新 更多