簡體   English   中英

如何從包含許多表的Excel工作表中解析數據幀(使用Python,可能是Pandas)

[英]How to parse dataframes from an excel sheet with many tables (using Python, possibly Pandas)

我正在處理布局糟糕的excel表,我正在嘗試解析並寫入數據庫。

每張表都可以有多個表。 雖然這些可能的表格的標題是已知的,但是哪些表格將在任何給定的表格上都沒有,它們在表格上的確切位置也不是(表格不以一致的方式對齊)。 我添加了兩張可能的工作表布局的圖片來說明這一點: 這個布局有兩個表,而這個表有第一個表,但不在同一個位置,加上一個額外的表。

我所知道的:

  1. 所有可能的表頭,因此每個單獨的表可以通過其標題來標識
  2. 表格由空白單元格分隔。 他們互不接觸。

我的問題是否有一種干凈的方法來處理這個使用一些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)

這是一個特殊的情況,使用現成的模塊可能沒有“干凈”的方法來做到這一點。

一種方法可能會使用您已有的頭信息來查找每個表的起始索引,類似於此解決方案( Python Pandas - 讀取包含多個表的csv文件 ),但在列方向上也有偏移量。

一旦有了每個表的起始位置,就需要確定寬度(先驗已知或通過讀取直到下一個空白列發現)並將這些列讀入數據幀直到表的末尾。

基於索引的方法而不是基於隊列的方法的好處是您不需要重新發現每行中分隔符的位置,也不需要跟蹤哪些行片段屬於哪個表。 每行> 2個表的存在也是不可知的。

我編寫代碼來合並垂直分隔的多個表,每個表中都有公共標題。 我假設唯一標題應該名稱不以點整數來結束。 “””

    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)

“””

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM