How to make a grouped BarChart with ios-charts?

2019-02-14 15:47发布

问题:

I am using ios-charts library.

I would like to group my inverter values so that each year is one group. Unfortunately, the number of monthly values per year may vary.

My data json looks like this:

{"monthlyData":[{"ERTRAG":"30.2989999055862","MONAT":"2","JAHR":"2016"},{"ERTRAG":"154.897000223398","MONAT":"1","JAHR":"2016"},{"ERTRAG":"141.996000155807","MONAT":"12","JAHR":"2015"},{"ERTRAG":"135.449000149965","MONAT":"11","JAHR":"2015"},{"ERTRAG":"319.437000751495","MONAT":"10","JAHR":"2015"},{"ERTRAG":"483.369997739792","MONAT":"9","JAHR":"2015"},{"ERTRAG":"698.112997770309","MONAT":"8","JAHR":"2015"},{"ERTRAG":"771.764000892639","MONAT":"7","JAHR":"2015"},{"ERTRAG":"736.611003398895","MONAT":"6","JAHR":"2015"},{"ERTRAG":"737.237999916077","MONAT":"5","JAHR":"2015"},{"ERTRAG":"720.181995391846","MONAT":"4","JAHR":"2015"},{"ERTRAG":"484.979001283646","MONAT":"3","JAHR":"2015"},{"ERTRAG":"249.974001646042","MONAT":"2","JAHR":"2015"},{"ERTRAG":"92.8830004036427","MONAT":"1","JAHR":"2015"},{"ERTRAG":"52.7970000207424","MONAT":"12","JAHR":"2014"},{"ERTRAG":"181.025999426842","MONAT":"11","JAHR":"2014"},{"ERTRAG":"332.789002537727","MONAT":"10","JAHR":"2014"},{"ERTRAG":"482.244999885559","MONAT":"9","JAHR":"2014"},{"ERTRAG":"602.811999320984","MONAT":"8","JAHR":"2014"},{"ERTRAG":"699.872003316879","MONAT":"7","JAHR":"2014"},{"ERTRAG":"828.212007522583","MONAT":"6","JAHR":"2014"},{"ERTRAG":"679.010004997253","MONAT":"5","JAHR":"2014"},{"ERTRAG":"635.115998744965","MONAT":"4","JAHR":"2014"},{"ERTRAG":"559.617002010345","MONAT":"3","JAHR":"2014"},{"ERTRAG":"265.135001063347","MONAT":"2","JAHR":"2014"},{"ERTRAG":"165.272998273373","MONAT":"1","JAHR":"2014"},{"ERTRAG":"134.578999936581","MONAT":"12","JAHR":"2013"},{"ERTRAG":"153.774000287056","MONAT":"11","JAHR":"2013"},{"ERTRAG":"321.733997344971","MONAT":"10","JAHR":"2013"},{"ERTRAG":"482.768000483513","MONAT":"9","JAHR":"2013"},{"ERTRAG":"692.864000797272","MONAT":"8","JAHR":"2013"},{"ERTRAG":"846.429007053375","MONAT":"7","JAHR":"2013"},{"ERTRAG":"709.758005619049","MONAT":"6","JAHR":"2013"},{"ERTRAG":"532.493996858597","MONAT":"5","JAHR":"2013"},{"ERTRAG":"462.539998054504","MONAT":"4","JAHR":"2013"},{"ERTRAG":"419.105004012585","MONAT":"3","JAHR":"2013"},{"ERTRAG":"143.189998820424","MONAT":"2","JAHR":"2013"},{"ERTRAG":"78.720000628382","MONAT":"1","JAHR":"2013"},{"ERTRAG":"90.1430006623268","MONAT":"12","JAHR":"2012"},{"ERTRAG":"155.483000457287","MONAT":"11","JAHR":"2012"},{"ERTRAG":"348.231998205185","MONAT":"10","JAHR":"2012"},{"ERTRAG":"598.037001848221","MONAT":"9","JAHR":"2012"},{"ERTRAG":"729.740003108978","MONAT":"8","JAHR":"2012"},{"ERTRAG":"676.923998832703","MONAT":"7","JAHR":"2012"},{"ERTRAG":"694.879002094269","MONAT":"6","JAHR":"2012"},{"ERTRAG":"811.281997680664","MONAT":"5","JAHR":"2012"},{"ERTRAG":"489.765002369881","MONAT":"4","JAHR":"2012"},{"ERTRAG":"538.866001605988","MONAT":"3","JAHR":"2012"},{"ERTRAG":"277.856996208429","MONAT":"2","JAHR":"2012"},{"ERTRAG":"155.854999214411","MONAT":"1","JAHR":"2012"},{"ERTRAG":"148.157999750227","MONAT":"12","JAHR":"2011"},{"ERTRAG":"230.409998774529","MONAT":"11","JAHR":"2011"}]}

I tried the following:

var months = Array<String>()
var years = Array<String>()
var dataEntries: [BarChartDataEntry] = []
var dataSets: [BarChartDataSet] = []
var overallSum = 0.0
var year = 2010 //monthlyValues[0].year
var count = 0
var chartDataSet: BarChartDataSet?

for i in 0..<monthlyValues.count {
  // if year is different, create new barchartdataset
  if monthlyValues[i].year != year {
    years.append("\(monthlyValues[i].year)")
    chartDataSet = BarChartDataSet(yVals: dataEntries, label: "\(year)")
    chartDataSet!.colors = [UIColor.whiteColor()]
    dataSets.append(chartDataSet!)
    year = monthlyValues[i].year
    dataEntries = []
    //months = []
    count = 0
  }
  months.append("\(monthlyValues[i].month)")
  overallSum += monthlyValues[i].amount

  let dataEntry = BarChartDataEntry(value: monthlyValues[i].amount, xIndex: count)
  count++
  dataEntries.append(dataEntry)
}
let chartData = BarChartData(xVals: months, dataSets: dataSets)
self.barChartView.data = chartData

