4.5 统计stats模块

本文介绍NumPy和SciPy库在统计分析中的应用,包括如何使用NumPy进行基本统计计算,以及如何利用SciPy进行概率分布和样本检验。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

NumPy库已经提供了一些基本的统计函数,如求期望、方差、中位数、最大值和最小值等。示例代码:

import numpy as np

#构建一个1000个随机变量的数组
x = np.random.randn(1000)

#对数组元素的值进行统计
mean = x.mean()
std = x.std()
var = x.var()

print(mean,std,var)

运行结果:

(0.02877273942510088, 0.97623362287515114, 0.95303208643194282)

mean是期望值,std是标准差,var是方差,使用numpy.array对象已有的方法获得统计指标快速有效,而SciPy库则提供了更高级的统计工具,它的Stats模块包含了多种概率分布的随机变量(随机变量是指概率论中的概念,不是Python中的变量),其中随机变量又分为连续和离散两种。所有的连续随机变量都是rv_continuous的派生类的对象,而所有的离散随机变量都是rv_discrete的派生类的对象。

4.5.1 连续概率分布

SciPy的stats模块提供了大约80种连续随机变量和10多种离散分布变量,这些分布都依赖于numpy.random函数。可以通过如下语句获得stats模块中所有的连续随机变量,示例代码:

from scipy import stats
[k for k, v in stats.__dict__.items() if isinstance(v, stats.rv_continuous)]

运行结果:

['ksone', 'kstwobign', 'norm', 'alpha', 'anglit', 'arcsine', 'beta', 'betaprime', 'bradford', 'burr', 'burr12', 'fisk', 'cauchy', 'chi', 'chi2', 'cosine', 'dgamma', 'dweibull', 'expon', 'exponnorm', 'exponweib', 'exponpow', 'fatiguelife', 'foldcauchy', 'f', 'foldnorm', 'frechet_r', 'weibull_min', 'frechet_l', 'weibull_max', 'genlogistic', 'genpareto', 'genexpon', 'genextreme', 'gamma', 'erlang', 'gengamma', 'genhalflogistic', 'gompertz', 'gumbel_r', 'gumbel_l', 'halfcauchy', 'halflogistic', 'halfnorm', 'hypsecant', 'gausshyper', 'invgamma', 'invgauss', 'invweibull', 'johnsonsb', 'johnsonsu', 'laplace', 'levy', 'levy_l', 'levy_stable', 'logistic', 'loggamma', 'loglaplace', 'lognorm', 'gilbrat', 'maxwell', 'mielke', 'kappa4', 'kappa3', 'nakagami', 'ncx2', 'ncf', 't', 'nct', 'pareto', 'lomax', 'pearson3', 'powerlaw', 'powerlognorm', 'powernorm', 'rdist', 'rayleigh', 'reciprocal', 'rice', 'recipinvgauss', 'semicircular', 'skewnorm', 'trapz', 'triang', 'truncexpon', 'truncnorm', 'tukeylambda', 'uniform', 'vonmises', 'vonmises_line', 'wald', 'wrapcauchy', 'gennorm', 'halfgennorm']

连续随机变量对象主要使用如下方法,下表所示:

方法名全称功能
rvsRandom Variates of given type对随机变量进行随机取值,通过size参数指定输出数组的大小
pdfProbability Density Function随机变量的概率密度函数
cdfCumulative Distribution Function随机变量的累积分布函数,它是概率密度函数的积分
sfSurvival function随机变量的生存函数,它的值是1-cdf(t)
ppfPercent point function累积分布函数的反函数
statstatistics计算随机变量的期望值和方差
fitfit对一组随机取样进行拟合,找出最适合取样数据的概率密度函数的系数

下面以标准正态分布(函数表示f(x)=(1/√2π)exp(-x^2/2))为例,简单介绍随机变量的用法。示例代码:

from scipy import stats
# 设置正态分布参数,其中loc是期望值参数,scale是标准差参数
X = stats.norm(loc=1.0, scale=2.0)

# 计算随机变量的期望值和方差
print(X.stats())

运行结果:

(array(1.0), array(4.0))

以上代码说明,norm可以像函数一样调用,通过loc和scale参数可以指定随机变量的偏移和缩放参数。对于正态分布的随机变量来说,这两个参数相当于指定其期望值和标准差,标准差是方差的算术平方根。X的stats()方法,可以计算随机变量X分布的特征值,如期望值和方差。
此外,通过调用随机变量X的rvs()方法,可以得到包含一万次随机取样值的数组x,然后调用NumPy的mean()和var()计算此数组的均值和方差,其结果符合随机变量X的特性,示例代码:

#对随机变量取10000个值
x = X.rvs(size=10000)
print(np.mean(x), np.var(x))

运行结果

(1.0287787687588861, 3.9944276709242805)

使用fit()方法对随机取样序列x进行拟合,它返回的是与随机取样值最吻合的随机变量参数,示例代码:

#输出随机序列的期望值和标准差
print(stats.norm.fit(x))

运行结果:

(1.0287787687588861, 1.998606432223283)

在下面的例子中,计算取样值x的直方图统计以及累计分布,并与随机变量的概率密度函数和累积分布函数进行比较。示例代码:

pdf, t = np.histogram(x, bins=100, normed=True)
t = (t[:-1]+t[1:])*0.5
cdf = np.cumsum(pdf) * (t[1] - t[0])
p_error = pdf - X.pdf(t)
c_error = cdf - X.cdf(t)
print("max pdf error: {}, max cdf error: {}".format(np.abs(p_error).max(), np.abs(c_error).max()))

