问题描述
我已经看到有人问过这个问题:
令人惊讶的是,我没有找到解决方案(嗯,qcustomplot 有这个功能,但我们不允许在我的公司使用它)。我的任务是将一些科学数据表示为线图,误差线表示数据标准偏差。我尝试了一些技巧来解决它,但结果对于我们的应用程序来说还不够:
- 为数据均值创建一个
QLineSeries
,然后添加N个QLineSeries
来表示误差线(见下例中的approachWithMultipleQLineSeries
,);这显然是一种矫枉过正,我的情节在较慢的机器上出现故障(使用 Windows :/) - 为数据平均值创建一个
QLineSeries
,然后添加QBoxPlotSeries
来表示误差线(参见下面示例中的approachWithQBoxSet
,);这不再出现故障,但误差线与线图不太对应(例如查看接近于零的值),可能是因为 Boxes 是为稍微不同的 X 轴设计的(带有类别,而不是浮点数);
您能否提出一些更好的方法,其中误差条值实际上与 X 值和平均 Y 值相关联(可能是子类 QXYSeries
或类似的,我没有任何好主意如何准确地做到这一点)?
MCVE 如果有人想尝试:
CMakeLists.txt
:
cmake_minimum_required(VERSION 3.11.0)
project(QtChartsErrBars CXX)
set(CMAKE_CXX_STANDARD 17)
find_package(Qt5 COMPONENTS Charts Core Widgets REQUIRED)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_AUTOUIC ON)
add_executable(QtChartsErrBars
main.cpp
Demo.cpp
Demo.hpp
)
target_link_libraries(QtChartsErrBars
Qt5::Charts
Qt5::Core
Qt5::Widgets
)
target_compile_options(QtChartsErrBars PUBLIC -g)
Demo.hpp
:
#pragma once
/**
* /author Pawel Ptasznik
*/
#include <QMainWindow>
#include <QWidget>
#include <memory>
class Demo : public QMainWindow
{
Q_OBJECT
public:
explicit Demo(QWidget* parent = nullptr);
~Demo() override = default;
};
Demo.cpp
:
#include "Demo.hpp"
#include <QDebug>
#include <QVector>
#include <QtCharts/QChartView>
#include <QtCharts/QLineSeries>
#include <QtCharts/QAreaSeries>
#include <QtCharts/QLegendMarker>
#include <QtCharts/QBoxSet>
#include <QtCharts/QBoxPlotSeries>
#include <algorithm>
#include <random>
static std::random_device rd{};
static std::mt19937 gen{ rd() };
// for each input generate n samples using normal distribution with expected value == x^2
QVector<QVector<double> > generateData(QVector<double> in,int nSamples)
{
QVector<QVector<double> > results(in.size());
for (int i = 0; i < in.size(); i++)
{
results[i].resize(nSamples);
std::normal_distribution<double> d{ in[i]*in[i],1 };
for (auto& sample : results[i])
{
sample = d(gen);
}
}
return results;
}
/************* HERE the solutions I have tried and I do not like ************/
namespace
{
class VerticalLine : public QtCharts::QLineSeries
{
public:
VerticalLine(double x,double yMin,double yMax)
{
replace(QVector<QPointF>{ { x,yMin },{ x,yMax } });
}
};
}
void approachWithMultipleQLineSeries(QtCharts::QChart *chart,const QVector<double>& x,const QVector<double>& yMean,const QVector<double>& yStdDev)
{
for (int i = 0; i < x.size(); i++)
{
auto series = new VerticalLine(x[i],yMean[i]-yStdDev[i],yMean[i]+yStdDev[i]);
chart->addSeries(series);
chart->legend()->markers(series)[0]->setVisible(false);
}
}
void approachWithQBoxSet(QtCharts::QChart *chart,const QVector<double>& yStdDev)
{
QList<QtCharts::QBoxSet*> boxList;
for (int i = 0; i < x.size(); i++)
{
boxList.push_back(new QtCharts::QBoxSet(yMean[i]-yStdDev[i],yMean[i]-yStdDev[i]/2,yMean[i],yMean[i]+yStdDev[i]/2,yMean[i]+yStdDev[i]));
}
auto series = new QtCharts::QBoxPlotSeries();
series->append(boxList);
series->setBoxWidth(0.1);
series->attachAxis(chart->axes(Qt::Horizontal)[0]);
chart->addSeries(series);
chart->legend()->markers(series)[0]->setVisible(false);
}
/************* HERE quick example that plots quadratic function over 200 points ************/
Demo::Demo(QWidget* parent): QMainWindow(parent)
{
QVector<double> xAxisValues(200); // lets have 200 points between -10 and 10
std::generate(xAxisValues.begin(),xAxisValues.end(),[x = -10.0]() mutable { return x += 0.1; });
QVector<double> yAxisMeanValues(xAxisValues.size());
QVector<double> yAxisStdDev(xAxisValues.size());
// lets generate 50 samples for each x value
auto data = generateData(xAxisValues,50);
// calculating mean values
std::transform(data.begin(),data.end(),yAxisMeanValues.begin(),[](const auto& samples)
{
double sum = 0.0;
for (auto sample : samples) sum += sample;
return sum / samples.size();
});
// calculating standard deviation
std::transform(data.begin(),yAxisStdDev.begin(),[](const auto& samples,auto mean)
{
double var = 0.0;
for (auto sample : samples) var += (sample - mean)*(sample - mean);
var /= samples.size();
return std::sqrt(var);
});
QtCharts::QLineSeries *series = new QtCharts::QLineSeries();
for (int i = 0; i < xAxisValues.size(); i++)
{
series->append(xAxisValues[i],yAxisMeanValues[i]);
}
QtCharts::QChart *chart = new QtCharts::QChart();
chart->addSeries(series);
// OK,now how do I add the error bars basing on x,yMean,yStdDev?
// approachWithMultipleQLineSeries(chart,xAxisValues,yAxisMeanValues,yAxisStdDev);
chart->createDefaultAxes();
approachWithQBoxSet(chart,yAxisStdDev);
chart->setTitle("Demo chart");
QtCharts::QChartView *chartView = new QtCharts::QChartView(chart);
chartView->setRenderHint(QPainter::Antialiasing);
setCentralWidget(chartView);
resize(400,300);
}
main.cpp
:
#include "Demo.hpp"
#include <QApplication>
int main(int argc,char** argv)
{
QApplication a(argc,argv);
Demo mainWindow;
mainWindow.show();
return a.exec();
}
解决方法
暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!
如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。
小编邮箱:dio#foxmail.com (将#修改为@)