Let's define precisely what is a line chart when it's best used and the dos and don'ts of this type of chart.

If you want to dive directly into practice, have a look at the How-to do a line chart? guide.

What's a line chart?

A line chart (or line plot or line graph or curve chart) displays data values as points (markers or symbols) that are connected using line segments, curves, or even steps.

The most standard usage of a line chart is to show the trend of a value over a continuous time span which is represented along the X-axis.

The measurement points are ordered, typically by their X-axis value.

//run height=400 from=100 to=114
import io.data2viz.charts.*
import io.data2viz.charts.core.Padding
import io.data2viz.charts.core.*
import io.data2viz.charts.dimension.*
import io.data2viz.charts.chart.*
import io.data2viz.charts.chart.mark.*
import io.data2viz.charts.viz.*
import io.data2viz.charts.layout.*
import io.data2viz.charts.config.configs.*

import io.data2viz.math.*
import io.data2viz.color.*
import io.data2viz.geom.*
import io.data2viz.shape.Symbols
import io.data2viz.dsv.Dsv

import org.w3c.fetch.Response
import kotlinx.browser.window
import kotlin.js.Promise

val width = 600.0
val height = 400.0

// The dataset holds 2016 & 2017
private val year = 2017

// The "Weather" class
data class Weather(
    val city: String, 
    val year: Int, 
    val month: Int, 
    val highTemp: Double, 
    val avgTemp: Double, 
    val lowTemp: Double,
    val precip: Double)

// This function transform a CSV line to a "Weather" instance
private fun parseWeather(row: List<String>) = Weather(
    row[0],
    row[1].toInt(), 
    row[2].toInt(),
    row[3].toDouble(), 
    row[4].toDouble(), 
    row[5].toDouble(),
    row[6].toDouble()
)

// Just use a simple list of months label for the X axis
private val months = listOf("Jan.", "Feb.", "Mar.", "Apr.", "May", "Jun.", 
														"Jul.", "Aug.", "Sep.", "Oct.", "Nov.", "Dec.")

fun main() {
    
    // Creating and sizing the VizContainer
    val vc = newVizContainer().apply {
        size = Size(width, height)
    }
    
    // original taken from <https://vincentarelbundock.github.io/Rdatasets/>
    val request: Promise<Response> =
		window.fetch("<https://docs.google.com/spreadsheets/d/e/2PACX-1vTX4QuCNyDvUoA>"
									+"wk6Jl6UJ4r336A87VIKQ5BVyEgowXG_raXdFBMvmUhmz1LLc07GavyC9J6pZ4"
									+"YHqJ/pub?gid=650761999&single=true&output=csv")
    
	request.then {
		it.text().then { 
            
			val results = Dsv()
        		.parseRows(it)
        		.drop(1)
        		.map { parseWeather(it) }
               	.filter { it.year == year }
                .toMutableList()
            
            vc.chart(results) {

                title = "Monthly average temperature in $year"
                
                config {
                    cursor {
                        show = true
                        type = CursorType.Vertical
                    }
                    legend {
                        show = LegendVisibility.Show
                    }
                }
                
                series = discrete( { domain.city } )

                val monthDim = discrete( { domain.month } ) {
                    formatter = { "${months[this - 1]} "}
                }
                
                val tempDim = quantitative( { domain.avgTemp } ) {
                    name = "Average temperature for the month"
                    formatter = { "$thisĀ°F" }
                }
                
                line(monthDim, tempDim) {
                    
										// data is loaded from an external CSV, it may take a few seconds
                    curve = MarkCurves.Curved
                    size = constant(30.0)
                    symbol = constant(Symbols.Circle)
                    showSymbols = true
                    
                    strokeWidth = constant(2.0)
                    
                    y {
                        start = .0
                        end = 100.0
                    }
                }
            }
        }
    }
}

When to use a line chart?

A line chart is particularly great for these use cases:

Catch a glimpse of the changes over time

We generally associate the X-axis of a line chart with time, so using a line chart to display the variation of a value over time allows the user to see it very quickly.

It's also working very well for any other dimension (distance, temperature...) that is expected to be continuous.

//run height=150 from=15 to=20
import io.data2viz.charts.core.*
import io.data2viz.charts.chart.*
import io.data2viz.charts.chart.mark.*
import io.data2viz.charts.viz.*
import io.data2viz.geom.*

val width = 400.0
val height = 150.0

fun main() {
    
    val vc = newVizContainer().apply { size = Size(width, height) }
    
    with(vc) {
        val values = listOf(1.0, 1.2, 1.3, 1.2, 1.5, 1.9, 2.6, 4.2, 6.8, 7.7, 8.2, 8.4, 8.6, 8.6, 8.5, 8.7)
    	chart(values) {
            val indexDimension = quantitative( { indexInData.toDouble() } ) 
            val valueDimension = quantitative( { domain } )
            line(indexDimension, valueDimension)
        }
    }
}

Identify trends

Line charts are good to detect patterns and identify trends, but also very good for seeing when something's wrong (pikes or holes...).

//run height=150 from=17 to=22
import io.data2viz.charts.core.*
import io.data2viz.charts.chart.*
import io.data2viz.charts.chart.mark.*
import io.data2viz.charts.viz.*
import io.data2viz.geom.*
import kotlin.random.*
import kotlin.math.*

val width = 400.0
val height = 150.0

fun main() {
    
    val vc = newVizContainer().apply { size = Size(width, height) }
    
    with(vc) {
        val values = (0 .. 100).map{ Random.nextDouble() + if (it == 80) 16.0 else .0 }
    	chart(values) {
            val indexDimension = quantitative( { indexInData.toDouble() } )
            val valueDimension = quantitative( { domain } )
            line(indexDimension, valueDimension)
        }
    }
}

Compare patterns

Line charts offer a high data to ink ratio, making them a good option to identify patterns when you have multiple series.

//run height=150 from=53 to=60
import io.data2viz.charts.core.*
import io.data2viz.charts.chart.*
import io.data2viz.charts.chart.mark.*
import io.data2viz.charts.viz.*
import io.data2viz.geom.*
import kotlin.random.*
import kotlin.math.*

val width = 400.0
val height = 150.0

data class Record(val batch:Int, val value:Double)
val values = listOf(
    Record(1, 1.0),
    Record(1, 1.2),
    Record(1, 1.3),
    Record(1, 1.2),
    Record(1, 1.5),
    Record(1, 1.9),
    Record(1, 2.6),
    Record(1, 4.2),
    Record(1, 6.8),
    Record(1, 7.7),
    Record(1, 8.2),
    Record(1, 8.4),
    Record(1, 8.6),
    Record(1, 8.6),
    Record(1, 8.5),
    Record(1, 8.7),
    Record(2, 8.0),
    Record(2, 7.8),
    Record(2, 7.3),
    Record(2, 7.2),
    Record(2, 7.5),
    Record(2, 6.9),
    Record(2, 6.6),
    Record(2, 6.2),
    Record(2, 5.8),
    Record(2, 5.7),
    Record(2, 5.2),
    Record(2, 5.4),
    Record(2, 5.6),
    Record(2, 5.6),
    Record(2, 5.5),
    Record(2, 5.7)
)       

fun main() {
    
    val vc = newVizContainer().apply { size = Size(width, height) }
    
    with(vc) {
    	chart(values) {
            val indexDimension = quantitative( { indexInSeries.toDouble() } )
            val valueDimension = quantitative( { domain.value } )
            series = discrete( { domain.batch } )
            line(indexDimension, valueDimension) {
							strokeWidth = constant(2.0)
						}
        }
    }
}

When not to use a line chart?