Code
import pandas as pd
import numpy as np
import statsmodels.api as sm
import scipy.stats
def rsr(data, weight=None, threshold=None, full_rank=True):
Result = pd.DataFrame()
n, m = data.shape
# 对原始数据编秩
if full_rank:
for i, X in enumerate(data.columns):
Result[f'X{str(i + 1)}:{X}'] = data.iloc[:, i]
Result[f'R{str(i + 1)}:{X}'] = data.iloc[:, i].rank(method="average")
else:
for i, X in enumerate(data.columns):
Result[f'X{str(i + 1)}:{X}'] = data.iloc[:, i]
Result[f'R{str(i + 1)}:{X}'] = 1 + (n - 1) * (data.iloc[:, i].max() - data.iloc[:, i]) / (data.iloc[:, i].max() - data.iloc[:, i].min())
# 计算秩和比
weight = 1 / m if weight is None else np.array(weight) / sum(weight)
Result['RSR'] = (Result.iloc[:, 1::2] * weight).sum(axis=1) / n
Result['RSR_Rank'] = Result['RSR'].rank(ascending=False)
# 绘制 RSR 分布表
RSR = Result['RSR']
RSR_RANK_DICT = dict(zip(RSR.values, RSR.rank().values))
Distribution = pd.DataFrame(index=sorted(RSR.unique()))
Distribution['f'] = RSR.value_counts().sort_index()
Distribution['Σ f'] = Distribution['f'].cumsum()
Distribution[r'\bar{R} f'] = [RSR_RANK_DICT[i] for i in Distribution.index]
Distribution[r'\bar{R}/n*100%'] = Distribution[r'\bar{R} f'] / n #根据累计频数算累计频率
Distribution.iat[-1, -1] = 1 - 1 / (4 * n) #修正最后一项累计频率
Distribution['Probit'] = 5 - stats.norm.isf(Distribution.iloc[:, -1]) #inverse survival function 将累计频率换算为概率单位
# 计算回归方差并进行回归分析
r0 = np.polyfit(Distribution['Probit'], Distribution.index, deg=1) #x,y
model = sm.OLS(Distribution.index, sm.add_constant(Distribution['Probit']))#y,x
result = model.fit()
print(result.summary())
#print(sm.OLS(Distribution.index, sm.add_constant(Distribution['Probit'])).fit().summary())
# 残差检验
z_error, p_error = stats.normaltest(result.resid.values) #tests the null hypothesis that a sample comes from a normal distribution
print("残差分析: ", p_error)
if r0[1] > 0:
print(f"\n回归直线方程为:y = {r0[0]} Probit + {r0[1]}")
else:
print(f"\n回归直线方程为:y = {r0[0]} Probit - {abs(r0[1])}")
# 代入回归方程并分档排序
Result['Probit'] = Result['RSR'].apply(lambda item: Distribution.at[item, 'Probit'])
Result['RSR Regression'] = np.polyval(r0, Result['Probit'])
threshold = np.polyval(r0, [2, 4, 6, 8]) if threshold is None else np.polyval(r0, threshold)
Result['Level'] = pd.cut(Result['RSR Regression'], threshold, labels=range(len(threshold) - 1, 0, -1)) #Probit分组[(2, 4] < (4, 6] < (6, 8]]
return Result, Distribution
def rsrAnalysis(data, file_name=None, **kwargs):
Result, Distribution = rsr(data, **kwargs)
file_name = 'RSR 分析结果报告.xlsx' if file_name is None else file_name + '.xlsx'
Excel_Writer = pd.ExcelWriter(file_name)
Result.to_excel(Excel_Writer, '综合评价结果')
Result.sort_values(by='Level', ascending=False).to_excel(Excel_Writer, '分档排序结果')
Distribution.to_excel(Excel_Writer, 'RSR分布表')
Excel_Writer.save()
return Result, Distribution
### 下面添加文件读取,然后调用rsr子函数就可以了,最后结果会自动输出到excel
#链接里面的是:
# 读取数据
data = pd.DataFrame({'产前检查率': [99.54, 96.52, 99.36, 92.83, 91.71, 95.35, 96.09, 99.27, 94.76, 84.80],
'孕妇死亡率': [60.27, 59.67, 43.91, 58.99, 35.40, 44.71, 49.81, 31.69, 22.91, 81.49],
'围产儿死亡率': [16.15, 20.10, 15.60, 17.04, 15.01, 13.93, 17.43, 13.89, 19.87, 23.63]},
index=list('ABCDEFGHIJ'), columns=['产前检查率', '孕妇死亡率', '围产儿死亡率'])
#因为下面两个是成本型指标,需要反向排序,所以他做了这个变换
data["孕妇死亡率"] = 1 / data["孕妇死亡率"]
data["围产儿死亡率"] = 1 / data["围产儿死亡率"]
# 下面是调用子函数进行秩和比几个步骤的计算
rsr(data)
Notes
Barlett检验:Bartlett检验是方差齐性检验(对两组样本的方差是否相同进行检验)的一种方法,其核心思想是通过求取不同组之间的卡方统计量,然后根据卡方统计量的值来判断组间方差是否相等。该方法极度依赖于数据是正态分布,如果数据非正态分布,则结果偏差很大;而Levene检验对正态分布没有要求
方差分析(ANOVA/F检验):
Reference
RSR(秩和比综合评价法)介绍及python3实现
秩和比法RSR
概率密度函数pdf
python 计算概率密度、累计分布、逆函数
probit与分位函数
scipy.stats与统计学(概率分布)
一元线性回归分析的t检验和f检验