创建箱型图

注意:这是带有小部件的图表库示例的一部分。

该示例还展示了如何从文件中读取非连续数据,整理数据,并找到箱型图绘制所必需的中位数。

为了展示两家公司的份额差异,我们首先创建两个QBoxPlotSeries来处理月份数据。

auto acmeSeries = new QBoxPlotSeries;
acmeSeries->setName("Acme Ltd");

auto boxWhiskSeries = new QBoxPlotSeries;
boxWhiskSeries->setName("BoxWhisk Inc");

QFile类用于打开一个文本文件,其中存储非连续数据。BoxDataReader是一个辅助类,用于读取文本文件并从数据中找到极端值和平均值。BoxDataReader将在后续部分进行详细解释。readBox方法读取值并将其设置为QBoxSet项,该方法将其返回给调用者。返回的QBoxSet项被添加到系列中。

QFile acmeData(":boxplot_a");
const QString errorTemplate = QStringLiteral("Failed to load '%1' file.");
if (!acmeData.open(QIODevice::ReadOnly | QIODevice::Text)) {
    m_loadError = errorTemplate.arg(acmeData.fileName());
    return false;
}

BoxPlotDataReader dataReader(&acmeData);
while (!dataReader.atEnd()) {
    QBoxSet *set = dataReader.readBox();
    if (set)
        acmeSeries->append(set);
}

在本节中,另一个文件被打开以读取第二家公司的数据。

QFile boxwhiskData(":boxplot_b");
if (!boxwhiskData.open(QIODevice::ReadOnly | QIODevice::Text)) {
    m_loadError = errorTemplate.arg(acmeData.fileName());
    return false;
}

dataReader.readFile(&boxwhiskData);
while (!dataReader.atEnd()) {
    QBoxSet *set = dataReader.readBox();
    if (set)
        boxWhiskSeries->append(set);
}

在本代码片段中,创建了一个新的QChart实例,并将之前创建的系列添加到其中。还定义了标题,并将动画设置为SeriesAnimation。

auto chart = new QChart;
chart->addSeries(acmeSeries);
chart->addSeries(boxWhiskSeries);
chart->setTitle("Acme Ltd. and BoxWhisk Inc. share deviation in 2012");
chart->setAnimationOptions(QChart::SeriesAnimations);

在此,我们请求图表为我们创建默认的轴。我们还通过查询图表中的轴指针并设置该轴的最小值和最大值来设置垂直轴的范围。

chart->createDefaultAxes();
chart->axes(Qt::Vertical).first()->setMin(15.0);
chart->axes(Qt::Horizontal).first()->setMax(34.0);

在本节中,我们将图例设置为可见,并将它们放置在图表的底部。

chart->legend()->setVisible(true);
chart->legend()->setAlignment(Qt::AlignBottom);

最后,我们将图表添加到视图中。我们还为图表视图启用了抗锯齿。

createDefaultChartView(chart);

图表已准备好显示。

下面将详细介绍readBox方法。

首先,从文件中读取一行,并以#开头的行被拒绝,因为它们被视为注释行。

QString line = m_textStream.readLine();
if (line.startsWith("#"))
    return nullptr;

在此文件中,数据按数字、空格、数字或空格的顺序排列。在此代码片段中,行被拆分为单个数字字符串,并存储在QStringList中。

QStringList strList = line.split(QLatin1Char(' '), Qt::SkipEmptyParts);

sortedList将按连续顺序存储数字,在此代码段中显示了如何执行此操作。首先,sortedList被清空,并从strList中读取数字并以双精度格式存储到sortedList中。qSort方法将sortedList按照从最小到最大的顺序排列。

m_sortedList.clear();
for (int i = 1; i < strList.count(); i++)
    m_sortedList.append(strList.at(i).toDouble());

std::sort(m_sortedList.begin(), m_sortedList.end());

以下是选择连续数据中的极值和中位数的代码示例。首先创建一个新的QBoxSet。选择下限和上限极值很简单;它们是sortedList中的第一个和最后一个元素。对于中位数,我们使用辅助方法findMedian,这在后面将进行解释。对于上半部分的中位数,如果数字的数量是偶数或奇数,则需要调整起始数字。下半部分的结束数字自然来自整数舍入。

auto box = new QBoxSet(strList.first());
box->setValue(QBoxSet::LowerExtreme, m_sortedList.first());
box->setValue(QBoxSet::UpperExtreme, m_sortedList.last());
box->setValue(QBoxSet::Median, findMedian(0, count));
box->setValue(QBoxSet::LowerQuartile, findMedian(0, count / 2));
box->setValue(QBoxSet::UpperQuartile, findMedian(count / 2 + (count % 2), count));

以下是为方法findMedian提供的代码示例。如果数字数量为奇数,则选择中间的数字。如果数字数量为偶数,则选择两个中间数字并计算平均值。

int count = end - begin;
if (count % 2) {
    return m_sortedList.at(count / 2 + begin);
} else {
    qreal right = m_sortedList.at(count / 2 + begin);
    qreal left = m_sortedList.at(count / 2 - 1 + begin);
    return (right + left) / 2.0;
}

© 2024 Qt公司有限公司。本文件中的文档贡献权属于各自的拥有者。本文件提供的文档在自由软件基金会发布的GNU自由文档许可证版本1.3条款下获得授权。Qt及其相关标志是芬兰及全球其他地区的The Qt Company Ltd.的商标。商标。所有其他商标均为其各自拥有者的财产。