如何从包含许多表的Excel工作表中解析数据帧(使用Python,可能是Pandas)

时间:2017-01-31 21:29:12

标签: python excel pandas

我正在处理布局糟糕的Excel工作表,我正在尝试解析并写入数据库。

每张工作表都可以有多个表格。虽然这些可能的表格的标题是已知的,但是哪些表格将在任何给定的表格上都没有,它们在表格上的确切位置也不是(表格不以一致的方式对齐)。我添加了两张可能的工作表布局图片来说明这一点:This layout有两个表,而this one包含第一个表的所有表,但不在同一个位置,加上一个额外的表。

我所知道的事:

  1. 所有可能的表头,因此每个表都可以通过其标题来标识
  2. 表格由空白单元格分隔。他们没有互相接触。
  3. 我的问题有没有一种干净的方法可以使用像pandas这样的Python模块来解决这个问题?

    我目前的做法

    我目前正在转换为.csv并解析每一行。我将每一行分成空白单元格,然后处理行的第一部分(应该属于最左边的表格)。行的其余部分排队,然后以相同的方式处理。然后我读了这个first_part并检查它是否是标题行。如果是,我用它来识别我正在处理的表(这是存储在全局current_df中)。不是标题行的后续行被输入到此表中(这里我使用pandas.DataFrame作为我的表)。

    到目前为止,

    代码低于(大部分是不完整和未经测试的,但它应该传达上述方法):

    class DFManager(object): # keeps track of current table and its headers
        current_df = None
        current_headers = []
    
        def set_current_df(self, df, headers):
            self.current_headers = headers
            self.current_df = df
    
    
    def split_row(row, separator):
        while row and row[0] == separator:
           row.pop(0)
        while row and row[-1] == separator:
           row.pop()
    
        if separator in row:
            split_index = row.index(separator)
            return row[:split_index], row[split_index:]
        else:
            return row, []
    
    
    def process_df_row(row, dfmgr):
        df = df_with_header(row) # returns the dataframe with these headers
    
        if df is None: # is not a header row, add it to current df
            df = dfmgr.current_df
            add_row_to_df(row, df)
        else:
            dfmgr.set_current_df(df, row)
    
    
    # this is passed the Excel sheet
    def populate_dataframes(xl_sheet):
        dfmgr = DFManager()
        row_queue = Queue()
        for row in xl_sheet:
            row_queue.put(row)
    
        for row in iter(row_queue.get, None):
            if not row:
                continue
    
            first_part, remainder = split_row(row)
            row_queue.put(remainder)
    
            process_df_row(first_part, dfmgr)
    

2 个答案:

答案 0 :(得分:3)

这是一个特殊的情况,很可能没有"清洁"用现成模块做到这一点的方法。

执行此操作的一种方法可能是使用您已有的标头信息来查找每个表的起始索引,类似于此解决方案(Python Pandas - Read csv file containing multiple tables),但在列方向上也有偏移量。

一旦你有了每个表的起始位置,你就要确定宽度(先验已知或通过读取直到下一个空白列发现)并将这些列读入数据帧直到结束。表

基于索引的方法而不是基于队列的方法的好处是,您不需要重新发现每行中分隔符的位置,也不需要跟踪哪些行片段属于哪个表。对于每行> 2个表的存在也是不可知的。

答案 1 :(得分:0)

我编写了代码,以合并多个垂直分隔的表,每个表具有公共标题。我假设唯一标头的名称不应以点整数结尾。     '''

    def clean(input_file, output_file):
        try:
            df = pd.read_csv(input_file, skiprows=[1,1])
            df = df.drop(df.columns[df.columns.str.contains('unnamed',case = False)],axis=1)
            df = rename_duplicate_columns(df)
        except:
            df =[]
            print("Error: File Not found\t", sys.exc_info() [0])
            exit

        udf = df.loc[:, ~df.columns.str.match(".*\.\d")] 
        udf = udf.dropna(how='all')

        try: 
            table_num = int(df.columns.values[-1].split('.')[-1])
            fdf = udf
            for i in range(1,table_num+1):
                udfi = pd.DataFrame()
                udfi = df.loc[:, df.columns.str.endswith(f'.{i}')]
                udfi.rename(columns = lambda x: '.'.join(x.split('.')[:-1]), inplace=True)
                udfi = udfi.dropna(how='all')
                fdf = fdf.append(udfi,ignore_index=True)
            fdf.to_csv(output_file)

        except ValueError:
            print ("File Contains only single Table")
            exit

    def rename_duplicate_columns(df):
        cols=pd.Series(df.columns)
        for dup in df.columns.get_duplicates(): 
        cols[df.columns.get_loc(dup)]=[dup+'.'+str(d_idx) if d_idx!=0 else 
              dup for d_idx in range(df.columns.get_loc(dup).sum())]
        df.columns=cols
        print(df.columns)
        return df

    clean(input_file, output_file)

'''