在pandas中高效对AI Builder的输出进行后处理,使用前向填充并合并拆分后的行

人工智能 2026-07-07

我正在处理来自Microsoft AI Builder Document Processing的 JSON输出。将JSON展平为pandas DataFrame之后,需要在导出之前对提取的行进行归一化处理。

提取结果通常比较准确,但存在若干可预见的模式,需要进行后处理。

场景1 — 头字段向前填充

TicketBOLDate 仅在每个逻辑分组的第一行被赋值。同一分组中的后续行这些字段都为null。

这些值需要在每个分组内向前填充,直到出现新的非空值 Ticket

场景2 — 移位的单字段行(累计净数量)

在某些情况下,OCR只将 Additive Net Qty 的值移到单独的一行。这一行除了 Additive Net Qty 外,其他字段均为null。

该值必须通过填充缺失的 Additive Net Qty,合并到前一条有效行中;随后删除这条独立的行。

场景3 — 跨多列拆分的行

在某些情况下,一个记录会分成两行相邻的行。第一行仅包含 TicketBOLDate,而 ProductNet QtyAdditive Net Qty 缺失。下一行包含缺失的产品级字段,但头字段为null。

这两行应合并为一个完整的记录,并删除冗余行。

MRE:

import pandas as pd
import numpy as np

df = pd.DataFrame(
    [
        # Normal row
        ["1000", "AAA", "4/1/26", "Regular", 8500, 15],

        # Scenario 1 - Ticket/BOL/Date missing
        ["1001", "ABC", "4/1/26", "Gasoline", 8000, 30],
        [np.nan, np.nan, np.nan, "Ethanol", 500, 12],
        [np.nan, np.nan, np.nan, "Dye", 20, 1],

        # Normal row
        ["1002", "DEF", "4/2/26", "Diesel", 7500, 40],

        # Scenario 2 - Additive Net Qty shifted to next row
        ["1003", "GHI", "4/3/26", "Premium", 9000, np.nan],
        [np.nan, np.nan, np.nan, np.nan, np.nan, 25],

        # Normal row
        ["1004", "JKL", "4/4/26", "Jet Fuel", 6000, 18],

        # Scenario 3 - Product/Net Qty/Additive Qty shifted to next row
        ["1005", "MNO", "4/5/26", np.nan, np.nan, np.nan],
        [np.nan, np.nan, np.nan, "Diesel", 7800, 35],
    ],
    columns=[
        "Ticket",
        "BOL",
        "Date",
        "Product",
        "Net Qty",
        "Additive Net Qty",
    ],
)


# Expected Output

  Ticket  BOL    Date    Product  Net Qty  Additive Net Qty
0   1000  AAA  4/1/26    Regular    8500.0              15.0
1   1001  ABC  4/1/26   Gasoline    8000.0              30.0
2   1001  ABC  4/1/26    Ethanol     500.0              12.0
3   1001  ABC  4/1/26         Dye      20.0               1.0
4   1002  DEF  4/2/26      Diesel    7500.0              40.0
5   1003  GHI  4/3/26     Premium    9000.0              25.0
6   1004  JKL  4/4/26    Jet Fuel    6000.0              18.0
7   1005  MNO  4/5/26      Diesel    7800.0              35.0

解决方案

可以通过向量掩码、.shift().ffill().dropna() 的组合来解决上述三种场景。

import numpy as np
import pandas as pd

# 1. Identify standalone "Additive Net Qty" rows (Scenario 2)
# Check for rows where all columns except 'Additive Net Qty' are null
is_shifted_additive = df[
    ["Ticket", "BOL", "Date", "Product", "Net Qty"]
].isna().all(axis=1) & df["Additive Net Qty"].notna()

# 2. Pull the shifted Additive Net Qty value back up to the previous row
df["Additive Net Qty"] = df["Additive Net Qty"].fillna(
    df["Additive Net Qty"].shift(-1)
)

# 3. Drop the now-redundant standalone Scenario 2 rows
df = df[~is_shifted_additive]

# 4. Forward fill the header columns (Scenario 1 & Scenario 3)
df[["Ticket", "BOL", "Date"]] = df[["Ticket", "BOL", "Date"]].ffill()

# 5. Drop rows missing 'Product' to eliminate empty split headers (Scenario 3)
df_clean = df.dropna(subset=["Product"]).reset_index(drop=True)

print(df_clean)

最终输出

  Ticket  BOL    Date   Product  Net Qty  Additive Net Qty
0   1000  AAA  4/1/26   Regular   8500.0              15.0
1   1001  ABC  4/1/26  Gasoline   8000.0              30.0
2   1001  ABC  4/1/26   Ethanol    500.0              12.0
3   1001  ABC  4/1/26       Dye     20.0               1.0
4   1002  DEF  4/2/26    Diesel   7500.0              40.0
5   1003  GHI  4/3/26   Premium   9000.0              25.0
6   1004  JKL  4/4/26  Jet Fuel   6000.0              18.0
7   1005  MNO  4/5/26    Diesel   7800.0              35.0
站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章