상자형 및 수염형 차트 만들기

참고: 이 예는 위젯이 있는 차트 갤러리 예제의 일부입니다.

이 예에서는 파일에서 비연속 데이터를 읽고, 정렬하고, 박스형 및 수염형 차트에 필요한 중앙값을 찾는 방법도 보여 줍니다.

두 회사의 점유율 편차를 보여주기 위해 먼저 월별 데이터를 처리하기 위해 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를 지우고 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 을 새로 만듭니다. 최하위와 최상위는 정렬된 목록의 첫 번째와 마지막 항목이므로 선택이 간단합니다. 중앙값의 경우 나중에 설명하는 헬퍼 메서드 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;
}

© 2025 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.