import pandas as pd
file_path="d:\\投产计划.xlsx"
# 步骤1:格式化日期列名(去除时分秒,统一为YYYY-MM-DD)
df = pd.read_excel(file_path,engine="openpyxl",na_values=["", "无", 0] )
# 自定义无产出的标识(空值/无/0均视为无产出)
# 步骤1:格式化日期列名(去除时分秒,统一为YYYY-MM-DD)
new_columns = []
for col in df.columns:
try:
# 解析列名为日期,转为标准格式
dt = pd.to_datetime(col)
new_columns.append(dt.strftime("%Y-%m-%d"))
except:
# 非日期列(A/B/C列)保留原名
new_columns.append(col)
df.columns = new_columns
# 步骤2:拆分列类型(固定列 + 日期列)
fixed_cols = ["销售订单号", "物料编码", "工序"] # A/B/C列
date_cols = [col for col in df.columns if col not in fixed_cols] # D列开始的日期列
# ===================== 2. 宽表转长表(核心操作) =====================
# 转换后结构:每行 = 销售订单号 + 物料编码 + 工序 + 日期 + 产出值
df_long = df.melt(id_vars=fixed_cols,
value_vars=date_cols,
var_name="产出日期",
value_name="产出值")
# 步骤3:过滤无产出记录,仅保留有产出的行
df_long = df_long[df_long["产出值"].notna()].copy()
# 确保产出日期为datetime类型(便于排序/计算)
df_long["产出日期"] = pd.to_datetime(df_long["产出日期"])
# ===================== 3. 计算每个物料的最大工序最后产出日期 =====================
# 步骤1:按「销售订单号+物料编码+工序」分组,取该工序最后产出日期
df_proc_last = df_long.groupby(["销售订单号", "物料编码", "工序"], as_index=False)["产出日期"].max()
df_proc_last.rename(columns={"产出日期": "工序最后产出日期"}, inplace=True)
# 步骤2:按物料编码+工序降序排序,取每个物料的最大工序
df_proc_last_sorted = df_proc_last.sort_values(by=["物料编码", "工序"],ascending=[True, False] # 工序降序:4>3>2>1
)
# 去重:每个物料仅保留最大工序的记录
df_material_max_proc = df_proc_last_sorted.drop_duplicates(
subset=["物料编码"],
keep="first"
)
df_material_max_proc.rename(
columns={
"工序": "物料最大工序",
"工序最后产出日期": "物料最大工序最后产出日期"
},
inplace=True
)
# ===================== 4. 筛选:仅保留≥2个物料的销售订单 =====================
# 步骤1:统计每个销售订单的物料数量
order_material_count = df_material_max_proc.groupby("销售订单号")["物料编码"].nunique().reset_index()
order_material_count.columns = ["销售订单号", "物料数量"]
# 步骤2:筛选出物料数≥2的订单
valid_orders = order_material_count[order_material_count["物料数量"] >= 2]["销售订单号"].tolist()
df_valid = df_material_max_proc[df_material_max_proc["销售订单号"].isin(valid_orders)].copy()
# ===================== 5. 计算订单内日期差,筛选>1天的订单 =====================
# 步骤1:按销售订单号分组,计算订单内最大/最小日期及差值
df_order_diff = df_valid.groupby("销售订单号", as_index=False).agg(
物料数量=("物料编码", "count"),
订单内最早最大工序日期=("物料最大工序最后产出日期", "min"),
订单内最晚最大工序日期=("物料最大工序最后产出日期", "max")
)
# 步骤2:计算日期差(天数)
sapace_day="日期差值_天数"
df_order_diff[sapace_day] = (df_order_diff["订单内最晚最大工序日期"] - df_order_diff["订单内最早最大工序日期"]).dt.days
# 步骤3:筛选出日期差>1天的订单
df_final = df_order_diff[df_order_diff[sapace_day] > 1].copy()
# 步骤4:关联物料明细(可选:展示订单下所有物料的详情)
df_final_detail = pd.merge(
df_final,
df_valid[["销售订单号", "物料编码", "物料最大工序", "物料最大工序最后产出日期"]],
on="销售订单号",
how="left"
)
# ===================== 6. 结果格式化与输出 =====================
# 日期显示为YYYY-MM-DD(无时分秒)
date_cols_format = ["订单内最早最大工序日期", "订单内最晚最大工序日期", "物料最大工序最后产出日期"]
for col in date_cols_format:
if col in df_final_detail.columns:
df_final_detail[col] = df_final_detail[col].dt.strftime("%Y-%m-%d")
# 整理列顺序(便于查看)
df_final_detail = df_final_detail[
["销售订单号", "物料数量", sapace_day, "订单内最早最大工序日期",
"订单内最晚最大工序日期", "物料编码", "物料最大工序", "物料最大工序最后产出日期"]
].reset_index(drop=True)
# 输出结果
print("=== 仅含≥2个物料且日期差>1天的订单详情 ===")
print(df_final_detail)