箱ひげ図の作成

注:これは 「ウィジェット付きグラフギャラリー」のサンプルの一部です。

この例では、ファイルから非連続データを読み込み、データを整理し、箱ひげ図の作成に必要な中央値を求める方法についても説明しています。

グラフ内の箱ひげ図を示すスクリーンショット

2 社の株価の偏差を示すために、まず月次データを扱う 2 つの 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);
}

このセクションでは、2社目のデータを読み込むために2つ目のファイルを開きます。

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);

最後に、チャートをビューに追加します。また、chartViewのアンチエイリアシングを有効にします。

createDefaultChartView(chart);

これで、チャートを表示する準備が整いました。

ここでは、readBox メソッドについて詳しく説明します。

まず、ファイルから行を読み込み、#で始まる行はコメント行とみなして除外します。

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

このファイルでは、データは「数値、スペース、数値、またはスペース」という形式で配置されています。このコードスニペットでは、行が単一の数値文字列に分割され、QStringList に格納されます。

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

sortedListには数値が連続した順序で格納されますが、このコードセグメントではその方法について説明します。まず、sortedListをクリアし、strListから数値を読み込んでdouble形式で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`メソッドのコードサンプルを示します。数値の個数が奇数の場合は、中央の数値を選択します。偶数の場合は、中央から2つの数値を取り出し、それらの平均値を算出します。

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;
}

© 2026 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.