This results in five series with the right year labels in the legend but there is no group separator in the chart and also the number of bars per year is either 4 or 5 (expected = 12).

Thanks for any hint on this!

回答1:

You got something wrong here. Right now you are creating a new DataSet for every year.

That is wrong.

What you need to do is create as many DataSets as you want bars per group where one DataSet represents one bar in each group. (e.g. DataSet 1 represents every first bar of a group, DataSet 2 every second bar of a group, ...)

As an example, imagine a chart like this with 4 groups of 3 bars per group. Each group represents one year.

||| ||| ||| |||

  • The setup shown above requires 3 DataSets (one for each group)
  • Entries in the same group have the same x-index (e.g. in the first group all have x-index 0)
  • It consists of a total of 12 entries
  • Each DataSet contains 4 entries with Entry x-indices ranging from 0 to 3

Here you can find an example of the scenario described above.



回答2:

let months = ["Jan", "Feb", "Mar", "Apr", "May"]
    let unitsSold = [20.0, 4.0, 6.0, 3.0, 12.0]
    let unitsBought = [10.0, 14.0, 60.0, 13.0, 2.0]

     override func viewDidLoad() {
            super.viewDidLoad()
            // Do any additional setup after loading the view, typically from a nib.
            barChartView.delegate = self
            barChartView.noDataText = "You need to provide data for the chart."
            barChartView.chartDescription?.text = "sales vs bought "


            //legend
            let legend = barChartView.legend
            legend.enabled = true
            legend.horizontalAlignment = .right
            legend.verticalAlignment = .top
            legend.orientation = .vertical
            legend.drawInside = true
            legend.yOffset = 10.0;
            legend.xOffset = 10.0;
            legend.yEntrySpace = 0.0;


            let xaxis = barChartView.xAxis
            xaxis.valueFormatter = axisFormatDelegate
            xaxis.drawGridLinesEnabled = true
            xaxis.labelPosition = .bottom
            xaxis.centerAxisLabelsEnabled = true
            xaxis.valueFormatter = IndexAxisValueFormatter(values:self.months)
            xaxis.granularity = 1


            let leftAxisFormatter = NumberFormatter()
            leftAxisFormatter.maximumFractionDigits = 1

            let yaxis = barChartView.leftAxis
            yaxis.spaceTop = 0.35
            yaxis.axisMinimum = 0
            yaxis.drawGridLinesEnabled = false

            barChartView.rightAxis.enabled = false
           //axisFormatDelegate = self

            setChart()
        }

 func setChart() {
        barChartView.noDataText = "You need to provide data for the chart."
        var dataEntries: [BarChartDataEntry] = []
        var dataEntries1: [BarChartDataEntry] = []

        for i in 0..<self.months.count {

            let dataEntry = BarChartDataEntry(x: Double(i) , y: self.unitsSold[i])
            dataEntries.append(dataEntry)

            let dataEntry1 = BarChartDataEntry(x: Double(i) , y: self.self.unitsBought[i])
            dataEntries1.append(dataEntry1)

            //stack barchart
            //let dataEntry = BarChartDataEntry(x: Double(i), yValues:  [self.unitsSold[i],self.unitsBought[i]], label: "groupChart")



        }

        let chartDataSet = BarChartDataSet(values: dataEntries, label: "Unit sold")
        let chartDataSet1 = BarChartDataSet(values: dataEntries1, label: "Unit Bought")

        let dataSets: [BarChartDataSet] = [chartDataSet,chartDataSet1]
        chartDataSet.colors = [UIColor(red: 230/255, green: 126/255, blue: 34/255, alpha: 1)]
        //chartDataSet.colors = ChartColorTemplates.colorful()
        //let chartData = BarChartData(dataSet: chartDataSet)

        let chartData = BarChartData(dataSets: dataSets)


        let groupSpace = 0.3
        let barSpace = 0.05
        let barWidth = 0.3
        // (0.3 + 0.05) * 2 + 0.3 = 1.00 -> interval per "group" 

        let groupCount = self.months.count
        let startYear = 0


        chartData.barWidth = barWidth;
        barChartView.xAxis.axisMinimum = Double(startYear)
        let gg = chartData.groupWidth(groupSpace: groupSpace, barSpace: barSpace)
        print("Groupspace: \(gg)")
        barChartView.xAxis.axisMaximum = Double(startYear) + gg * Double(groupCount)

        chartData.groupBars(fromX: Double(startYear), groupSpace: groupSpace, barSpace: barSpace)
        //chartData.groupWidth(groupSpace: groupSpace, barSpace: barSpace)
        barChartView.notifyDataSetChanged()

        barChartView.data = chartData






        //background color
        barChartView.backgroundColor = UIColor(red: 189/255, green: 195/255, blue: 199/255, alpha: 1)

        //chart animation
        barChartView.animate(xAxisDuration: 1.5, yAxisDuration: 1.5, easingOption: .linear)


    }


回答3:

if you have noticed in above code in function chart. There is let

 groupSpace = 0.3
 let barSpace = 0.05
 let barWidth = 0.3
 // (0.3 + 0.05) * 2 + 0.3 = 1.00 -> interval per "group" 

which will make equation (groupSpace * barSpace) * n + groupSpace = 1

you need to play around this equation for n number of bars



回答4:

I've tried Rajan Twanabashu solution, but it was almost the good one. Maybe it has changed since, I don't know.

Here's what I've done, after I found the groupWidth function in the BarDataChart file from Charts.

groupWidth function declaration

let groupSpace = 1 - n * (barWidth + barSpace)

Hope it could help