Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding a trend line to Line Charts and Bar Charts? #104

Closed
gesabo opened this issue Jul 20, 2021 · 15 comments
Closed

Adding a trend line to Line Charts and Bar Charts? #104

gesabo opened this issue Jul 20, 2021 · 15 comments

Comments

@gesabo
Copy link

gesabo commented Jul 20, 2021

@willdale have you considered adding a trend line or moving average line? I've done this in the past in the iOS Charts lib manually by adding another line with data points that average some number of total points on the chart. For example if I had 100 data points on line 1, line 2 would be constructed using 10 data points (0-10 average = point 1, 11-21 = point 2 etc.).

If you were to incorporate this in the library then maybe add a number to pass in for granularity, i.e. adding a trend line like my example above would pass in maybe the number 10, as in every 10 data points and more granularity might be 5, so that the trend line represented the average of every 5 data points. I think this would be a nice feature to add if not too difficult to incorporate, both for line charts but bar charts and ranged bar charts as well.
Thanks for considering!

@willdale
Copy link
Owner

@gesabo I have considered it, but decided to leave the average calculation as an implementation detail.

Here is an easy way to add a line on top of a LineChart or BarChart.

This goes high up the View Modifier chain.

.extraLine(chartData: data, legendTitle: "Test") {
    extraLineData
} style: {
    extraLineStyle
}

Then this further down.

.extraYAxisLabels(chartData: data, colourIndicator: .style(size: 12))

Data and styling.

private var extraLineData: [ExtraLineDataPoint] {
    [ExtraLineDataPoint(value: 200),
     ExtraLineDataPoint(value: 90),
     ExtraLineDataPoint(value: 700),
     ExtraLineDataPoint(value: 175),
     ExtraLineDataPoint(value: 60),
     ExtraLineDataPoint(value: 100),
     ExtraLineDataPoint(value: 600)]
}
private var extraLineStyle: ExtraLineStyle {
    ExtraLineStyle(lineColour: ColourStyle(colour: .blue),
                   lineType: .curvedLine,
                   lineSpacing: .bar,
                   yAxisTitle: "Bob",
                   yAxisNumberOfLabels: 7,
                   animationType: .raise,
                   baseline: .zero)
}

@gesabo
Copy link
Author

gesabo commented Jul 20, 2021

@willdale thanks so much! Had no idea .extraLine existed! 👏. I have 1 slight issue (this is using my own extra line data points below), I need the line to start after the Bar Chart's Y Axis Labels 👇 any idea?

image

@willdale
Copy link
Owner

Where in the view chain is it?

BarChart(...)
    .extraLine(...)

@gesabo
Copy link
Author

gesabo commented Jul 20, 2021

BarChart(chartData: barChartData)
                .xAxisLabels(chartData: barChartData)
                .yAxisLabels(chartData: barChartData)
                .id(barChartData.id)
                .extraLine(chartData: barChartData, legendTitle: "Test") {
                     makeRecoveryTrendData(objects)
                       
                } style: {
                    extraLineStyle
                }
                .extraYAxisLabels(chartData: barChartData, colourIndicator: .style(size: 12))

@gesabo
Copy link
Author

gesabo commented Jul 20, 2021

@willdale oh I see you meant all the way to the top! Got it! Fixed now 👍

@willdale
Copy link
Owner

No worries.

@gesabo
Copy link
Author

gesabo commented Aug 2, 2021

@willdale would it be possible to add .extraLine to a RangedLineChart as well?

@willdale
Copy link
Owner

willdale commented Aug 3, 2021

It should already work, are you facing an issue?

@gesabo
Copy link
Author

gesabo commented Aug 3, 2021

@willdale yes unless I'm doing something wrong, I'm getting Instance method 'extraLine(chartData:legendTitle:datapoints:style:)' requires that '[ExtraLineDataPoint]' conform to 'CTLineBarChartDataProtocol'


import SwiftUI
import SwiftUICharts

struct RangedLineChartDemoView: View {

    let data : RangedLineChartData = weekOfData()
    
        private var extraLineStyle: ExtraLineStyle {
    
        ExtraLineStyle(lineColour: ColourStyle(colour: TrackerConstants.trendLineColor),
                       lineType: .curvedLine,
                       lineSpacing: .bar,
                       animationType: .raise,
                       baseline: .zero,
                       topLine: .maximum(of: 100.0))
                       
                       
    }
    
    private var extraLineData: [ExtraLineDataPoint] {
    [ExtraLineDataPoint(value: 5.4),
     ExtraLineDataPoint(value: 2.1),
     ExtraLineDataPoint(value: 5.4),
     ExtraLineDataPoint(value: 5.5),
     ExtraLineDataPoint(value: 1.5),
     ExtraLineDataPoint(value: 0.5),
     ExtraLineDataPoint(value: 5.5)]
    }
    
