diff --git a/analyzer/CMakeLists.txt b/analyzer/CMakeLists.txt
index b1643d5ca8abb8c867854c5b04fad1b2fc4b9670..a1cc62e6026a5132bc2536770f8f98227219077d 100644
--- a/analyzer/CMakeLists.txt
+++ b/analyzer/CMakeLists.txt
@@ -6,6 +6,7 @@ set(analyzer_srcs
     datamodel.cpp
     main.cpp
     mainwindow.cpp
+    numericaggregationmodel.cpp
     product.cpp
     productmodel.cpp
     productschemaentry.cpp
diff --git a/analyzer/categoryaggregationmodel.cpp b/analyzer/categoryaggregationmodel.cpp
index 02df904184605b378c0474225243019bc7fb5f01..31437c7778d192d0f07c07e43db5d921048a1a7c 100644
--- a/analyzer/categoryaggregationmodel.cpp
+++ b/analyzer/categoryaggregationmodel.cpp
@@ -69,14 +69,15 @@ QVariant CategoryAggregationModel::data(const QModelIndex& index, int role) cons
     if (!index.isValid() || !m_sourceModel)
         return {};
 
+    if (role == TimeAggregationModel::MaximumValueRole)
+        return m_maxValue;
+
     if (index.column() == 0) {
         const auto srcIdx = m_sourceModel->index(index.row(), 0);
         return m_sourceModel->data(srcIdx, role);
     }
     if (role == Qt::DisplayRole) {
         return m_data[index.row() * m_categories.size() + index.column() - 1];
-    } else if (role == TimeAggregationModel::MaximumValueRole) {
-        return m_maxValue;
     }
 
     return {};
diff --git a/analyzer/mainwindow.cpp b/analyzer/mainwindow.cpp
index 1a846dcdf4a77870e3b858086cc4fef05033829f..197bedca9916d004c2e314e88502c7333808074b 100644
--- a/analyzer/mainwindow.cpp
+++ b/analyzer/mainwindow.cpp
@@ -24,6 +24,7 @@
 #include "chart.h"
 #include "connectdialog.h"
 #include "datamodel.h"
+#include "numericaggregationmodel.h"
 #include "productmodel.h"
 #include "restclient.h"
 #include "serverinfo.h"
@@ -281,6 +282,16 @@ void MainWindow::productSelected()
                 ui->chartType->addItem(schemaEntry.name(), QVariant::fromValue(model));
                 break;
             }
+            case ProductSchemaEntry::IntegerType:
+            {
+                auto model = new NumericAggregationModel(this);
+                model->setSourceModel(m_timeAggregationModel);
+                model->setAggregationValue(schemaEntry.name());
+                m_aggregationModels.push_back(model);
+                m_aggregatedDataModel->addSourceModel(model, schemaEntry.name());
+                ui->chartType->addItem(schemaEntry.name(), QVariant::fromValue(model));
+                break;
+            }
         }
     }
 }