运行结果:

max pdf error: 0.0208405611169, max cdf error: 0.0126874590568

通过绘图的方式查看概率密度函数求得的理论值(theory value)和直方图统计值(statistic value)的结果,可以看出二者是一致的,示例代码:

import pylab as pl
pl.plot(t, pdf, color="green", label = "statistic value")
pl.plot(t, X.pdf(t), color="yellow", label ="theory value")
pl.legend(loc = "best")
pl.show()

绘图如下:


正态分布的概率密度函数

也可以用同样的方式显示随机变量X的累积分布和数组pdf的累加结果,示例代码:

import pylab as pl
pl.plot(t, cdf, color="green", label = "statistic value")
pl.plot(t, X.cdf(t), color="yellow", label ="theory value")
pl.legend(loc = "best")
pl.show()

绘图如下:


累积分布函数

4.5.2 离散概率分布

投掷有六个面的骰子时,只能获得1到6的整数,因此所得到的概率分布是离散的。我们以值域离散的分布称为离散概率分布,包括泊松分布、二项分布、几何分布等。通常使用概率质量函数(PMF)描述其分布情况,如几何分布函数PMF=(1-p)(k-1)p。
在stats模块中所有描述离散分布的随机变量都从rv_discrete类继承,也可以直接用rv_discrete类自定义离散概率分布。假设有一个不均匀的骰子,其各点出现的概率不相等,我们用如下代码定义其分布,示例代码:

# 数组x保存骰子的所有可能值,数组p保存每个值出现的概率
x = range(1, 7)
p = (0.4, 0.2, 0.1, 0.1, 0.1, 0.1)

# 创建表示这个骰子的随机变量dice,调用其rvs()方法投掷此骰子20次,获得符合概率p的随机数
dice = stats.rv_discrete(values=(x, p))
print(dice.rvs(size=20))

运行结果:

array([3, 6, 4, 5, 5, 2, 1, 3, 3, 1, 1, 3, 1, 5, 1, 3, 4, 1, 2, 2])

除了自定义离散概率分布,我们也可以利用stats模块里的函数定义各种分布。下面以生成几何分布为例,其函数是geom(),示例代码:

import numpy as np
from scipy.stats import geom

# 设置几何分布的参数
p = 0.5
dist = geom(p)

# 设置样本区间  
x = np.linspace(0, 5, 1000)  

# 得到几何分布的 PMF 和CDF  
pmf = dist.pmf(x) 
cdf = dist.cdf(x)  

# 生成500个随机数  
sample = dist.rvs(500)

4.5.3 描述与检验函数

SciPy中有超过60种统计函数。stats模块包括了诸如kstest 和normaltest等样本测试函数,用来检测样本是否服从某种分布。在使用这些工具前,要对数据有较好的理解,否则可能会误读它们的结果。样本分布检验为例,示例代码:

import numpy as np 
from scipy import stats 

# 生成包括100个服从正态分布的随机数样本
sample = np.random.randn(100) 

# 用normaltest检验原假设
out = stats.normaltest(sample) 
print('normaltest output') 
print('Z-score = ' + str(out[0])) 
print('P-value = ' + str(out[1])) 

# kstest 是检验拟合度的Kolmogorov-Smirnov检验,这里针对正态分布进行检验
# D是KS统计量的值,越接近0越好
out = stats.kstest(sample, 'norm') 
print('\nkstest output for the Normal distribution') 
print('D = ' + str(out[0])) 
print('P-value = ' + str(out[1])) 

# 类似地可以针对其他分布进行检验,例如Wald分布
out = stats.kstest(sample, 'wald') 
print('\nkstest output for the Wald distribution') 
print('D = ' + str(out[0])) 
print('P-value = ' + str(out[1]))

SciPy的stats模块中还提供了一些描述函数,如几何平均(gmean)、偏度(skew
)、样本频数(itemfreq)等。示例代码:

import numpy as np 
from scipy import stats 

# 生成包括100个服从正态分布的随机数样本
sample = np.random.randn(100) 

# 调和平均数,样本值须大于0 
out = stats.hmean(sample[sample > 0]) 
print('Harmonic mean = ' + str(out)) 

# 计算-1到1之间样本的均值
out = stats.tmean(sample, limits=(-1, 1)) 
print('\nTrimmed mean = ' + str(out)) 

# 计算样本偏度
out = stats.skew(sample) 
print('\nSkewness = ' + str(out)) 