    var body: some View {
        VStack {
            
            RangedLineChart(chartData: data)
                .extraLine(chartData: extraLineData, legendTitle: "Test") {
                    extraLineData
                } style: {
                    extraLineStyle
                }
                .extraYAxisLabels(chartData: data, colourIndicator: .style(size: 12))
                .pointMarkers(chartData: data)
                .touchOverlay(chartData: data, specifier: "%.0f", unit: .prefix(of: "$"))
                .averageLine(chartData: data,
                             labelPosition: .center(specifier: "%.0f"),
                             strokeStyle: StrokeStyle(lineWidth: 3, dash: [5,10]))
                .xAxisGrid(chartData: data)
                .yAxisGrid(chartData: data)
                .xAxisLabels(chartData: data)
                .yAxisLabels(chartData: data)
                .infoBox(chartData: data)
                .headerBox(chartData: data)
                .legends(chartData: data, columns: [GridItem(.flexible()), GridItem(.flexible())])
                .id(data.id)
                .frame(minWidth: 150, maxWidth: 900, minHeight: 150, idealHeight: 500, maxHeight: 600, alignment: .center)
                .padding(.horizontal)
        }
        .navigationTitle("Week of Data")
    }
}


extension RangedLineChartDemoView {
    
    static func weekOfData() -> RangedLineChartData {

        let data = RangedLineDataSet(dataPoints: [
            RangedLineChartDataPoint(value: 6.8, upperValue: 7.8, lowerValue: 5.8, xAxisLabel: "", description: ""),
            RangedLineChartDataPoint(value: 7.4, upperValue: 8.4, lowerValue: 6.4, xAxisLabel: "", description: ""),
             RangedLineChartDataPoint(value: 9.2, upperValue: 10.0, lowerValue: 8.2, xAxisLabel: "", description: ""),
            RangedLineChartDataPoint(value: 5.9, upperValue: 6.9, lowerValue: 4.9, xAxisLabel: "", description: ""),
            RangedLineChartDataPoint(value: 3.8, upperValue: 4.8, lowerValue: 2.8, xAxisLabel: "", description: ""),
            RangedLineChartDataPoint(value: 6.5, upperValue: 7.5, lowerValue: 5.5, xAxisLabel: "", description: ""),
            RangedLineChartDataPoint(value: 7.2, upperValue: 8.2, lowerValue: 6.2, xAxisLabel: "", description: "")
           
        ],
        legendTitle: "Profits",
        legendFillTitle: "Expected",
        pointStyle: PointStyle(),
        style: RangedLineStyle(lineColour: ColourStyle(colour: .red),
                               fillColour: ColourStyle(colour: Color(.blue).opacity(0.25)),
                               lineType: .curvedLine))
        
        let metadata    = ChartMetadata(title: "Profits", subtitle: "with Expected")
                
        let gridStyle   = GridStyle(numberOfLines: 7,
                                    lineColour   : Color(.lightGray).opacity(0.5),
                                    lineWidth    : 1,
                                    dash         : [8],
                                    dashPhase    : 0)
        
        let chartStyle = LineChartStyle(infoBoxPlacement    : .infoBox(isStatic: false),
                                        
                                        markerType          : .vertical(attachment: .line(dot: .style(DotStyle()))),
                                        
                                        xAxisGridStyle      : gridStyle,
                                        xAxisLabelPosition  : .bottom,
                                        xAxisLabelColour    : Color.primary,
                                        xAxisLabelsFrom     : .dataPoint(rotation: .degrees(0)),
                                       
                                        yAxisGridStyle      : gridStyle,
                                        yAxisLabelPosition  : .leading,
                                        yAxisLabelColour    : Color.primary,
                                        yAxisNumberOfLabels : 7,
                                        
                                        baseline: .minimumValue,
                                        topLine: .maximumValue,
                                        
                                        globalAnimation     : .easeOut(duration: 1))
        
        return RangedLineChartData(dataSets       : data,
                                   metadata       : metadata,
                                   chartStyle     : chartStyle)
        
    }
    
}

@willdale
Copy link
Owner

willdale commented Aug 3, 2021

Hi,

