【问题标题】:How to parse tables from .txt files using Pandas如何使用 Pandas 从 .txt 文件中解析表格
【发布时间】:2019-12-22 13:09:37
【问题描述】:

我正在尝试从 .txt 格式的日志文件中提取表。该文件是使用来自pandasread_csv() 加载的。

日志文件如下所示:

aaa
bbb
ccc

=====================
A   B   C   D   E   F
=====================
1   2   3   4   5   6
7   8   9   1   2   3
4   5   6   7   8   9
1   2   3   4   5   6
---------------------

=====================
G     H     I     J
=====================
1           3     4
5           6     7
---------------------

=====================
K    L    M    N    O
=====================
1              2    3
4    5              6
7    8         9
---------------------

xxx
yyy
zzz

以下是关于日志文件的一些要点:

  • 文件以一些可以忽略的注释行开始和结束。
  • 在上面的示例中,有三个表。
  • 每个表的标题位于“======...”行之间
  • 每张表的结尾都用一行“------...”表示

我现在的代码:

import pandas as pd
import itertools

df = pd.read_csv("xxx.txt", sep="\n", header=None)

# delimiters for header and end-of-table

h_dl = "=" * 21
r_dl = "-" * 21

for i in range(len(df.index)-2):

    # if loop to find lines which are table headers & convert to list    

    if (df.iloc[i].any() == h_dl) & (df.iloc[i+2].any() == h_dl):

        h = df.iloc[i+1].str.split().tolist()
        h = list(itertools.chain(*h))


        # while loop to find lines which are table rows & append to one list

        x = 3
        r = []

        while True:

            if df.iloc[i+x].any() == r_dl:
                break

            r.append(df.iloc[i+x].str.split().tolist())
            x += 1

        r = list(itertools.chain(*r))

        # create pandas dataframe with header and rows obtained above
        t = pd.DataFrame(data=r, columns=h)

此代码返回AssertionError: 14 columns passed, passed data had 15 columns。我知道这是因为对于表格行,我使用的是.str.split(),默认情况下会拆分为空格。由于有些列存在缺失值,因此表头中的元素数和表行中的元素数与第二个和 htird 表不匹配。我正在努力解决这个问题,因为每个表中表示缺失值的空白字符的数量是不同的。

我的问题是:有没有办法解释某些列中的缺失值,以便我可以得到一个 DataFrame 作为输出,其中适当的缺失值是 null 或 NaN?

【问题讨论】:

    标签: python pandas


    【解决方案1】:

    也许这可以帮助你。 假设我们有下一行文本:

    1           3     4
    

    问题是确定有多少空格分隔两个连续项目,而不考虑它们之间是否存在缺失值。
    假设 5 个空格是分隔符,超过 5 个是缺失值。

    您可以使用正则表达式来解析项目:

    from re import finditer
    
    line = '1           3     4'
    items = []
    
    for result in finditer('(\d+)([ ]*)', line):
        item, delimiter = result.groups()
        items.append(item)
        if len(delimiter) > 5:
            items.append(nan)
    print(items)
    

    输出是:

    ['1', nan, '3', '4']
    

    更复杂的情况是它可以出现两个或多个连续的缺失值(上面的代码只会插入一个 nan)

    【讨论】:

    • 谢谢,正则表达式的这种使用让我深入了解了我应该如何设计我的解析器。很遗憾我没有权利投票回答...
    【解决方案2】:

    使用 Victor Ruiz 方法,我添加了 if 选项来处理不同的标头大小。

    =^..^=

    代码说明:

    import re
    import pandas as pd
    import itertools
    
    df = pd.read_csv("stack.txt", sep="\n", header=None)
    
    # delimiters for header and end-of-table
    
    h_dl = "=" * 21
    r_dl = "-" * 21
    
    for i in range(len(df.index)-2):
    
        # if loop to find lines which are table headers & convert to list
        if (df.iloc[i].any() == h_dl) & (df.iloc[i+2].any() == h_dl):
    
            h = df.iloc[i+1].str.split().tolist()
            h = list(itertools.chain(*h))
    
            # get header string
            head = df.iloc[i+1].to_string()
            # get space distance in header
            space_range = 0
            for result in re.findall('([ ]*)', head):
                if len(result) > 0:
                    space_range = len(result)
    
            x = 3
            r = []
            while True:
                if df.iloc[i+x].any() == r_dl:
                    break
    
                # strip line
                line = df.iloc[i+x].to_string()[5::]
    
                # collect items based on elements distance
                items = []
                for result in re.finditer('(\d+)([ ]*)', line):
                    item, delimiter = result.groups()
                    items.append(item)
                    if len(delimiter) > space_range*2+1:
                        items.append('NaN')
                        items.append('NaN')
                    if len(delimiter) < space_range*2+2 and len(delimiter) > space_range:
                        items.append('NaN')
                r.append([items])
    
                x += 1
    
            r = list(itertools.chain(*r))
    
            # create pandas dataframe with header and rows obtained above
            t = pd.DataFrame(data=r, columns=h)
    

    输出:

       A  B  C  D  E  F
    0  1  2  3  4  5  6
    1  7  8  9  1  2  3
    2  4  5  6  7  8  9
    3  1  2  3  4  5  6
    
       G    H  I  J
    0  1  NaN  3  4
    1  5  NaN  6  7
    
       K    L    M    N     O
    0  1  NaN  NaN    2     3
    1  4    5  NaN  NaN     6
    2  7    8  NaN    9  None
    

    【讨论】:

    • 谢谢,这正是我正在寻找的输出!我想澄清一件事......当你用line = df.iloc[i+x].to_string()[5::]“剥离线”时,最后索引的目的是什么?
    猜你喜欢
    • 2021-11-19
    • 2020-10-02
    • 1970-01-01
    • 2021-01-11
    • 1970-01-01
    • 2016-05-11
    • 2021-11-08
    • 2020-01-28
    • 2017-10-08
    相关资源
    最近更新 更多