diff --git a/analyzer/numericaggregationmodel.cpp b/analyzer/numericaggregationmodel.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..b7debc791239464679125058b131fb4d4e7788af
--- /dev/null
+++ b/analyzer/numericaggregationmodel.cpp
@@ -0,0 +1,145 @@
+/*
+    Copyright (C) 2016 Volker Krause <vkrause@kde.org>
+
+    This program is free software; you can redistribute it and/or modify it
+    under the terms of the GNU Library General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or (at your
+    option) any later version.
+
+    This program is distributed in the hope that it will be useful, but WITHOUT
+    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Library General Public
+    License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "numericaggregationmodel.h"
+#include "sample.h"
+#include "timeaggregationmodel.h"
+
+using namespace UserFeedback::Analyzer;
+
+NumericAggregationModel::NumericAggregationModel(QObject *parent) :
+    QAbstractTableModel(parent)
+{
+}
+
+NumericAggregationModel::~NumericAggregationModel() = default;
+
+void NumericAggregationModel::setSourceModel(QAbstractItemModel* model)
+{
+    Q_ASSERT(model);
+    m_sourceModel = model;
+    connect(model, &QAbstractItemModel::modelReset, this, &NumericAggregationModel::recompute);
+    recompute();
+}
+
+void NumericAggregationModel::setAggregationValue(const QString& aggrValue)
+{
+    m_aggrValue = aggrValue;
+    recompute();
+}
+
+int NumericAggregationModel::columnCount(const QModelIndex& parent) const
+{
+    Q_UNUSED(parent);
+    return 6;
+}
+
+int NumericAggregationModel::rowCount(const QModelIndex& parent) const
+{
+    if (parent.isValid() || !m_sourceModel)
+        return 0;
+    return m_sourceModel->rowCount();
+}
+
+QVariant NumericAggregationModel::data(const QModelIndex& index, int role) const
+{
+    if (!index.isValid() || !m_sourceModel)
+        return {};
+
+    if (role == TimeAggregationModel::MaximumValueRole)
+        return m_maxValue;
+
+    if (index.column() == 0) {
+        const auto srcIdx = m_sourceModel->index(index.row(), 0);
+        return m_sourceModel->data(srcIdx, role);
+    }
+    if (role == Qt::DisplayRole) {
+        const auto d = m_data.at(index.row());
+        switch (index.column()) {
+            case 1: return d.lowerExtreme;
+            case 2: return d.lowerQuartile;
+            case 3: return d.median;
+            case 4: return d.upperQuartile;
+            case 5: return d.upperExtreme;
+        }
+    }
+
+    return {};
+}
+
+QVariant NumericAggregationModel::headerData(int section, Qt::Orientation orientation, int role) const
+{
+    if (orientation == Qt::Horizontal && role == Qt::DisplayRole && m_sourceModel) {
+        switch (section) {
+            case 0: return m_sourceModel->headerData(section, orientation, role);
+            case 1: return tr("Lower Extreme");
+            case 2: return tr("Lower Quartile");
+            case 3: return tr("Median");
+            case 4: return tr("Upper Quartile");
+            case 5: return tr("Upper Extreme");
+        }
+    }
+
+    return QAbstractTableModel::headerData(section, orientation, role);
+}
+
+void NumericAggregationModel::recompute()
+{
+    if (!m_sourceModel)
+        return;
+
+    const auto rowCount = m_sourceModel->rowCount();
+    beginResetModel();
+
+    m_data.clear();
+    m_maxValue = 0;
+
+    if (rowCount <= 0 || m_aggrValue.isEmpty()) {
+        endResetModel();
+        return;
+    }
+
+    m_data.reserve(rowCount);
+    QVector<double> values;
+
+    for (int row = 0; row < rowCount; ++row) {
+        const auto samples = m_sourceModel->index(row, 0).data(TimeAggregationModel::SamplesRole).value<QVector<Sample>>();
+
+        values.clear();
+        values.reserve(samples.size());
+
+        foreach (const auto &sample, samples) {
+            const auto v = sample.value(m_aggrValue).toDouble();
+            values.push_back(v);
+        }
+
+        std::sort(values.begin(), values.end());
+
+        Data d;
+        if (values.size() > 0) {
+            d.lowerExtreme = values.at(0);
+            d.lowerQuartile = values.at(values.size() / 4);
+            d.median = values.at(values.size() / 2);
+            d.upperQuartile = values.at(values.size() * 3 / 4);
+            d.upperExtreme = values.last();
+            m_maxValue = std::max(m_maxValue, d.upperExtreme);
+        }
+        m_data.push_back(d);
+    }
+
+    endResetModel();
+}
diff --git a/analyzer/numericaggregationmodel.h b/analyzer/numericaggregationmodel.h
new file mode 100644
index 0000000000000000000000000000000000000000..e0aa756e783c704aec8b3cca8ce013a38e6b4b70
--- /dev/null
+++ b/analyzer/numericaggregationmodel.h
@@ -0,0 +1,61 @@
+/*
+    Copyright (C) 2016 Volker Krause <vkrause@kde.org>
+
+    This program is free software; you can redistribute it and/or modify it
+    under the terms of the GNU Library General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or (at your
+    option) any later version.
+
+    This program is distributed in the hope that it will be useful, but WITHOUT
+    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Library General Public
+    License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef USERFEEDBACK_ANALYZER_NUMERICAGGREGATIONMODEL_H
+#define USERFEEDBACK_ANALYZER_NUMERICAGGREGATIONMODEL_H
+
+#include <QAbstractTableModel>
+
+namespace UserFeedback {
+namespace Analyzer {
+
+class NumericAggregationModel : public QAbstractTableModel
+{
+    Q_OBJECT
+public:
+    explicit NumericAggregationModel(QObject *parent = nullptr);
+    ~NumericAggregationModel();
+
+    void setSourceModel(QAbstractItemModel *model);
+    void setAggregationValue(const QString &aggrValue);
+
+    int rowCount(const QModelIndex &parent) const override;
+    int columnCount(const QModelIndex &parent) const override;
+    QVariant data(const QModelIndex &index, int role) const override;
+    QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
+
+private:
+    void recompute();
+
+    QAbstractItemModel *m_sourceModel = nullptr;
+    QString m_aggrValue;
+    struct Data {
+        double lowerExtreme = 0.0;
+        double lowerQuartile = 0.0;
+        double median = 0.0;
+        double upperQuartile = 0.0;
+        double upperExtreme = 0.0;
+    };
+    QVector<Data> m_data;
+
+    double m_maxValue = 0.0;
+};
+
+}
+}
+
+#endif // USERFEEDBACK_ANALYZER_NUMERICAGGREGATIONMODEL_H