Change .extraLine(chartData: extraLineData, ... to .extraLine(chartData: data, ...

Should solve it.

@gesabo
Copy link
Author

gesabo commented Aug 3, 2021

@willdale got it, yes it does. 👍 Is there any way to display the points on the extraLine? markerType doesn't seem to be available in ExtraLineStyle?

@willdale
Copy link
Owner

This has been added in v2.9.0.
A pointStyle attribute has been added to ExtraLineStyle to allow the addition of Point Markers.
A markerType attribute has been added to ExtraLineStyle to add touch interaction.

Theres also a optional change to the API to make is slightly neater.

.extraLine(chartData: data,
           legendTitle: "Test",
           datapoints: extraLineData,
           style: extraLineStyle)

@gesabo
Copy link
Author

gesabo commented Aug 13, 2021

@willdale is there a way I can force the ExtraLine Point Marker to alway show or always be at the top z position? in my code here below... the middle value's (5.5) point parker does not show because it is covered by the green range. If I set the RangeLineStyle's FillColour to .clear then the 5.5 point shows. 🤔


struct RangedLineChartDemoView: View {

    let data : RangedLineChartData = weekOfData()
    
        private var extraLineStyle: ExtraLineStyle {
    
        ExtraLineStyle(lineColour: ColourStyle(colour: TrackerConstants.AppleFitnessBlue),
                       lineType: .curvedLine,
                       lineSpacing: .line,
                       markerType: .vertical(attachment: .line(dot: .style(DotStyle()))),
                       pointStyle: PointStyle(),
                       animationType: .raise,
                       baseline: .zero,
                       topLine: .maximum(of: 10.0))
                       
                       
    }
    
    private var extraLineData: [ExtraLineDataPoint] {
    [ExtraLineDataPoint(value: 5.4),
     ExtraLineDataPoint(value: 2.1),
     ExtraLineDataPoint(value: 5.4),
     ExtraLineDataPoint(value: 5.5),
     ExtraLineDataPoint(value: 1.5),
     ExtraLineDataPoint(value: 0.5),
     ExtraLineDataPoint(value: 5.5)]
    }
    
    var body: some View {
        VStack {
            
            RangedLineChart(chartData: data)
                .extraLine(chartData: data,
                           legendTitle: "Exertion",
                           datapoints: extraLineData,
                           style: extraLineStyle)
            
                .extraYAxisLabels(chartData: data, colourIndicator: .style(size: 12))
                .pointMarkers(chartData: data)
                .touchOverlay(chartData: data, specifier: "%.0f", unit: .prefix(of: "$"))
                .averageLine(chartData: data,
                             labelPosition: .center(specifier: "%.0f"),
                             strokeStyle: StrokeStyle(lineWidth: 3, dash: [5,10]))
                .xAxisGrid(chartData: data)
                .yAxisGrid(chartData: data)
                .xAxisLabels(chartData: data)
                .yAxisLabels(chartData: data)
                .infoBox(chartData: data)
                .headerBox(chartData: data)
                .legends(chartData: data, columns: [GridItem(.flexible()), GridItem(.flexible())])
                .id(data.id)
                .frame(minWidth: 150, maxWidth: 900, minHeight: 150, idealHeight: 500, maxHeight: 600, alignment: .center)
                .padding(.horizontal)
        }
        .navigationTitle("Week of Data")
    }
}


extension RangedLineChartDemoView {
    
    static func weekOfData() -> RangedLineChartData {

        let data = RangedLineDataSet(dataPoints: [
            RangedLineChartDataPoint(value: 6.8, upperValue: 7.8, lowerValue: 5.8, xAxisLabel: "", description: ""),
            RangedLineChartDataPoint(value: 7.4, upperValue: 8.4, lowerValue: 6.4, xAxisLabel: "", description: ""),
             RangedLineChartDataPoint(value: 9.2, upperValue: 10.0, lowerValue: 8.2, xAxisLabel: "", description: ""),
            RangedLineChartDataPoint(value: 5.9, upperValue: 6.9, lowerValue: 4.9, xAxisLabel: "", description: ""),
            RangedLineChartDataPoint(value: 3.8, upperValue: 4.8, lowerValue: 2.8, xAxisLabel: "", description: ""),
            RangedLineChartDataPoint(value: 6.5, upperValue: 7.5, lowerValue: 5.5, xAxisLabel: "", description: ""),
            RangedLineChartDataPoint(value: 7.2, upperValue: 8.2, lowerValue: 6.2, xAxisLabel: "", description: "")
           
        ],
        legendTitle: "Recovery",
        legendFillTitle: "Target Range",
        pointStyle: PointStyle(),
        style: RangedLineStyle(lineColour: ColourStyle(colour: .gray),
                               fillColour: ColourStyle(colour: TrackerConstants.AppleFitnessGreen),
                               lineType: .curvedLine))
        
        let metadata    = ChartMetadata(title: "", subtitle: "")
                
        let gridStyle   = GridStyle(numberOfLines: 7,
                                    lineColour   : Color(.lightGray).opacity(0.5),
                                    lineWidth    : 1,
                                    dash         : [8],
                                    dashPhase    : 0)
        
        let chartStyle = LineChartStyle(infoBoxPlacement    : .infoBox(isStatic: false),
                                        
                                        markerType          : .vertical(attachment: .line(dot: .style(DotStyle()))),
                                        
                                        xAxisGridStyle      : gridStyle,
                                        xAxisLabelPosition  : .bottom,
                                        xAxisLabelColour    : Color.primary,
                                        xAxisLabelsFrom     : .dataPoint(rotation: .degrees(0)),
                                       
                                        yAxisGridStyle      : gridStyle,
                                        yAxisLabelPosition  : .leading,
                                        yAxisLabelColour    : Color.primary,
                                        yAxisNumberOfLabels : 7,
                                        
                                        baseline: .zero,
                                        topLine: .maximum(of: 10),
                                        
                                        globalAnimation     : .easeOut(duration: 1))
        
        return RangedLineChartData(dataSets       : data,
                                   metadata       : metadata,
                                   chartStyle     : chartStyle)
        
    }
    
}

@willdale
Copy link
Owner

should be fixed in v2.9.1

@gesabo
Copy link
Author

gesabo commented Aug 17, 2021

@willdale it is, thanks! 🍻

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants