{"id":"charts-several-series","slug":"several-series","title":"Several series from a map","text":"One chart can hold several series. Each gets a line of its own, in a color of its own, and the legend says which line is which: that's what makes lines easy to compare. A bakery counts what it sells of three products each week, one column per product:\n\n```file bakery.csv\nweek,bread,buns,pies\n1,120,80,15\n2,115,95,-\n3,130,90,18\n4,125,-,22\n5,140,105,20\n```\n\nThe names of the series come from the header, so the program needs no change when the bakery adds a fourth product:\n\n```java\nimport java.nio.file.Files;\nimport java.nio.file.Paths;\nimport java.util.ArrayList;\nimport java.util.List;\nimport javafx.application.Application;\nimport javafx.scene.Scene;\nimport javafx.scene.chart.LineChart;\nimport javafx.scene.chart.NumberAxis;\nimport javafx.scene.chart.XYChart;\nimport javafx.stage.Stage;\n\npublic class Main extends Application {\n\n    @Override\n    public void start(Stage stage) throws Exception {\n        NumberAxis weeks = new NumberAxis(\"Week\", 1, 5, 1);\n        NumberAxis sold = new NumberAxis();\n        sold.setLabel(\"Sold\");\n        LineChart<Number, Number> chart = new LineChart<>(weeks, sold);\n        chart.setTitle(\"Bakery sales\");\n\n        List<String> lines = Files.readAllLines(Paths.get(\"bakery.csv\"));\n        String[] names = lines.get(0).split(\",\");\n        List<XYChart.Series<Number, Number>> products = new ArrayList<>();\n        for (int c = 1; c < names.length; c++) {\n            XYChart.Series<Number, Number> series = new XYChart.Series<>();\n            series.setName(names[c]);\n            products.add(series);\n        }\n        for (int i = 1; i < lines.size(); i++) {\n            String[] parts = lines.get(i).split(\",\");\n            int week = Integer.valueOf(parts[0]);\n            for (int c = 1; c < parts.length; c++) {\n                if (!parts[c].equals(\"-\")) {\n                    products.get(c - 1).getData().add(new XYChart.Data<>(week, Integer.valueOf(parts[c])));\n                }\n            }\n        }\n        chart.getData().addAll(products);\n\n        stage.setScene(new Scene(chart));\n        stage.setTitle(\"Bakery\");\n        stage.show();\n    }\n\n    public static void main(String[] args) {\n        launch(Main.class);\n    }\n}\n```\n\n```window\n{\"events\":[],\"window\":{\"windows\":[{\"title\":\"Bakery\",\"scene\":{\"root\":{\"n\":1,\"type\":\"LineChart\",\"title\":\"Bakery sales\",\"xAxis\":{\"type\":\"NumberAxis\",\"label\":\"Week\",\"autoRanging\":false,\"lowerBound\":1,\"upperBound\":5,\"tickUnit\":1,\"forceZeroInRange\":true,\"minorTickVisible\":true},\"yAxis\":{\"type\":\"NumberAxis\",\"label\":\"Sold\",\"autoRanging\":true,\"lowerBound\":0,\"upperBound\":100,\"tickUnit\":5,\"forceZeroInRange\":true,\"minorTickVisible\":true},\"series\":[{\"name\":\"bread\",\"data\":[[1,120],[2,115],[3,130],[4,125],[5,140]]},{\"name\":\"buns\",\"data\":[[1,80],[2,95],[3,90],[5,105]]},{\"name\":\"pies\",\"data\":[[1,15],[3,18],[4,22],[5,20]]}]}}}],\"problems\":[]},\"outline\":\"Window \\\"Bakery\\\"\\n  LineChart \\\"Bakery sales\\\"\\n    x axis NumberAxis \\\"Week\\\" 1 to 5 by 1\\n    y axis NumberAxis \\\"Sold\\\"\\n    series \\\"bread\\\": (1, 120) (2, 115) (3, 130) (4, 125) (5, 140)\\n    series \\\"buns\\\": (1, 80) (2, 95) (3, 90) (5, 105)\\n    series \\\"pies\\\": (1, 15) (3, 18) (4, 22) (5, 20)\"}\n```\n\n- Column `c` of the file is the series at index `c - 1` of the list, since column 0 is the week.\n- A `-` leaves that week out of that series only: the other products of the week still get their points.\n- `chart.getData().addAll(products)` adds every series of the list, in its order. That order is the order of the legend, and the order in which the chart hands out its colors.\n- The legend is visible, as charts have it by default. With three lines, it's needed.\n\n### One series per key\n\nA file can also have one row per measurement. A school garden measures three plants each week, and writes a row for each plant:\n\n```file garden.csv\nweek,plant,cm\n1,tomato,12\n1,bean,8\n1,pea,5\n2,tomato,15\n2,bean,13\n2,pea,-\n3,tomato,19\n3,bean,20\n3,pea,9\n```\n\nNow the names are in the rows, and the program doesn't know them before it reads the file. A map from each name to its series collects the points: the first row of a plant makes its series, and every row adds a point to the series of its plant:\n\n```java\nimport java.nio.file.Files;\nimport java.nio.file.Paths;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport javafx.application.Application;\nimport javafx.scene.Scene;\nimport javafx.scene.chart.LineChart;\nimport javafx.scene.chart.NumberAxis;\nimport javafx.scene.chart.XYChart;\nimport javafx.stage.Stage;\n\npublic class Main extends Application {\n\n    @Override\n    public void start(Stage stage) throws Exception {\n        NumberAxis weeks = new NumberAxis(\"Week\", 1, 3, 1);\n        NumberAxis height = new NumberAxis();\n        height.setLabel(\"Height (cm)\");\n        LineChart<Number, Number> chart = new LineChart<>(weeks, height);\n        chart.setTitle(\"School garden\");\n\n        Map<String, XYChart.Series<Number, Number>> byPlant = new HashMap<>();\n        List<String> lines = Files.readAllLines(Paths.get(\"garden.csv\"));\n        for (int i = 1; i < lines.size(); i++) {\n            String[] parts = lines.get(i).split(\",\");\n            if (!parts[2].equals(\"-\")) {\n                String plant = parts[1];\n                XYChart.Series<Number, Number> series = byPlant.get(plant);\n                if (series == null) {\n                    series = new XYChart.Series<>();\n                    series.setName(plant);\n                    byPlant.put(plant, series);\n                }\n                series.getData().add(new XYChart.Data<>(Integer.valueOf(parts[0]), Integer.valueOf(parts[2])));\n            }\n        }\n\n        List<String> plants = new ArrayList<>(byPlant.keySet());\n        Collections.sort(plants);\n        for (String plant : plants) {\n            chart.getData().add(byPlant.get(plant));\n        }\n\n        stage.setScene(new Scene(chart));\n        stage.setTitle(\"Garden\");\n        stage.show();\n    }\n\n    public static void main(String[] args) {\n        launch(Main.class);\n    }\n}\n```\n\n```window\n{\"events\":[],\"window\":{\"windows\":[{\"title\":\"Garden\",\"scene\":{\"root\":{\"n\":1,\"type\":\"LineChart\",\"title\":\"School garden\",\"xAxis\":{\"type\":\"NumberAxis\",\"label\":\"Week\",\"autoRanging\":false,\"lowerBound\":1,\"upperBound\":3,\"tickUnit\":1,\"forceZeroInRange\":true,\"minorTickVisible\":true},\"yAxis\":{\"type\":\"NumberAxis\",\"label\":\"Height (cm)\",\"autoRanging\":true,\"lowerBound\":0,\"upperBound\":100,\"tickUnit\":5,\"forceZeroInRange\":true,\"minorTickVisible\":true},\"series\":[{\"name\":\"bean\",\"data\":[[1,8],[2,13],[3,20]]},{\"name\":\"pea\",\"data\":[[1,5],[3,9]]},{\"name\":\"tomato\",\"data\":[[1,12],[2,15],[3,19]]}]}}}],\"problems\":[]},\"outline\":\"Window \\\"Garden\\\"\\n  LineChart \\\"School garden\\\"\\n    x axis NumberAxis \\\"Week\\\" 1 to 3 by 1\\n    y axis NumberAxis \\\"Height (cm)\\\"\\n    series \\\"bean\\\": (1, 8) (2, 13) (3, 20)\\n    series \\\"pea\\\": (1, 5) (3, 9)\\n    series \\\"tomato\\\": (1, 12) (2, 15) (3, 19)\"}\n```\n\n- The map's values are series. `byPlant.get(plant)` returns `null` for a plant that has no series yet, and then the program makes one, names it and puts it in the map. Module 33 gave each new key its own list the same way (\"A list as a value\").\n- The series go to the chart in the **sorted** order of their names. A HashMap's order isn't one you choose (module 31), so a program that added them in the map's order would have a legend in an order nobody decided, and the checks compare the series in order. Sorted names give a fixed order: bean, pea, tomato.\n- Each series gets its points in the order of the rows, and the file goes week by week, so every series goes from the smallest x up. Keep it that way: the window as text lists the points in the order they were added, and so do the checks. If a file isn't in order, sort the rows first.\n","task":"A running club writes down how far each runner ran in each week's training run, one row per runner and week, with a `-` when a runner skipped the run:\n\n```text\nweek,runner,km\n1,Mia,5.5\n1,Leo,7\n1,Ava,4\n2,Mia,6\n2,Leo,-\n2,Ava,4.5\n3,Mia,6.5\n3,Leo,8\n3,Ava,5\n```\n\nThe window, its line chart and the axes are ready. Read `runs.csv` and add a series for each runner, named after the runner, with a point (week, km) for each of the runner's rows that has a number, in the order of the file. Read the km as doubles. Add the series to the chart in the alphabetical order of the names (here Ava, Leo, Mia). A runner whose every row has a `-` gets no series. The task card shows the window for the file above.\n","kind":"code","seed":"import java.nio.file.Files;\nimport java.nio.file.Paths;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport javafx.application.Application;\nimport javafx.scene.Scene;\nimport javafx.scene.chart.LineChart;\nimport javafx.scene.chart.NumberAxis;\nimport javafx.scene.chart.XYChart;\nimport javafx.stage.Stage;\n\npublic class Main extends Application {\n\n    @Override\n    public void start(Stage stage) throws Exception {\n        NumberAxis weeks = new NumberAxis();\n        weeks.setLabel(\"Week\");\n        NumberAxis km = new NumberAxis();\n        km.setLabel(\"km\");\n        LineChart<Number, Number> chart = new LineChart<>(weeks, km);\n        chart.setTitle(\"Running club\");\n\n        List<String> lines = Files.readAllLines(Paths.get(\"runs.csv\"));\n\n        stage.setScene(new Scene(chart));\n        stage.setTitle(\"Club\");\n        stage.show();\n    }\n\n    public static void main(String[] args) {\n        launch(Main.class);\n    }\n}\n","solution":"import java.nio.file.Files;\nimport java.nio.file.Paths;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport javafx.application.Application;\nimport javafx.scene.Scene;\nimport javafx.scene.chart.LineChart;\nimport javafx.scene.chart.NumberAxis;\nimport javafx.scene.chart.XYChart;\nimport javafx.stage.Stage;\n\npublic class Main extends Application {\n\n    @Override\n    public void start(Stage stage) throws Exception {\n        NumberAxis weeks = new NumberAxis();\n        weeks.setLabel(\"Week\");\n        NumberAxis km = new NumberAxis();\n        km.setLabel(\"km\");\n        LineChart<Number, Number> chart = new LineChart<>(weeks, km);\n        chart.setTitle(\"Running club\");\n\n        List<String> lines = Files.readAllLines(Paths.get(\"runs.csv\"));\n        Map<String, XYChart.Series<Number, Number>> byRunner = new HashMap<>();\n        for (int i = 1; i < lines.size(); i++) {\n            String[] parts = lines.get(i).split(\",\");\n            if (!parts[2].equals(\"-\")) {\n                String runner = parts[1];\n                XYChart.Series<Number, Number> series = byRunner.get(runner);\n                if (series == null) {\n                    series = new XYChart.Series<>();\n                    series.setName(runner);\n                    byRunner.put(runner, series);\n                }\n                series.getData().add(new XYChart.Data<>(Integer.valueOf(parts[0]), Double.valueOf(parts[2])));\n            }\n        }\n        List<String> runners = new ArrayList<>(byRunner.keySet());\n        Collections.sort(runners);\n        for (String runner : runners) {\n            chart.getData().add(byRunner.get(runner));\n        }\n\n        stage.setScene(new Scene(chart));\n        stage.setTitle(\"Club\");\n        stage.show();\n    }\n\n    public static void main(String[] args) {\n        launch(Main.class);\n    }\n}\n","hints":["Keep a `Map<String, XYChart.Series<Number, Number>>` from each runner's name to the runner's series. For a row with a number, get the runner's series from the map, and make it (named, and put in the map) when it's `null`.","After the loop, copy the names into a list, `new ArrayList<>(byRunner.keySet())`, sort it with `Collections.sort`, and add each runner's series to `chart.getData()` in that order."],"tests":[{"name":"Test 1","stdin":"","files":{"runs.csv":"week,runner,km\n1,Mia,5.5\n1,Leo,7\n1,Ava,4\n2,Mia,6\n2,Leo,-\n2,Ava,4.5\n3,Mia,6.5\n3,Leo,8\n3,Ava,5\n"},"expect":"","hidden":false,"events":[],"window":{"windows":[{"title":"Club","scene":{"root":{"n":1,"type":"LineChart","title":"Running club","xAxis":{"type":"NumberAxis","label":"Week","autoRanging":true,"lowerBound":0,"upperBound":100,"tickUnit":5,"forceZeroInRange":true,"minorTickVisible":true},"yAxis":{"type":"NumberAxis","label":"km","autoRanging":true,"lowerBound":0,"upperBound":100,"tickUnit":5,"forceZeroInRange":true,"minorTickVisible":true},"series":[{"name":"Ava","data":[[1,4],[2,4.5],[3,5]]},{"name":"Leo","data":[[1,7],[3,8]]},{"name":"Mia","data":[[1,5.5],[2,6],[3,6.5]]}]}}}],"problems":[]},"outline":"Window \"Club\"\n  LineChart \"Running club\"\n    x axis NumberAxis \"Week\"\n    y axis NumberAxis \"km\"\n    series \"Ava\": (1, 4) (2, 4.5) (3, 5)\n    series \"Leo\": (1, 7) (3, 8)\n    series \"Mia\": (1, 5.5) (2, 6) (3, 6.5)"},{"name":"Test 2","stdin":"","files":{"runs.csv":"week,runner,km\n4,Zoe,10\n4,Ben,-\n5,Ben,-\n5,Zoe,11.25\n5,Kai,3\n6,Kai,3.5\n"},"expect":"","hidden":true,"events":[],"window":{"windows":[{"title":"Club","scene":{"root":{"n":1,"type":"LineChart","title":"Running club","xAxis":{"type":"NumberAxis","label":"Week","autoRanging":true,"lowerBound":0,"upperBound":100,"tickUnit":5,"forceZeroInRange":true,"minorTickVisible":true},"yAxis":{"type":"NumberAxis","label":"km","autoRanging":true,"lowerBound":0,"upperBound":100,"tickUnit":5,"forceZeroInRange":true,"minorTickVisible":true},"series":[{"name":"Kai","data":[[5,3],[6,3.5]]},{"name":"Zoe","data":[[4,10],[5,11.25]]}]}}}],"problems":[]},"outline":"Window \"Club\"\n  LineChart \"Running club\"\n    x axis NumberAxis \"Week\"\n    y axis NumberAxis \"km\"\n    series \"Kai\": (5, 3) (6, 3.5)\n    series \"Zoe\": (4, 10) (5, 11.25)"},{"name":"Test 3","stdin":"","files":{"runs.csv":"week,runner,km\n1,Noor,2\n"},"expect":"","hidden":true,"events":[],"window":{"windows":[{"title":"Club","scene":{"root":{"n":1,"type":"LineChart","title":"Running club","xAxis":{"type":"NumberAxis","label":"Week","autoRanging":true,"lowerBound":0,"upperBound":100,"tickUnit":5,"forceZeroInRange":true,"minorTickVisible":true},"yAxis":{"type":"NumberAxis","label":"km","autoRanging":true,"lowerBound":0,"upperBound":100,"tickUnit":5,"forceZeroInRange":true,"minorTickVisible":true},"series":[{"name":"Noor","data":[[1,2]]}]}}}],"problems":[]},"outline":"Window \"Club\"\n  LineChart \"Running club\"\n    x axis NumberAxis \"Week\"\n    y axis NumberAxis \"km\"\n    series \"Noor\": (1, 2)"}],"require":[],"forbid":[],"more":[{"task":"A park measures its young trees in centimeters. What does this program print?\n","kind":"predict","seed":"import java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport javafx.application.Application;\nimport javafx.scene.Scene;\nimport javafx.scene.chart.LineChart;\nimport javafx.scene.chart.NumberAxis;\nimport javafx.scene.chart.XYChart;\nimport javafx.stage.Stage;\n\npublic class Main extends Application {\n\n    @Override\n    public void start(Stage stage) {\n        String[] rows = {\"1,oak,30\", \"1,elm,25\", \"2,oak,34\", \"2,ash,-\", \"2,elm,29\"};\n        Map<String, XYChart.Series<Number, Number>> byTree = new HashMap<>();\n        for (String row : rows) {\n            String[] parts = row.split(\",\");\n            XYChart.Series<Number, Number> series = byTree.get(parts[1]);\n            if (series == null) {\n                series = new XYChart.Series<>();\n                series.setName(parts[1]);\n                byTree.put(parts[1], series);\n            }\n            if (!parts[2].equals(\"-\")) {\n                series.getData().add(new XYChart.Data<>(Integer.valueOf(parts[0]), Integer.valueOf(parts[2])));\n            }\n        }\n        List<String> trees = new ArrayList<>(byTree.keySet());\n        Collections.sort(trees);\n        LineChart<Number, Number> chart = new LineChart<>(new NumberAxis(), new NumberAxis());\n        for (String tree : trees) {\n            chart.getData().add(byTree.get(tree));\n            System.out.println(tree + \": \" + byTree.get(tree).getData().size());\n        }\n\n        stage.setScene(new Scene(chart));\n        stage.setTitle(\"Park\");\n        stage.show();\n    }\n\n    public static void main(String[] args) {\n        launch(Main.class);\n    }\n}\n","solution":"import java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport javafx.application.Application;\nimport javafx.scene.Scene;\nimport javafx.scene.chart.LineChart;\nimport javafx.scene.chart.NumberAxis;\nimport javafx.scene.chart.XYChart;\nimport javafx.stage.Stage;\n\npublic class Main extends Application {\n\n    @Override\n    public void start(Stage stage) {\n        String[] rows = {\"1,oak,30\", \"1,elm,25\", \"2,oak,34\", \"2,ash,-\", \"2,elm,29\"};\n        Map<String, XYChart.Series<Number, Number>> byTree = new HashMap<>();\n        for (String row : rows) {\n            String[] parts = row.split(\",\");\n            XYChart.Series<Number, Number> series = byTree.get(parts[1]);\n            if (series == null) {\n                series = new XYChart.Series<>();\n                series.setName(parts[1]);\n                byTree.put(parts[1], series);\n            }\n            if (!parts[2].equals(\"-\")) {\n                series.getData().add(new XYChart.Data<>(Integer.valueOf(parts[0]), Integer.valueOf(parts[2])));\n            }\n        }\n        List<String> trees = new ArrayList<>(byTree.keySet());\n        Collections.sort(trees);\n        LineChart<Number, Number> chart = new LineChart<>(new NumberAxis(), new NumberAxis());\n        for (String tree : trees) {\n            chart.getData().add(byTree.get(tree));\n            System.out.println(tree + \": \" + byTree.get(tree).getData().size());\n        }\n\n        stage.setScene(new Scene(chart));\n        stage.setTitle(\"Park\");\n        stage.show();\n    }\n\n    public static void main(String[] args) {\n        launch(Main.class);\n    }\n}\n","hints":["This program makes a tree's series before it checks for `-`, so every tree that has a row gets a series, even one without a single number.","The names are sorted before they're printed: ash, elm, oak. Count each tree's rows with a number."],"tests":[{"name":"Output","stdin":"","expect":"ash: 0\nelm: 2\noak: 2","hidden":false}],"require":[],"forbid":[],"lines":["ash: 0","elm: 2","oak: 2"]},{"task":"A town has three swimming pools, and counts each pool's visitors per week in the tab-separated file `pools.tsv`, one column per pool. A pool that was closed for a week has a `-`:\n\n```text\nweek\tNorth\tLake\tPark\n1\t310\t520\t-\n2\t295\t610\t140\n3\t-\t700\t180\n4\t350\t655\t210\n```\n\nWrite `start` so that the window, titled `Pools`, shows a line chart (the scene's root) with the title `Swimming pool visitors`, an auto-ranging x axis labeled `Week`, an auto-ranging y axis labeled `Visitors` and a visible legend. It has a series for each pool, named as in the header and in the header's order, with a point (week, visitors) for each week the pool was open, in the order of the file. The file may have any number of pools. The task card shows the window for the file above.\n","kind":"code","seed":"import java.nio.file.Files;\nimport java.nio.file.Paths;\nimport java.util.ArrayList;\nimport java.util.List;\nimport javafx.application.Application;\nimport javafx.scene.Scene;\nimport javafx.scene.chart.LineChart;\nimport javafx.scene.chart.NumberAxis;\nimport javafx.scene.chart.XYChart;\nimport javafx.stage.Stage;\n\npublic class Main extends Application {\n\n    @Override\n    public void start(Stage stage) throws Exception {\n        List<String> lines = Files.readAllLines(Paths.get(\"pools.tsv\"));\n\n        stage.setTitle(\"Pools\");\n        stage.show();\n    }\n\n    public static void main(String[] args) {\n        launch(Main.class);\n    }\n}\n","solution":"import java.nio.file.Files;\nimport java.nio.file.Paths;\nimport java.util.ArrayList;\nimport java.util.List;\nimport javafx.application.Application;\nimport javafx.scene.Scene;\nimport javafx.scene.chart.LineChart;\nimport javafx.scene.chart.NumberAxis;\nimport javafx.scene.chart.XYChart;\nimport javafx.stage.Stage;\n\npublic class Main extends Application {\n\n    @Override\n    public void start(Stage stage) throws Exception {\n        List<String> lines = Files.readAllLines(Paths.get(\"pools.tsv\"));\n        NumberAxis weeks = new NumberAxis();\n        weeks.setLabel(\"Week\");\n        NumberAxis visitors = new NumberAxis();\n        visitors.setLabel(\"Visitors\");\n        LineChart<Number, Number> chart = new LineChart<>(weeks, visitors);\n        chart.setTitle(\"Swimming pool visitors\");\n\n        String[] names = lines.get(0).split(\"\\t\");\n        List<XYChart.Series<Number, Number>> pools = new ArrayList<>();\n        for (int c = 1; c < names.length; c++) {\n            XYChart.Series<Number, Number> series = new XYChart.Series<>();\n            series.setName(names[c]);\n            pools.add(series);\n        }\n        for (int i = 1; i < lines.size(); i++) {\n            String[] parts = lines.get(i).split(\"\\t\");\n            int week = Integer.valueOf(parts[0]);\n            for (int c = 1; c < parts.length; c++) {\n                if (!parts[c].equals(\"-\")) {\n                    pools.get(c - 1).getData().add(new XYChart.Data<>(week, Integer.valueOf(parts[c])));\n                }\n            }\n        }\n        chart.getData().addAll(pools);\n\n        stage.setScene(new Scene(chart));\n        stage.setTitle(\"Pools\");\n        stage.show();\n    }\n\n    public static void main(String[] args) {\n        launch(Main.class);\n    }\n}\n","hints":["Split the header with `split(\"\\t\")`: its values from index 1 on are the pools' names. Make a series for each, in a list, before you read the rows.","In each row, column `c` (from 1 on) belongs to the series at index `c - 1`. Skip a `-`, and add the series to the chart with `chart.getData().addAll(...)` at the end."],"tests":[{"name":"Test 1","stdin":"","files":{"pools.tsv":"week\tNorth\tLake\tPark\n1\t310\t520\t-\n2\t295\t610\t140\n3\t-\t700\t180\n4\t350\t655\t210\n"},"expect":"","hidden":false,"events":[],"window":{"windows":[{"title":"Pools","scene":{"root":{"n":1,"type":"LineChart","title":"Swimming pool visitors","xAxis":{"type":"NumberAxis","label":"Week","autoRanging":true,"lowerBound":0,"upperBound":100,"tickUnit":5,"forceZeroInRange":true,"minorTickVisible":true},"yAxis":{"type":"NumberAxis","label":"Visitors","autoRanging":true,"lowerBound":0,"upperBound":100,"tickUnit":5,"forceZeroInRange":true,"minorTickVisible":true},"series":[{"name":"North","data":[[1,310],[2,295],[4,350]]},{"name":"Lake","data":[[1,520],[2,610],[3,700],[4,655]]},{"name":"Park","data":[[2,140],[3,180],[4,210]]}]}}}],"problems":[]},"outline":"Window \"Pools\"\n  LineChart \"Swimming pool visitors\"\n    x axis NumberAxis \"Week\"\n    y axis NumberAxis \"Visitors\"\n    series \"North\": (1, 310) (2, 295) (4, 350)\n    series \"Lake\": (1, 520) (2, 610) (3, 700) (4, 655)\n    series \"Park\": (2, 140) (3, 180) (4, 210)"},{"name":"Test 2","stdin":"","files":{"pools.tsv":"week\tHill\tRiver\n7\t-\t88\n8\t120\t-\n9\t135\t95\n"},"expect":"","hidden":true,"events":[],"window":{"windows":[{"title":"Pools","scene":{"root":{"n":1,"type":"LineChart","title":"Swimming pool visitors","xAxis":{"type":"NumberAxis","label":"Week","autoRanging":true,"lowerBound":0,"upperBound":100,"tickUnit":5,"forceZeroInRange":true,"minorTickVisible":true},"yAxis":{"type":"NumberAxis","label":"Visitors","autoRanging":true,"lowerBound":0,"upperBound":100,"tickUnit":5,"forceZeroInRange":true,"minorTickVisible":true},"series":[{"name":"Hill","data":[[8,120],[9,135]]},{"name":"River","data":[[7,88],[9,95]]}]}}}],"problems":[]},"outline":"Window \"Pools\"\n  LineChart \"Swimming pool visitors\"\n    x axis NumberAxis \"Week\"\n    y axis NumberAxis \"Visitors\"\n    series \"Hill\": (8, 120) (9, 135)\n    series \"River\": (7, 88) (9, 95)"},{"name":"Test 3","stdin":"","files":{"pools.tsv":"week\tA\tB\tC\tD\n1\t1\t2\t3\t4\n"},"expect":"","hidden":true,"events":[],"window":{"windows":[{"title":"Pools","scene":{"root":{"n":1,"type":"LineChart","title":"Swimming pool visitors","xAxis":{"type":"NumberAxis","label":"Week","autoRanging":true,"lowerBound":0,"upperBound":100,"tickUnit":5,"forceZeroInRange":true,"minorTickVisible":true},"yAxis":{"type":"NumberAxis","label":"Visitors","autoRanging":true,"lowerBound":0,"upperBound":100,"tickUnit":5,"forceZeroInRange":true,"minorTickVisible":true},"series":[{"name":"A","data":[[1,1]]},{"name":"B","data":[[1,2]]},{"name":"C","data":[[1,3]]},{"name":"D","data":[[1,4]]}]}}}],"problems":[]},"outline":"Window \"Pools\"\n  LineChart \"Swimming pool visitors\"\n    x axis NumberAxis \"Week\"\n    y axis NumberAxis \"Visitors\"\n    series \"A\": (1, 1)\n    series \"B\": (1, 2)\n    series \"C\": (1, 3)\n    series \"D\": (1, 4)"}],"require":[],"forbid":[]}]}