|
@@ -0,0 +1,454 @@
|
|
|
+#include "MindEvaluation.h"
|
|
|
+#include "CMind.h"
|
|
|
+#include "dbService/NodeMatrixService.h"
|
|
|
+#include "dbService/UserConfigService.h"
|
|
|
+#include "algorithm/HierarchicalAnalysis.h"
|
|
|
+#include "algorithm/EntropyWeights.h"
|
|
|
+#include "algorithm/PCA.h"
|
|
|
+
|
|
|
+#include <QMessageBox>
|
|
|
+#include <QDebug>
|
|
|
+
|
|
|
+MindEvaluation::MindEvaluation(CMind *mind, int expertId, const QString &tableType, int mindMatrix, QObject *parent)
|
|
|
+ : QObject(parent),
|
|
|
+ m_mind(mind),
|
|
|
+ m_expertId(expertId),
|
|
|
+ m_tableType(tableType),
|
|
|
+ m_mindMatrix(mindMatrix),
|
|
|
+ m_isAggregationOp(false)
|
|
|
+{
|
|
|
+ init();
|
|
|
+}
|
|
|
+
|
|
|
+MindEvaluation::MindEvaluation(CMind *mind, MindEvaluation::AggregationType atype, const QString &tableType,
|
|
|
+ int mindMatrix, QObject *parent)
|
|
|
+ : QObject(parent),
|
|
|
+ m_mind(mind),
|
|
|
+ m_tableType(tableType),
|
|
|
+ m_mindMatrix(mindMatrix),
|
|
|
+ m_aggregationType(atype),
|
|
|
+ m_isAggregationOp(true)
|
|
|
+{
|
|
|
+}
|
|
|
+
|
|
|
+MindEvaluation::~MindEvaluation() { }
|
|
|
+
|
|
|
+void MindEvaluation::updateSeqNodes()
|
|
|
+{
|
|
|
+ m_mind->getSeqNodes(m_seqNodes);
|
|
|
+}
|
|
|
+
|
|
|
+static QList<QPair<QString, QString>> getMatrixString(const QStringList &in)
|
|
|
+{
|
|
|
+ QList<QPair<QString, QString>> res;
|
|
|
+ const int n = in.size();
|
|
|
+ for (int i = 0; i < n; ++i) {
|
|
|
+ for (int j = 0; j < n; ++j) {
|
|
|
+ res << QPair<QString, QString>(in.at(i), in.at(j));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return res;
|
|
|
+}
|
|
|
+
|
|
|
+void MindEvaluation::computeWeights()
|
|
|
+{
|
|
|
+ if (!m_isAggregationOp) {
|
|
|
+ computeSingleWeights();
|
|
|
+ } else {
|
|
|
+ if (m_aggregationType == NoMerge) {
|
|
|
+ computeNoMergeWeights();
|
|
|
+ } else {
|
|
|
+ computeMergeWeights();
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void MindEvaluation::init()
|
|
|
+{
|
|
|
+ updateSeqNodes();
|
|
|
+}
|
|
|
+
|
|
|
+void MindEvaluation::computeSingleWeights()
|
|
|
+{
|
|
|
+ QList<NodeMatrixInfo *> nmInfos;
|
|
|
+ if (NodeMatrixService().QueryNodeMatrixListByExpertIdAndEngineerId2(&nmInfos, m_expertId, m_mind->root().projectId,
|
|
|
+ m_tableType)) {
|
|
|
+ QMessageBox::warning(nullptr, "警告", "数据库访问失败");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ for (int sn = 0; sn < m_seqNodes.count(); sn++) {
|
|
|
+ qDebug() << "第" << sn << "层";
|
|
|
+ for (auto s : m_seqNodes.at(sn)) {
|
|
|
+ qDebug() << s.name << s.childs;
|
|
|
+
|
|
|
+ if (m_mindMatrix == 0) { // 层次分析法
|
|
|
+ auto matrixStr = getMatrixString(s.childs);
|
|
|
+ QVector<qreal> nxnValus(matrixStr.size(), 0.0001); // n x n矩阵
|
|
|
+
|
|
|
+ for (const auto &nmInfo : nmInfos) {
|
|
|
+ if (nmInfo->mindId == m_mindMatrix) {
|
|
|
+ int loc = matrixStr.indexOf(QPair<QString, QString>(nmInfo->abscissa, nmInfo->ordinate));
|
|
|
+ if (loc >= 0) {
|
|
|
+ QStringList nodeValue = nmInfo->nodeValue.split("/");
|
|
|
+ if (nodeValue.size() == 1) {
|
|
|
+ nxnValus[loc] = nodeValue[0].toDouble();
|
|
|
+ } else {
|
|
|
+ nxnValus[loc] = nodeValue[0].toDouble() / nodeValue[1].toDouble();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ QScopedPointer<HierarchicalAnalysis> ha(new HierarchicalAnalysis(s.childs, nxnValus));
|
|
|
+ QVector<qreal> weights = ha->getWeights();
|
|
|
+
|
|
|
+ for (int c = 0; c < s.childs.size(); ++c) {
|
|
|
+ mindNodeWeights << MindNodeItem { s.name, s.childs.at(c), weights.at(c), 0 };
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+
|
|
|
+ // 熵权法
|
|
|
+ if (1) {
|
|
|
+ QMap<QString /*指标*/, QMap<QString /*uuid*/, double>> seqMap;
|
|
|
+ for (const auto &nmInfo : nmInfos) {
|
|
|
+ if (nmInfo->mindId == m_mindMatrix) {
|
|
|
+ int loc = s.childs.indexOf(nmInfo->abscissa); // 指标索引
|
|
|
+ if (loc >= 0) {
|
|
|
+ QStringList nodeValue = nmInfo->nodeValue.split("/");
|
|
|
+ double v;
|
|
|
+ if (nodeValue.size() == 1) {
|
|
|
+ v = nodeValue[0].toDouble();
|
|
|
+ } else {
|
|
|
+ v = nodeValue[0].toDouble() / nodeValue[1].toDouble();
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!seqMap.contains(nmInfo->abscissa)) {
|
|
|
+ seqMap.insert(nmInfo->abscissa, QMap<QString /*uuid*/, double>());
|
|
|
+ }
|
|
|
+ seqMap[nmInfo->abscissa].insert(nmInfo->strUuid, v);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ EntropyMat eMat; // 一个指标一行数据
|
|
|
+ for (const auto &c : s.childs) {
|
|
|
+ if (seqMap.contains(c)) {
|
|
|
+ QVector<double> indexVecs;
|
|
|
+
|
|
|
+ QMapIterator<QString, double> iter(seqMap[c]);
|
|
|
+ while (iter.hasNext()) {
|
|
|
+ iter.next();
|
|
|
+ indexVecs << iter.value();
|
|
|
+ }
|
|
|
+
|
|
|
+ eMat << indexVecs;
|
|
|
+ } else {
|
|
|
+ QMessageBox::warning(nullptr, "警告", QString("指标%1数据缺失").arg(c));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ QScopedPointer<EntropyWeights> ew(new EntropyWeights(eMat));
|
|
|
+ QVector<qreal> weights, score;
|
|
|
+ ew->compute(weights, score);
|
|
|
+
|
|
|
+ for (int c = 0; c < s.childs.size(); ++c) {
|
|
|
+ mindNodeWeights << MindNodeItem { s.name, s.childs.at(c), weights.at(c), score.at(c) };
|
|
|
+ }
|
|
|
+ } else { // pca
|
|
|
+ QMap<QString /*uuid*/, QMap<QString /*指标*/, double>> seqMap;
|
|
|
+ for (const auto &nmInfo : nmInfos) {
|
|
|
+ if (nmInfo->mindId == m_mindMatrix) {
|
|
|
+ int loc = s.childs.indexOf(nmInfo->abscissa); // 指标索引
|
|
|
+ if (loc >= 0) {
|
|
|
+ QStringList nodeValue = nmInfo->nodeValue.split("/");
|
|
|
+ double v;
|
|
|
+ if (nodeValue.size() == 1) {
|
|
|
+ v = nodeValue[0].toDouble();
|
|
|
+ } else {
|
|
|
+ v = nodeValue[0].toDouble() / nodeValue[1].toDouble();
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!seqMap.contains(nmInfo->strUuid)) {
|
|
|
+ seqMap.insert(nmInfo->strUuid, QMap<QString /*指标*/, double>());
|
|
|
+ }
|
|
|
+ seqMap[nmInfo->strUuid].insert(nmInfo->abscissa, v);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ QVector<QVector<double>> pMat; // 一个指标一列数据
|
|
|
+
|
|
|
+ QMapIterator<QString, QMap<QString /*指标*/, double>> iter(seqMap);
|
|
|
+
|
|
|
+ while (iter.hasNext()) {
|
|
|
+ iter.next();
|
|
|
+ QMap<QString /*指标*/, double> indexMap = iter.value();
|
|
|
+
|
|
|
+ QVector<double> tmp;
|
|
|
+ QMapIterator<QString /*指标*/, double> iterIndex(indexMap);
|
|
|
+ while (iterIndex.hasNext()) {
|
|
|
+ iterIndex.next();
|
|
|
+ tmp << iterIndex.value();
|
|
|
+ }
|
|
|
+ pMat << tmp;
|
|
|
+ }
|
|
|
+
|
|
|
+ QScopedPointer<PCA> pca(new PCA(pMat));
|
|
|
+ pca->compute();
|
|
|
+ QVector<qreal> weights = pca->weights();
|
|
|
+ QVector<qreal> score = pca->scores();
|
|
|
+ for (int c = 0; c < s.childs.size(); ++c) {
|
|
|
+ mindNodeWeights << MindNodeItem { s.name, s.childs.at(c), weights.at(c), score.at(c) };
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ qDeleteAll(nmInfos);
|
|
|
+}
|
|
|
+
|
|
|
+/// 数据集结,先计算每个专家的权重,在求均值
|
|
|
+void MindEvaluation::computeNoMergeWeights()
|
|
|
+{
|
|
|
+ int enjId = m_mind->root().projectId;
|
|
|
+ QList<UserConfig *> users;
|
|
|
+
|
|
|
+ if (UserConfigService().QueryUserConfigListInfoByEngineerId(&users, enjId)) {
|
|
|
+ QMessageBox::warning(nullptr, "警告", "数据库访问失败");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ bool QueryUserConfigListInfoByEngineerId(QList<UserConfig *> * userCfgList, int engineerId);
|
|
|
+
|
|
|
+ QScopedPointer<QList<QList<MindNodeItem>>> allItems(new QList<QList<MindNodeItem>>());
|
|
|
+
|
|
|
+ for (const auto &user : users) {
|
|
|
+ QList<NodeMatrixInfo *> nmInfos;
|
|
|
+ if (NodeMatrixService().QueryNodeMatrixListByExpertIdAndEngineerId2(&nmInfos, user->id, enjId, m_tableType)) {
|
|
|
+ qDeleteAll(users);
|
|
|
+ QMessageBox::warning(nullptr, "警告", "数据库访问失败");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ QList<MindNodeItem> mnItem;
|
|
|
+
|
|
|
+ computeOneEntry(nmInfos, mnItem);
|
|
|
+ allItems->append(mnItem);
|
|
|
+
|
|
|
+ qDeleteAll(nmInfos);
|
|
|
+ }
|
|
|
+ qDeleteAll(users);
|
|
|
+
|
|
|
+ for (int i = 0; i < allItems->size(); ++i) {
|
|
|
+ if (i == 0) {
|
|
|
+ mindNodeWeights = allItems->at(0);
|
|
|
+ } else {
|
|
|
+ for (const auto &item : allItems->at(i)) {
|
|
|
+ int id = mindNodeWeights.indexOf(item);
|
|
|
+ mindNodeWeights[id].value += item.value;
|
|
|
+ mindNodeWeights[id].score += item.score;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ for (int id = 0; id < mindNodeWeights.size(); id++) {
|
|
|
+ mindNodeWeights[id].value /= allItems->size();
|
|
|
+ mindNodeWeights[id].score /= allItems->size();
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/// 数矩阵集结,先计算出一个综合专家,在计算权重
|
|
|
+void MindEvaluation::computeMergeWeights()
|
|
|
+{
|
|
|
+ int enjId = m_mind->root().projectId;
|
|
|
+ QList<UserConfig *> users;
|
|
|
+
|
|
|
+ if (UserConfigService().QueryUserConfigListInfoByEngineerId(&users, enjId)) {
|
|
|
+ QMessageBox::warning(nullptr, "警告", "数据库访问失败");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ bool QueryUserConfigListInfoByEngineerId(QList<UserConfig *> * userCfgList, int engineerId);
|
|
|
+
|
|
|
+ QList<NodeMatrixInfo *> megerNMInfo;
|
|
|
+ QVector<double> nodeValues;
|
|
|
+
|
|
|
+ for (const auto &user : users) {
|
|
|
+ QList<NodeMatrixInfo *> nmInfos;
|
|
|
+ if (NodeMatrixService().QueryNodeMatrixListByExpertIdAndEngineerId2(&nmInfos, user->id, enjId, m_tableType)) {
|
|
|
+ qDeleteAll(users);
|
|
|
+ QMessageBox::warning(nullptr, "警告", "数据库访问失败");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (megerNMInfo.size() == 0) {
|
|
|
+ for (int nm = 0; nm < nmInfos.size(); ++nm) {
|
|
|
+ NodeMatrixInfo *info = nmInfos.at(nm);
|
|
|
+ megerNMInfo.append(new NodeMatrixInfo());
|
|
|
+ *megerNMInfo[0] = *info;
|
|
|
+ double v;
|
|
|
+ QStringList nds = nmInfos.at(nm)->nodeValue.split("/");
|
|
|
+ if (nds.size() == 1) {
|
|
|
+ v = nds.at(0).toDouble();
|
|
|
+ } else {
|
|
|
+ v = nds.at(0).toDouble() / nds.at(1).toDouble();
|
|
|
+ }
|
|
|
+ nodeValues << v;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ for (int nm = 0; nm < nmInfos.size(); ++nm) {
|
|
|
+ double v;
|
|
|
+ QStringList nds = nmInfos.at(nm)->nodeValue.split("/");
|
|
|
+ if (nds.size() == 1) {
|
|
|
+ v = nds.at(0).toDouble();
|
|
|
+ } else {
|
|
|
+ v = nds.at(0).toDouble() / nds.at(1).toDouble();
|
|
|
+ }
|
|
|
+ nodeValues[nm] += v;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ qDeleteAll(nmInfos);
|
|
|
+ }
|
|
|
+
|
|
|
+ for (int nm = 0; nm < megerNMInfo.size(); ++nm) {
|
|
|
+ megerNMInfo[nm]->nodeValue = QString::number(nodeValues[nm] / users.size());
|
|
|
+ }
|
|
|
+
|
|
|
+ computeOneEntry(megerNMInfo, mindNodeWeights);
|
|
|
+
|
|
|
+ qDeleteAll(megerNMInfo);
|
|
|
+ qDeleteAll(users);
|
|
|
+}
|
|
|
+
|
|
|
+void MindEvaluation::computeOneEntry(const QList<NodeMatrixInfo *> &nmInfos, QList<MindNodeItem> &mindNodeItem)
|
|
|
+{
|
|
|
+ for (int sn = 0; sn < m_seqNodes.count(); sn++) {
|
|
|
+ qDebug() << "第" << sn << "层";
|
|
|
+ for (auto s : m_seqNodes.at(sn)) {
|
|
|
+ qDebug() << s.name << s.childs;
|
|
|
+
|
|
|
+ if (m_mindMatrix == 0) { // 层次分析法
|
|
|
+ auto matrixStr = getMatrixString(s.childs);
|
|
|
+ QVector<qreal> nxnValus(matrixStr.size(), 0.0001); // n x n矩阵
|
|
|
+
|
|
|
+ for (const auto &nmInfo : nmInfos) {
|
|
|
+ if (nmInfo->mindId == m_mindMatrix) {
|
|
|
+ int loc = matrixStr.indexOf(QPair<QString, QString>(nmInfo->abscissa, nmInfo->ordinate));
|
|
|
+ if (loc >= 0) {
|
|
|
+ QStringList nodeValue = nmInfo->nodeValue.split("/");
|
|
|
+ if (nodeValue.size() == 1) {
|
|
|
+ nxnValus[loc] = nodeValue[0].toDouble();
|
|
|
+ } else {
|
|
|
+ nxnValus[loc] = nodeValue[0].toDouble() / nodeValue[1].toDouble();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ QScopedPointer<HierarchicalAnalysis> ha(new HierarchicalAnalysis(s.childs, nxnValus));
|
|
|
+ QVector<qreal> weights = ha->getWeights();
|
|
|
+
|
|
|
+ for (int c = 0; c < s.childs.size(); ++c) {
|
|
|
+ mindNodeItem << MindNodeItem { s.name, s.childs.at(c), weights.at(c), 0 };
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+
|
|
|
+ // 熵权法
|
|
|
+ if (1) {
|
|
|
+ QMap<QString /*指标*/, QMap<QString /*uuid*/, double>> seqMap;
|
|
|
+ for (const auto &nmInfo : nmInfos) {
|
|
|
+ if (nmInfo->mindId == m_mindMatrix) {
|
|
|
+ int loc = s.childs.indexOf(nmInfo->abscissa); // 指标索引
|
|
|
+ if (loc >= 0) {
|
|
|
+ QStringList nodeValue = nmInfo->nodeValue.split("/");
|
|
|
+ double v;
|
|
|
+ if (nodeValue.size() == 1) {
|
|
|
+ v = nodeValue[0].toDouble();
|
|
|
+ } else {
|
|
|
+ v = nodeValue[0].toDouble() / nodeValue[1].toDouble();
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!seqMap.contains(nmInfo->abscissa)) {
|
|
|
+ seqMap.insert(nmInfo->abscissa, QMap<QString /*uuid*/, double>());
|
|
|
+ }
|
|
|
+ seqMap[nmInfo->abscissa].insert(nmInfo->strUuid, v);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ EntropyMat eMat; // 一个指标一行数据
|
|
|
+ for (const auto &c : s.childs) {
|
|
|
+ if (seqMap.contains(c)) {
|
|
|
+ QVector<double> indexVecs;
|
|
|
+
|
|
|
+ QMapIterator<QString, double> iter(seqMap[c]);
|
|
|
+ while (iter.hasNext()) {
|
|
|
+ iter.next();
|
|
|
+ indexVecs << iter.value();
|
|
|
+ }
|
|
|
+
|
|
|
+ eMat << indexVecs;
|
|
|
+ } else {
|
|
|
+ QMessageBox::warning(nullptr, "警告", QString("指标%1数据缺失").arg(c));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ QScopedPointer<EntropyWeights> ew(new EntropyWeights(eMat));
|
|
|
+ QVector<qreal> weights, score;
|
|
|
+ ew->compute(weights, score);
|
|
|
+
|
|
|
+ for (int c = 0; c < s.childs.size(); ++c) {
|
|
|
+ mindNodeItem << MindNodeItem { s.name, s.childs.at(c), weights.at(c), score.at(c) };
|
|
|
+ }
|
|
|
+ } else { // pca
|
|
|
+ QMap<QString /*uuid*/, QMap<QString /*指标*/, double>> seqMap;
|
|
|
+ for (const auto &nmInfo : nmInfos) {
|
|
|
+ if (nmInfo->mindId == m_mindMatrix) {
|
|
|
+ int loc = s.childs.indexOf(nmInfo->abscissa); // 指标索引
|
|
|
+ if (loc >= 0) {
|
|
|
+ QStringList nodeValue = nmInfo->nodeValue.split("/");
|
|
|
+ double v;
|
|
|
+ if (nodeValue.size() == 1) {
|
|
|
+ v = nodeValue[0].toDouble();
|
|
|
+ } else {
|
|
|
+ v = nodeValue[0].toDouble() / nodeValue[1].toDouble();
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!seqMap.contains(nmInfo->strUuid)) {
|
|
|
+ seqMap.insert(nmInfo->strUuid, QMap<QString /*指标*/, double>());
|
|
|
+ }
|
|
|
+ seqMap[nmInfo->strUuid].insert(nmInfo->abscissa, v);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ QVector<QVector<double>> pMat; // 一个指标一列数据
|
|
|
+
|
|
|
+ QMapIterator<QString, QMap<QString /*指标*/, double>> iter(seqMap);
|
|
|
+
|
|
|
+ while (iter.hasNext()) {
|
|
|
+ iter.next();
|
|
|
+ QMap<QString /*指标*/, double> indexMap = iter.value();
|
|
|
+
|
|
|
+ QVector<double> tmp;
|
|
|
+ QMapIterator<QString /*指标*/, double> iterIndex(indexMap);
|
|
|
+ while (iterIndex.hasNext()) {
|
|
|
+ iterIndex.next();
|
|
|
+ tmp << iterIndex.value();
|
|
|
+ }
|
|
|
+ pMat << tmp;
|
|
|
+ }
|
|
|
+
|
|
|
+ QScopedPointer<PCA> pca(new PCA(pMat));
|
|
|
+ pca->compute();
|
|
|
+ QVector<qreal> weights = pca->weights();
|
|
|
+ QVector<qreal> score = pca->scores();
|
|
|
+ for (int c = 0; c < s.childs.size(); ++c) {
|
|
|
+ mindNodeItem << MindNodeItem { s.name, s.childs.at(c), weights.at(c), score.at(c) };
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|