# 函数describe可以一次给出样本的多种描述统计结果
out = stats.describe(sample) 
print('\nSize = ' + str(out[0])) 
print('Min = ' + str(out[1][0])) 
print('Max = ' + str(out[1][1])) 
print('Mean = ' + str(out[2])) 
print('Variance = ' + str(out[3])) 
print('Skewness = ' + str(out[4])) 
print('Kurtosis = ' + str(out[5]))
<think>我们选择另一个模块:库存管理(Inventory Management)中的库存盘点(Physical Inventory)功能。 我们将实现一个ABAP程序,用于执行以下任务: 1. 创建库存盘点凭证 2. 打印库存盘点清单 3. 输入实际盘点数量 4. 过账盘点差异 注意:由于完整代码较长,我们将展示关键部分,并尽量保持结构清晰。</think>### 采购订单处理与供应商评估系统 以下代码实现了采购订单处理、供应商绩效评估和采购分析功能: ```abap REPORT Z_PURCHASE_ORDER_MANAGEMENT. * 采购订单抬头结构 TYPES: BEGIN OF ty_po_header, ebeln TYPE ebeln, " 采购订单号 bukrs TYPE bukrs, " 公司代码 lifnr TYPE lifnr, " 供应商号 name1 TYPE name1, " 供应商名称 bedat TYPE bedat, " 凭证日期 zterm TYPE dzterm, " 付款条件 waers TYPE waers, " 货币 netwr TYPE netwr, " 订单净值 inco1 TYPE inco1, " 国际贸易条款1 inco2 TYPE inco2, " 国际贸易条款2 status TYPE c LENGTH 20, " 订单状态 END OF ty_po_header. * 采购订单行项结构 TYPES: BEGIN OF ty_po_item, ebeln TYPE ebeln, " 采购订单号 ebelp TYPE ebelp, " 行项目号 matnr TYPE matnr, " 物料号 maktx TYPE maktx, " 物料描述 menge TYPE bstmg, " 订单数量 meins TYPE meins, " 单位 netpr TYPE netpr, " 净价 peinh TYPE peinh, " 价格单位 werks TYPE werks_d, " 工厂 lgort TYPE lgort_d, " 库存地点 afnam TYPE afnam, " 收货方 eindt TYPE eindt, " 交货日期 wemng TYPE wemng, " 收货数量 open_qty TYPE bstmg, " 未清数量 status TYPE c LENGTH 20, " 行项目状态 END OF ty_po_item. * 供应商评估结构 TYPES: BEGIN OF ty_vendor_eval, lifnr TYPE lifnr, " 供应商号 name1 TYPE name1, " 供应商名称 total_orders TYPE i, " 总订单数 total_value TYPE netwr, " 总订单金额 avg_delivery_days TYPE p DECIMALS 1, " 平均交货天数 quality_rate TYPE p DECIMALS 1, " 质量评分 ontime_rate TYPE p DECIMALS 1, " 准时率 overall_score TYPE p DECIMALS 1, " 综合评分 eval_class TYPE c LENGTH 20, " 评估等级 END OF ty_vendor_eval. * 输出结构 TYPES: BEGIN OF ty_output, ebeln TYPE ebeln, " 采购订单号 ebelp TYPE ebelp, " 行项目号 bedat TYPE bedat, " 凭证日期 lifnr TYPE lifnr, " 供应商号 name1 TYPE name1, " 供应商名称 matnr TYPE matnr, " 物料号 maktx TYPE maktx, " 物料描述 menge TYPE bstmg, " 订单数量 meins TYPE meins, " 单位 netpr TYPE netpr, " 净价 waers TYPE waers, " 货币 eindt TYPE eindt, " 交货日期 wemng TYPE wemng, " 收货数量 open_qty TYPE bstmg, " 未清数量 delivery_days TYPE i, " 交货延迟天数 status TYPE c LENGTH 20, " 行项目状态 eval_class TYPE c LENGTH 20, " 供应商等级 END OF ty_output. DATA: lt_output TYPE TABLE OF ty_output, lt_vendor_eval TYPE TABLE OF ty_vendor_eval. * 选择屏幕 SELECTION-SCREEN BEGIN OF BLOCK b1 WITH FRAME TITLE TEXT-001. SELECT-OPTIONS: s_ebeln FOR ekko-ebeln, " 采购订单 s_matnr FOR ekpo-matnr, " 物料号 s_lifnr FOR ekko-lifnr, " 供应商 s_bedat FOR ekko-bedat, " 凭证日期 s_eindt FOR ekpo-eindt. " 交货日期 SELECTION-SCREEN END OF BLOCK b1. SELECTION-SCREEN BEGIN OF BLOCK b2 WITH FRAME TITLE TEXT-002. PARAMETERS: p_eval RADIOBUTTON GROUP gr1 DEFAULT 'X' USER-COMMAND cmd, p_crea RADIOBUTTON GROUP gr1. SELECTION-SCREEN END OF BLOCK b2. START-OF-SELECTION. IF p_eval = 'X'. PERFORM get_purchase_data. PERFORM calculate_status. PERFORM vendor_evaluation. PERFORM display_results. ELSEIF p_crea = 'X'. PERFORM create_purchase_order. ENDIF. *&---------------------------------------------------------------------* *& Form GET_PURCHASE_DATA *&---------------------------------------------------------------------* FORM get_purchase_data. DATA: lt_header TYPE TABLE OF ty_po_header, lt_items TYPE TABLE OF ty_po_item, lt_vendors TYPE TABLE OF lfa1, lt_mat_desc TYPE TABLE OF makt, lt_gr_data TYPE TABLE OF ekbe. " 获取采购订单抬头数据 SELECT a~ebeln, a~bukrs, a~lifnr, a~bedat, a~zterm, a~waers, a~netwr, a~inco1, a~inco2 FROM ekko AS a INTO TABLE @lt_header WHERE ebeln IN @s_ebeln AND lifnr IN @s_lifnr AND bedat IN @s_bedat. IF lt_header IS INITIAL. MESSAGE '未找到采购订单' TYPE 'I' DISPLAY LIKE 'E'. RETURN. ENDIF. " 获取供应商名称 SELECT lifnr, name1 FROM lfa1 INTO TABLE @lt_vendors FOR ALL ENTRIES IN @lt_header WHERE lifnr = @lt_header-lifnr. " 更新抬头供应商名称 LOOP AT lt_header ASSIGNING FIELD-SYMBOL(<fs_header>). READ TABLE lt_vendors INTO DATA(ls_vendor) WITH KEY lifnr = <fs_header>-lifnr. IF sy-subrc = 0. <fs_header>-name1 = ls_vendor-name1. ENDIF. ENDLOOP. " 获取采购订单行项目 SELECT ebeln, ebelp, matnr, menge, meins, netpr, peinh, werks, lgort, afnam, eindt FROM ekpo INTO TABLE @lt_items FOR ALL ENTRIES IN @lt_header WHERE ebeln = @lt_header-ebeln AND matnr IN @s_matnr AND eindt IN @s_eindt. " 获取物料描述 IF lt_items IS NOT INITIAL. SELECT matnr, maktx FROM makt INTO TABLE @lt_mat_desc FOR ALL ENTRIES IN @lt_items WHERE matnr = @lt_items-matnr AND spras = @sy-langu. LOOP AT lt_items ASSIGNING FIELD-SYMBOL(<fs_item>). READ TABLE lt_mat_desc INTO DATA(ls_mat_desc) WITH KEY matnr = <fs_item>-matnr. IF sy-subrc = 0. <fs_item>-maktx = ls_mat_desc-maktx. ENDIF. ENDLOOP. ENDIF. " 获取收货数据 SELECT ebeln, ebelp, bewtp, menge, shkzg, gjahr, belnr, buzei FROM ekbe INTO TABLE @lt_gr_data FOR ALL ENTRIES IN @lt_items WHERE ebeln = @lt_items-ebeln AND ebelp = @lt_items-ebelp AND bewtp = 'E'. " 收货 " 计算收货数量和未清数量 LOOP AT lt_items ASSIGNING <fs_item>. LOOP AT lt_gr_data INTO DATA(ls_gr) WHERE ebeln = <fs_item>-ebeln AND ebelp = <fs_item>-ebelp. IF ls_gr-shkzg = 'S'. <fs_item>-wemng = <fs_item>-wemng + ls_gr-menge. ELSEIF ls_gr-shkzg = 'H'. <fs_item>-wemng = <fs_item>-wemng - ls_gr-menge. ENDIF. ENDLOOP. <fs_item>-open_qty = <fs_item>-menge - <fs_item>-wemng. ENDLOOP. " 组合输出数据 LOOP AT lt_header INTO DATA(ls_header). LOOP AT lt_items INTO DATA(ls_item) WHERE ebeln = ls_header-ebeln. APPEND INITIAL LINE TO lt_output ASSIGNING FIELD-SYMBOL(<fs_out>). " 抬头信息 <fs_out>-ebeln = ls_header-ebeln. <fs_out>-ebelp = ls_item-ebelp. <fs_out>-bedat = ls_header-bedat. <fs_out>-lifnr = ls_header-lifnr. <fs_out>-name1 = ls_header-name1. <fs_out>-waers = ls_header-waers. " 行项目信息 <fs_out>-matnr = ls_item-matnr. <fs_out>-maktx = ls_item-maktx. <fs_out>-menge = ls_item-menge. <fs_out>-meins = ls_item-meins. <fs_out>-netpr = ls_item-netpr. <fs_out>-eindt = ls_item-eindt. <fs_out>-wemng = ls_item-wemng. <fs_out>-open_qty = ls_item-open_qty. ENDLOOP. ENDLOOP. ENDFORM. *&---------------------------------------------------------------------* *& Form CALCULATE_STATUS *&---------------------------------------------------------------------* FORM calculate_status. LOOP AT lt_output ASSIGNING FIELD-SYMBOL(<fs_out>). " 计算交货延迟 IF <fs_out>-wemng > 0 AND <fs_out>-eindt IS NOT INITIAL. DATA(lv_gr_date) = sy-datum. " 实际收货日期(简化处理) <fs_out>-delivery_days = lv_gr_date - <fs_out>-eindt. ENDIF. " 确定行项目状态 IF <fs_out>-open_qty = 0. <fs_out>-status = '已完成'. ELSEIF <fs_out>-wemng > 0. <fs_out>-status = '部分收货'. ELSE. <fs_out>-status = '未收货'. ENDIF. " 延迟检查 IF <fs_out>-delivery_days > 0 AND <fs_out>-status <> '已完成'. <fs_out>-status = |延迟: { <fs_out>-delivery_days }天|. ENDIF. ENDLOOP. ENDFORM. *&---------------------------------------------------------------------* *& Form VENDOR_EVALUATION *&---------------------------------------------------------------------* FORM vendor_evaluation. DATA: lt_vendor_stats TYPE TABLE OF ty_vendor_eval. " 按供应商汇总数据 LOOP AT lt_output ASSIGNING <fs_out>. READ TABLE lt_vendor_eval ASSIGNING FIELD-SYMBOL(<fs_vendor>) WITH KEY lifnr = <fs_out>-lifnr. IF sy-subrc <> 0. APPEND INITIAL LINE TO lt_vendor_eval ASSIGNING <fs_vendor>. <fs_vendor>-lifnr = <fs_out>-lifnr. <fs_vendor>-name1 = <fs_out>-name1. ENDIF. " 统计订单数和金额 <fs_vendor>-total_orders = <fs_vendor>-total_orders + 1. <fs_vendor>-total_value = <fs_vendor>-total_value + ( <fs_out>-netpr * <fs_out>-menge ). " 计算平均交货延迟 IF <fs_out>-delivery_days > 0. <fs_vendor>-avg_delivery_days = ( ( <fs_vendor>-avg_delivery_days * ( <fs_vendor>-total_orders - 1 ) ) + <fs_out>-delivery_days ) / <fs_vendor>-total_orders. ENDIF. " 质量评分(简化处理) <fs_vendor>-quality_rate = 4.5. " 假设固定值 ENDLOOP. " 计算供应商评分 LOOP AT lt_vendor_eval ASSIGNING <fs_vendor>. " 准时率计算(假设目标为0延迟) <fs_vendor>-ontime_rate = 100 - ( <fs_vendor>-avg_delivery_days * 5 ). IF <fs_vendor>-ontime_rate < 0. <fs_vendor>-ontime_rate = 0. ENDIF. " 综合评分 = 质量评分(40%) + 准时率(40%) + 价格因素(20%) <fs_vendor>-overall_score = ( <fs_vendor>-quality_rate * 0.4 ) + ( <fs_vendor>-ontime_rate / 20 * 0.4 ) + ( 4.0 * 0.2 ). " 假设价格因素固定为4.0 " 评估等级 CASE <fs_vendor>-overall_score. WHEN 4.5 TO 5.0. <fs_vendor>-eval_class = 'A级(优秀)'. WHEN 4.0 TO 4.4. <fs_vendor>-eval_class = 'B级(良好)'. WHEN 3.0 TO 3.9. <fs_vendor>-eval_class = 'C级(合格)'. WHEN OTHERS. <fs_vendor>-eval_class = 'D级(不合格)'. ENDCASE. " 更新输出表 LOOP AT lt_output ASSIGNING <fs_out> WHERE lifnr = <fs_vendor>-lifnr. <fs_out>-eval_class = <fs_vendor>-eval_class. ENDLOOP. ENDLOOP. ENDFORM. *&---------------------------------------------------------------------* *& Form DISPLAY_RESULTS *&---------------------------------------------------------------------* FORM display_results. DATA: lt_fieldcat TYPE slis_t_fieldcat_alv, ls_layout TYPE slis_layout_alv. " 构建字段目录 PERFORM build_field_catalog CHANGING lt_fieldcat. " 设置布局 ls_layout-colwidth_optimize = 'X'. ls_layout-zebra = 'X'. " 显示ALV报表 CALL FUNCTION 'REUSE_ALV_GRID_DISPLAY' EXPORTING i_callback_program = sy-repid i_callback_pf_status_set = 'SET_PF_STATUS' i_callback_user_command = 'USER_COMMAND' is_layout = ls_layout it_fieldcat = lt_fieldcat i_save = 'A' TABLES t_outtab = lt_output EXCEPTIONS program_error = 1 OTHERS = 2. IF sy-subrc <> 0. MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4. ENDIF. ENDFORM. *&---------------------------------------------------------------------* *& Form BUILD_FIELD_CATALOG *&---------------------------------------------------------------------* FORM build_field_catalog CHANGING pt_fieldcat TYPE slis_t_fieldcat_alv. DATA: ls_fieldcat TYPE slis_fieldcat_alv. " 采购订单号 ls_fieldcat-fieldname = 'EBELN'. ls_fieldcat-seltext_l = '采购订单'. ls_fieldcat-key = 'X'. APPEND ls_fieldcat TO pt_fieldcat. " 行项目号 ls_fieldcat-fieldname = 'EBELP'. ls_fieldcat-seltext_l = '行项目'. ls_fieldcat-key = 'X'. APPEND ls_fieldcat TO pt_fieldcat. " 供应商名称 ls_fieldcat-fieldname = 'NAME1'. ls_fieldcat-seltext_l = '供应商'. APPEND ls_fieldcat TO pt_fieldcat. " 物料描述 ls_fieldcat-fieldname = 'MAKTX'. ls_fieldcat-seltext_l = '物料描述'. APPEND ls_fieldcat TO pt_fieldcat; " 订单数量 ls_fieldcat-fieldname = 'MENGE'. ls_fieldcat-seltext_l = '订单数量'. APPEND ls_fieldcat TO pt_fieldcat; " 收货数量 ls_fieldcat-fieldname = 'WEMNG'. ls_fieldcat-seltext_l = '收货数量'. APPEND ls_fieldcat TO pt_fieldcat; " 未清数量 ls_fieldcat-fieldname = 'OPEN_QTY'. ls_fieldcat-seltext_l = '未清数量'. APPEND ls_fieldcat TO pt_fieldcat; " 交货日期 ls_fieldcat-fieldname = 'EINDT'. ls_fieldcat-seltext_l = '交货日期'. ls_fieldcat-datatype = 'DATS'. APPEND ls_fieldcat TO pt_fieldcat; " 延迟天数 ls_fieldcat-fieldname = 'DELIVERY_DAYS'. ls_fieldcat-seltext_l = '延迟天数'. ls_fieldcat-emphasize = 'C300'. " 黄色高亮延迟 APPEND ls_fieldcat TO pt_fieldcat; " 状态 ls_fieldcat-fieldname = 'STATUS'. ls_fieldcat-seltext_l = '状态'. ls_fieldcat-emphasize = 'C500'. " 红色高亮问题状态 APPEND ls_fieldcat TO pt_fieldcat; " 供应商等级 ls_fieldcat-fieldname = 'EVAL_CLASS'. ls_fieldcat-seltext_l = '供应商等级'. APPEND ls_fieldcat TO pt_fieldcat; ENDFORM. *&---------------------------------------------------------------------* *& Form SET_PF_STATUS *&---------------------------------------------------------------------* FORM set_pf_status USING rt_extab TYPE slis_t_extab. SET PF-STATUS 'PURCHASE_ORDER_STATUS'. ENDFORM. *&---------------------------------------------------------------------* *& Form USER_COMMAND *&---------------------------------------------------------------------* FORM user_command USING r_ucomm LIKE sy-ucomm rs_selfield TYPE slis_selfield. CASE r_ucomm. WHEN '&IC1'. " 双击事件 CASE rs_selfield-fieldname. WHEN 'EBELN'. " 查看采购订单 SET PARAMETER ID 'BES' FIELD rs_selfield-value. CALL TRANSACTION 'ME23N' AND SKIP FIRST SCREEN. WHEN 'LIFNR'. " 查看供应商 SET PARAMETER ID 'LIF' FIELD rs_selfield-value. CALL TRANSACTION 'XK03' AND SKIP FIRST SCREEN. WHEN 'MATNR'. " 查看物料 SET PARAMETER ID 'MAT' FIELD rs_selfield-value. CALL TRANSACTION 'MM03' AND SKIP FIRST SCREEN. ENDCASE. WHEN 'CREATE_GR'. " 创建收货 PERFORM create_goods_receipt. WHEN 'VENDOR_EVAL'. " 供应商评估报告 PERFORM display_vendor_evaluation. WHEN 'PRICE_ANALYSIS'. " 价格分析 PERFORM price_analysis. ENDCASE. ENDFORM. *&---------------------------------------------------------------------* *& Form CREATE_GOODS_RECEIPT *&---------------------------------------------------------------------* FORM create_goods_receipt. DATA: lt_selected TYPE TABLE OF ty_output. " 获取选中的行项目 CALL FUNCTION 'REUSE_ALV_GRID_LAYOUT_INFO_GET' IMPORTING et_index_rows = DATA(lt_index_rows). " 为选中的行项目创建收货 LOOP AT lt_index_rows INTO DATA(ls_row). READ TABLE lt_output INTO DATA(ls_selected) INDEX ls_row-index. IF sy-subrc = 0 AND ls_selected-open_qty > 0. PERFORM create_single_gr USING ls_selected. ENDIF. ENDLOOP. " 刷新显示 PERFORM refresh_display. ENDFORM. *&---------------------------------------------------------------------* *& Form CREATE_SINGLE_GR *&---------------------------------------------------------------------* FORM create_single_gr USING is_po TYPE ty_output. DATA: lt_items TYPE TABLE OF bapi_po_item, ls_item TYPE bapi_po_item, lt_return TYPE TABLE OF bapiret2. " 准备收货项目 ls_item-po_item = is_po-ebelp. ls_item-delivery_note = 'GR-' && sy-datum. " 交货单号 ls_item-vendor = is_po-lifnr. ls_item-material = is_po-matnr. ls_item-plant = '1000'. " 假设工厂 ls_item-stge_loc = '0001'. " 库存地点 ls_item-po_number = is_po-ebeln. ls_item-entry_qnt = is_po-open_qty. " 收货数量 ls_item-entry_uom = is_po-meins. APPEND ls_item TO lt_items. " 调用BAPI创建收货 CALL FUNCTION 'BAPI_GOODSMVT_CREATE' EXPORTING goodsmvt_header = VALUE bapi2017_gm_head_01( pstng_date = sy-datum doc_date = sy-datum ref_doc_no = is_po-ebeln ) goodsmvt_code = '01' " 采购订单收货 TABLES goodsmvt_item = lt_items return = lt_return. " 检查结果 READ TABLE lt_return TRANSPORTING NO FIELDS WITH KEY type = 'E'. IF sy-subrc = 0. PERFORM display_messages USING lt_return. ELSE. " 提交更改 CALL FUNCTION 'BAPI_TRANSACTION_COMMIT' EXPORTING wait = 'X'. MESSAGE s398(00) WITH '采购订单' is_po-ebeln '收货成功'. " 更新输出表 READ TABLE lt_output ASSIGNING FIELD-SYMBOL(<fs_out>) WITH KEY ebeln = is_po-ebeln ebelp = is_po-ebelp. IF sy-subrc = 0. <fs_out>-wemng = <fs_out>-wemng + is_po-open_qty. <fs_out>-open_qty = 0. <fs_out>-status = '已完成'. ENDIF. ENDIF. ENDFORM. *&---------------------------------------------------------------------* *& Form DISPLAY_VENDOR_EVALUATION *&---------------------------------------------------------------------* FORM display_vendor_evaluation. DATA: lt_fieldcat TYPE slis_t_fieldcat_alv, ls_layout TYPE slis_layout_alv. " 构建字段目录 PERFORM build_vendor_catalog CHANGING lt_fieldcat. " 设置布局 ls_layout-colwidth_optimize = 'X'. ls_layout-zebra = 'X'. " 显示供应商评估报表 CALL FUNCTION 'REUSE_ALV_GRID_DISPLAY' EXPORTING is_layout = ls_layout it_fieldcat = lt_fieldcat TABLES t_outtab = lt_vendor_eval. ENDFORM. *&---------------------------------------------------------------------* *& Form BUILD_VENDOR_CATALOG *&---------------------------------------------------------------------* FORM build_vendor_catalog CHANGING pt_fieldcat TYPE slis_t_fieldcat_alv. DATA: ls_fieldcat TYPE slis_fieldcat_alv. " 供应商号 ls_fieldcat-fieldname = 'LIFNR'. ls_fieldcat-seltext_l = '供应商号'. APPEND ls_fieldcat TO pt_fieldcat. " 供应商名称 ls_fieldcat-fieldname = 'NAME1'. ls_fieldcat-seltext_l = '供应商名称'. APPEND ls_fieldcat TO pt_fieldcat; " 总订单数 ls_fieldcat-fieldname = 'TOTAL_ORDERS'. ls_fieldcat-seltext_l = '总订单数'; APPEND ls_fieldcat TO pt_fieldcat; " 总订单金额 ls_fieldcat-fieldname = 'TOTAL_VALUE'. ls_fieldcat-seltext_l = '总金额'; APPEND ls_fieldcat TO pt_fieldcat; " 平均交货天数 ls_fieldcat-fieldname = 'AVG_DELIVERY_DAYS'. ls_fieldcat-seltext_l = '平均延迟(天)'; ls_fieldcat-datatype = 'DEC'; ls_fieldcat-decimals = 1; APPEND ls_fieldcat TO pt_fieldcat; " 质量评分 ls_fieldcat-fieldname = 'QUALITY_RATE'. ls_fieldcat-seltext_l = '质量评分'; ls_fieldcat-datatype = 'DEC'; ls_fieldcat-decimals = 1; APPEND ls_fieldcat TO pt_fieldcat; " 准时率 ls_fieldcat-fieldname = 'ONTIME_RATE'. ls_fieldcat-seltext_l = '准时率(%)'; ls_fieldcat-datatype = 'DEC'; ls_fieldcat-decimals = 1; APPEND ls_fieldcat TO pt_fieldcat; " 综合评分 ls_fieldcat-fieldname = 'OVERALL_SCORE'. ls_fieldcat-seltext_l = '综合评分'; ls_fieldcat-datatype = 'DEC'; ls_fieldcat-decimals = 1; APPEND ls_fieldcat TO pt_fieldcat; " 评估等级 ls_fieldcat-fieldname = 'EVAL_CLASS'. ls_fieldcat-seltext_l = '评估等级'; APPEND ls_fieldcat TO pt_fieldcat; ENDFORM. *&---------------------------------------------------------------------* *& Form PRICE_ANALYSIS *&---------------------------------------------------------------------* FORM price_analysis. DATA: lt_prices TYPE TABLE OF ty_po_item, lt_fieldcat TYPE slis_t_fieldcat_alv, ls_layout TYPE slis_layout_alv. " 获取价格数据 SELECT a~matnr, b~maktx, a~lifnr, c~name1, AVG( a~netpr ) AS avg_price, MIN( a~netpr ) AS min_price, MAX( a~netpr ) AS max_price FROM ekpo AS a INNER JOIN makt AS b ON b~matnr = a~matnr AND b~spras = @sy-langu INNER JOIN lfa1 AS c ON c~lifnr = a~lifnr WHERE a~matnr IN @s_matnr AND a~loekz = @space " 未删除项目 GROUP BY a~matnr, b~maktx, a~lifnr, c~name1 INTO TABLE @DATA(lt_price_data). " 构建字段目录 PERFORM build_price_catalog CHANGING lt_fieldcat. " 设置布局 ls_layout-colwidth_optimize = 'X'. ls_layout-zebra = 'X'. " 显示价格分析报表 CALL FUNCTION 'REUSE_ALV_GRID_DISPLAY' EXPORTING is_layout = ls_layout it_fieldcat = lt_fieldcat TABLES t_outtab = lt_price_data. ENDFORM. *&---------------------------------------------------------------------* *& Form BUILD_PRICE_CATALOG *&---------------------------------------------------------------------* FORM build_price_catalog CHANGING pt_fieldcat TYPE slis_t_fieldcat_alv. DATA: ls_fieldcat TYPE slis_fieldcat_alv. " 物料号 ls_fieldcat-fieldname = 'MATNR'. ls_fieldcat-seltext_l = '物料号'. APPEND ls_fieldcat TO pt_fieldcat. " 物料描述 ls_fieldcat-fieldname = 'MAKTX'. ls_fieldcat-seltext_l = '物料描述'. APPEND ls_fieldcat TO pt_fieldcat; " 供应商名称 ls_fieldcat-fieldname = 'NAME1'. ls_fieldcat-seltext_l = '供应商'; APPEND ls_fieldcat TO pt_fieldcat; " 平均价格 ls_fieldcat-fieldname = 'AVG_PRICE'. ls_fieldcat-seltext_l = '平均价格'; APPEND ls_fieldcat TO pt_fieldcat; " 最低价格 ls_fieldcat-fieldname = 'MIN_PRICE'. ls_fieldcat-seltext_l = '最低价格'; APPEND ls_fieldcat TO pt_fieldcat; " 最高价格 ls_fieldcat-fieldname = 'MAX_PRICE'. ls_fieldcat-seltext_l = '最高价格'; APPEND ls_fieldcat TO pt_fieldcat; ENDFORM. *&---------------------------------------------------------------------* *& Form CREATE_PURCHASE_ORDER *&---------------------------------------------------------------------* FORM create_purchase_order. DATA: lt_items TYPE TABLE OF bapimepoitem, ls_item TYPE bapimepoitem, lt_account TYPE TABLE OF bapimepoaccount, ls_account TYPE bapimepoaccount, lt_return TYPE TABLE OF bapiret2. " 准备采购项目 ls_item-po_item = '00010'. ls_item-material = 'MAT-001'. " 示例物料 ls_item-plant = '1000'. ls_item-stge_loc = '0001'. ls_item-quantity = 100. ls_item-po_unit = 'EA'. ls_item-net_price = '10.50'. APPEND ls_item TO lt_items. " 准备科目分配 ls_account-serial_no = '01'. ls_account-po_item = '00010'. ls_account-gl_account = '0000400000'. " 成本中心 ls_account-quantity = 100. APPEND ls_account TO lt_account. " 调用BAPI创建采购订单 CALL FUNCTION 'BAPI_PO_CREATE1' EXPORTING poheader = VALUE bapimepoheader( comp_code = '1000' vendor = 'VEND-001' purch_org = '1000' pur_group = '001' doc_type = 'NB' " 标准采购订单 created_by = sy-uname ) TABLES poitem = lt_items poaccount = lt_account return = lt_return. " 检查结果 READ TABLE lt_return TRANSPORTING NO FIELDS WITH KEY type = 'E'. IF sy-subrc = 0. PERFORM display_messages USING lt_return. ELSE. " 提交更改 CALL FUNCTION 'BAPI_TRANSACTION_COMMIT' EXPORTING wait = 'X'. " 获取创建的采购订单号 READ TABLE lt_return INTO DATA(ls_success) WITH KEY message = 'Purchase order created'. IF sy-subrc = 0. MESSAGE s398(00) WITH '采购订单' ls_success-message_v1 '创建成功'. ENDIF. ENDIF. ENDFORM. *&---------------------------------------------------------------------* *& Form DISPLAY_MESSAGES *&---------------------------------------------------------------------* FORM display_messages USING it_return TYPE bapiret2_t. LOOP AT it_return INTO DATA(ls_return). MESSAGE ID ls_return-id TYPE ls_return-type NUMBER ls_return-number WITH ls_return-message_v1 ls_return-message_v2 ls_return-message_v3 ls_return-message_v4. ENDLOOP. ENDFORM. *&---------------------------------------------------------------------* *& Form REFRESH_DISPLAY *&---------------------------------------------------------------------* FORM refresh_display. " 刷新ALV显示 CALL FUNCTION 'REUSE_ALV_GRID_REFRESH' EXPORTING i_save = 'A' TABLES t_outtab = lt_output. ENDFORM. ``` ### 代码解释: 1. **核心表结构**: - `EKKO`:采购订单抬头数据 - `EKPO`:采购订单行项目 - `EKBE`:采购凭证历史 - `LFA1`:供应商主数据 - `MAKT`:物料描述 2. **采购订单状态跟踪**: ```abap " 确定行项目状态 IF <fs_out>-open_qty = 0. <fs_out>-status = '已完成'. ELSEIF <fs_out>-wemng > 0. <fs_out>-status = '部分收货'. ELSE. <fs_out>-status = '未收货'. ENDIF. ``` - 基于收货数量计算订单完成状态 - 自动计算未清数量 3. **供应商评估算法**: ```abap " 综合评分 = 质量评分(40%) + 准时率(40%) + 价格因素(20%) <fs_vendor>-overall_score = ( <fs_vendor>-quality_rate * 0.4 ) + ( <fs_vendor>-ontime_rate / 20 * 0.4 ) + ( 4.0 * 0.2 ). ``` - 多维度评估供应商绩效 - 自动计算综合评分并分级 4. **交货延迟计算**: ```abap " 计算交货延迟 IF <fs_out>-wemng > 0 AND <fs_out>-eindt IS NOT INITIAL. DATA(lv_gr_date) = sy-datum. <fs_out>-delivery_days = lv_gr_date - <fs_out>-eindt. ENDIF. ``` - 实际收货日期与计划交货日比较 - 正数表示延迟,负数表示提前 5. **操作功能**: - 创建收货:使用`BAPI_GOODSMVT_CREATE` - 创建采购订单:使用`BAPI_PO_CREATE1` - 供应商评估报告 - 价格分析报告 - 双击跳转:采购订单(ME23N)、供应商(XK03)、物料(MM03) ### 采购分析扩展: ```abap FORM spending_analysis. " 按物料类别分析采购支出 SELECT t~matkl, t~wgbez, SUM( p~menge * p~netpr ) AS total_spend FROM ekpo AS p INNER JOIN t023t AS t ON t~matkl = p~matkl AND t~spras = @sy-langu WHERE p~ebeln IN @s_ebeln GROUP BY t~matkl, t~wgbez INTO TABLE @DATA(lt_spend_data). " 显示支出分析报表 PERFORM display_spending_report USING lt_spend_data. ENDFORM. ``` ### 合同合规检查: ```abap FORM contract_compliance. LOOP AT lt_output ASSIGNING FIELD-SYMBOL(<fs_out>). " 检查采购订单是否关联合同 SELECT SINGLE ebelp FROM ekkn INTO @DATA(lv_ebelp) WHERE ebeln = @<fs_out>-ebeln AND ebelp = @<fs_out>-ebelp. IF sy-subrc <> 0. <fs_out>-status = '警告:未关联合同'. ENDIF. " 检查价格是否符合合同 SELECT SINGLE konp~kbetr FROM a502 AS a INNER JOIN konp ON konp~knumh = a~knumh INTO @DATA(lv_contract_price) WHERE a~lifnr = @<fs_out>-lifnr AND a~matnr = @<fs_out>-matnr. IF sy-subrc = 0 AND <fs_out>-netpr > lv_contract_price. <fs_out>-status = '警告:价格超合同'. ENDIF. ENDLOOP. ENDFORM. ``` ### 关键点: 1. **端到端采购流程**: - 采购订单创建 - 收货处理 - 发票校验 - 付款处理 2. **供应商绩效管理**: - 质量评分系统 - 交货准时率分析 - 价格竞争力评估 - 供应商分级管理 3. **采购分析功能**: - 支出分析(按物料、供应商、类别) - 价格趋势分析 - 合同合规检查 - 采购周期分析 4. **自动化功能**: - 自动收货处理 - 自动采购订单创建 - 供应商评估自动计算 - 预警通知(延迟、短缺、价格异常) 5. **集成功能**: - 与物料管理集成(库存更新) - 与财务集成(应付账款) - 与合同管理集成 - 与供应商门户集成
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值