{"id":36418618,"url":"https://github.com/zavtech/morpheus-viz","last_synced_at":"2026-01-11T17:01:30.727Z","repository":{"id":57730019,"uuid":"95721263","full_name":"zavtech/morpheus-viz","owner":"zavtech","description":"The visualisation components of the Morpheus framework","archived":false,"fork":false,"pushed_at":"2018-01-06T16:33:23.000Z","size":1216,"stargazers_count":7,"open_issues_count":4,"forks_count":4,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-07-07T13:47:19.172Z","etag":null,"topics":["chart","charting-library","charts","dataframe","jfreechart","plotting","reporting","visualization"],"latest_commit_sha":null,"homepage":"http://www.zavtech.com/morpheus/docs/viz/charts/overview/","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/zavtech.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-06-28T23:56:40.000Z","updated_at":"2024-01-27T19:18:15.000Z","dependencies_parsed_at":"2022-09-07T20:23:47.445Z","dependency_job_id":null,"html_url":"https://github.com/zavtech/morpheus-viz","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/zavtech/morpheus-viz","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zavtech%2Fmorpheus-viz","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zavtech%2Fmorpheus-viz/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zavtech%2Fmorpheus-viz/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zavtech%2Fmorpheus-viz/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/zavtech","download_url":"https://codeload.github.com/zavtech/morpheus-viz/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zavtech%2Fmorpheus-viz/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28314259,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-11T14:58:17.114Z","status":"ssl_error","status_checked_at":"2026-01-11T14:55:53.580Z","response_time":60,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["chart","charting-library","charts","dataframe","jfreechart","plotting","reporting","visualization"],"created_at":"2026-01-11T17:01:29.921Z","updated_at":"2026-01-11T17:01:30.716Z","avatar_url":"https://github.com/zavtech.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"## Morpheus Charting API\n\nThe Morpheus visualization library defines a **simple chart abstraction API** with adapters supporting both \n[JFreeChart](http://www.jfree.org/jfreechart/) as well as [Google Charts](https://developers.google.com/chart/) (with others\nto follow by popular demand). This design makes it possible to generate interactive [Java Swing](https://en.wikipedia.org/wiki/Swing_(Java)) \ncharts as well as HTML5 browser based charts via the same API. By default, the framework is configured to use the JFreeChart \nadapter, however this can be re-configured on a global basis by calling either `htmlMode()` or `swingMode()` as shown \nbelow.\n\n```java\n//Switch chart adapter to HTML mode globally \nChart.create().htmlMode();\n\n//Switch chart adapter to SWING mode globally \nChart.create().swingMode();\n```\n\nIt is also possible to operate in **mixed mode** from within the the same application rather than switching the adapter \nglobally. By explicitly calling `asHtml()` or `asSwing()` prior to invoking one of the plotting functions on the `Chart`\ninterface, Html and Swing based charts can be generated from within the same application as shown below.  \n\n```java\n//Create chart using SWING adapter\nChart.create().asSwing().withLinePlot(frame, chart -\u003e {\n    chart.title().withText(\"Chart Title Goes Here...\");\n    chart.legend().on().bottom();\n    chart.show();\n});\n\n//Create chart using HTML adapter\nChart.create().asHtml().withLinePlot(frame, chart -\u003e {\n    chart.title().withText(\"Chart Title Goes Here...\");\n    chart.legend().on().bottom();\n    chart.show();\n});\n```\n\nThe following sections demonstrate how to use the Morpheus charting API, and provide various examples of what kind of \ncharts are supported. The illustrations below are PNG files generated using the JFreeChart adapter, however a [gallery](http://www.zavtech.com/morpheus/docs/viz/charts/gallery1/) \nof the same plots generated via the **Google adapter** show just how similar the plots from the two implementations are. \nWhile most of the functionality exposed by the Morpheus Charting API are supported by both adapters, there are some gaps \nin the Google adapter which are documented below.\n\n## Line Charts\n\n### Single Series\n\nConsider the `DataFrame` below with dimensions `1000x1` which has a row axis of type `LocalDate` and 1 column of \ndouble precision values representing the cumulative sum of an `Array` of normally distributed random values. The first \n10 rows of this frame are printed below.\n\n```java\nimport com.zavtech.morpheus.array.Array;\nimport com.zavtech.morpheus.frame.DataFrame;\n\nint rowCount = 1000;\nLocalDate startDate = LocalDate.of(2013, 1, 1);\nRange\u003cLocalDate\u003e dates = Range.of(0, rowCount).map(startDate::plusDays);\nDataFrame\u003cLocalDate,String\u003e frame = DataFrame.of(dates, String.class, columns -\u003e {\n    columns.add(\"A\", Array.randn(rowCount).cumSum());\n});\n```\n\n\u003cdiv class=\"frame\"\u003e\u003cpre class=\"frame\"\u003e\n   Index     |       A       |\n------------------------------\n 2013-01-01  |   0.58638321  |\n 2013-01-02  |  -0.44176283  |\n 2013-01-03  |  -0.07187819  |\n 2013-01-04  |  -1.31157143  |\n 2013-01-05  |  -1.69375864  |\n 2013-01-06  |  -2.23840733  |\n 2013-01-07  |  -2.42279587  |\n 2013-01-08  |  -2.95871372  |\n 2013-01-09  |  -3.63748847  |\n \u003c/pre\u003e\u003c/div\u003e\n\nTo generate an line plot of this series, we can use the dates in the `DataFrame` row axis as the **x-values**, and \nthe numeric values in column `A` as the range or **y-values**. The `withLines()` method on the `ChartFactory` interface\nexpects the `DataFrame` containing the data to plot, and a `Consumer` which is used to configure various features of \nthe chart. In the example below, we simply display the chart with no further customization by calling `show()`.\n\n```java\nChart.create().withLinePlot(frame, chart -\u003e {\n    chart.show();\n});\n```\n\n\u003cp align=\"center\"\u003e\n    \u003cimg class=\"chart\" src=\"http://www.zavtech.com/morpheus/docs/images/charts/chart-basic-1.png\"/\u003e\n\u003c/p\u003e\n\n### Multiple Series\n\nA common scenario is to generate an line plot where the domain or **x-axis** is based on data in a specific column of the frame \nrather than the **row axis** as in the previous example. This can be done by passing the label of the column to use for \nthe domain axis to the `withLines()` method. In the example below, we create a similar dataset to above but with dimensions \n`1000x5`, and in this case the row axis is simply a sequence of integers with the dates included as a column keyed as `DataDate`. \n\n```java\nint rowCount = 1000;\nLocalDate startDate = LocalDate.of(2013, 1, 1);\nRange\u003cInteger\u003e rowKeys = Range.of(0, rowCount);\nRange\u003cLocalDate\u003e dates = rowKeys.map(startDate::plusDays);\nDataFrame\u003cInteger,String\u003e frame = DataFrame.of(rowKeys, String.class, columns -\u003e {\n    columns.add(\"DataDate\", dates);\n    Stream.of(\"A\", \"B\", \"C\", \"D\").forEach(label -\u003e {\n        columns.add(label, Array.randn(rowCount).cumSum());\n    });\n});\n```\n\n\u003cdiv class=\"frame\"\u003e\u003cpre class=\"frame\"\u003e\n Index  |   DataDate   |      A       |       B       |       C       |       D       |\n---------------------------------------------------------------------------------------\n     0  |  2013-01-01  |  1.29439793  |  -0.91248479  |  -0.51141634  |   0.45271667  |\n     1  |  2013-01-02  |  1.04502117  |   -1.5841936  |   1.04050209  |    0.3773374  |\n     2  |  2013-01-03  |   3.0924616  |  -2.35489228  |   3.35960923  |  -1.52130364  |\n     3  |  2013-01-04  |  2.58882443  |  -1.80476051  |   3.79982322  |  -2.03083571  |\n     4  |  2013-01-05  |  2.95265199  |  -3.22752153  |   4.92200533  |  -0.05268766  |\n     5  |  2013-01-06  |  2.54600084  |  -3.46228629  |   3.34627638  |  -0.73170641  |\n     6  |  2013-01-07  |  2.11506239  |  -3.34459359  |   3.68209019  |  -0.80961531  |\n     7  |  2013-01-08  |   3.1889199  |  -3.75034967  |    3.7681561  |    0.0668972  |\n     8  |  2013-01-09  |  4.83991797  |  -3.21969887  |   2.99041415  |   0.89681662  |\n     9  |  2013-01-10  |  4.33025037  |  -2.65846363  |   2.44799574  |   0.62896343  |\n\u003c/div\u003e\u003c/pre\u003e\n\nGiven that we are plotting multiple series, we also turn on the chart legend, and place it at the bottom of the chart.\n\n```java\nChart.create().withLinePlot(frame, \"DataDate\", chart -\u003e {\n    chart.legend().on().bottom();\n    chart.show();\n});\n```\n\u003cp align=\"center\"\u003e\n    \u003cimg class=\"chart\" src=\"http://www.zavtech.com/morpheus/docs/images/charts/chart-basic-2.png\"/\u003e\n\u003c/p\u003e\n\n### Series Specific Style\n\nExtending the prior example, below we generate the same `1000x5` dataset but add an additional column post construction \nwhich represents the sum of values in each row. In addition, we add **text** to the chart in the form of a **title**, \n**subtitle**, **x-axis** and **y-axis** label. Finally, we explicitly configure the chart to render the `Total` column \nin **black**, and using a **thicker** point size to make it distinguishable from the other series.\n\n```java\nint rowCount = 1000;\nLocalDate startDate = LocalDate.of(2013, 1, 1);\nRange\u003cInteger\u003e rowKeys = Range.of(0, rowCount);\nRange\u003cLocalDate\u003e dates = rowKeys.map(startDate::plusDays);\nDataFrame\u003cInteger,String\u003e frame = DataFrame.of(rowKeys, String.class, columns -\u003e {\n    columns.add(\"DataDate\", dates);\n    Stream.of(\"A\", \"B\", \"C\", \"D\").forEach(label -\u003e {\n        columns.add(label, Array.randn(rowCount).cumSum());\n    });\n});\n\n//Add a total column that sumns A+B+C+D\nframe.cols().add(\"Total\", Double.class, v -\u003e v.row().stats().sum());\n```\n\n\u003cdiv class=\"frame\"\u003e\u003cpre class=\"frame\"\u003e\n Index  |   DataDate   |      A       |       B       |       C       |       D       |     Total     |\n-------------------------------------------------------------------------------------------------------\n     0  |  2013-01-01  |  1.25934218  |   0.69220501  |  -0.31238595  |  -0.56357062  |   1.07559062  |\n     1  |  2013-01-02  |  2.29908995  |   0.17146519  |  -1.60587813  |   0.05049886  |   0.91517587  |\n     2  |  2013-01-03  |  2.28895812  |   0.19584665  |  -1.81601295  |  -0.93573356  |  -0.26694175  |\n     3  |  2013-01-04  |   0.5587514  |   1.73915178  |   0.29149944  |  -0.29126603  |   2.29813659  |\n     4  |  2013-01-05  |  2.11189209  |   0.72883374  |   0.22025859  |  -0.15000083  |   2.91098359  |\n     5  |  2013-01-06  |  2.26632523  |  -0.65280247  |  -0.48525074  |   1.66929384  |   2.79756585  |\n     6  |  2013-01-07  |  3.32634531  |  -0.37416553  |  -0.71419742  |   0.73102638  |   2.96900874  |\n     7  |  2013-01-08  |  2.85345213  |   1.50372178  |   1.14531164  |   1.84564308  |   7.34812862  |\n     8  |  2013-01-09  |  1.70266008  |   1.20891251  |   0.87620806  |  -1.10380341  |   2.68397724  |\n     9  |  2013-01-10  |  1.90697991  |   2.20887263  |  -1.22202884  |  -0.30028665  |   2.59353704  |\n \u003c/div\u003e\u003c/pre\u003e\n\n```java\nChart.create().withLinePlot(frame, \"DataDate\", chart -\u003e {\n    chart.title().withText(\"Example Time Series Chart\");\n    chart.subtitle().withText(\"Cumulative Sum of Random Normal Data\");\n    chart.plot().axes().domain().label().withText(\"Data Date\");\n    chart.plot().axes().range(0).label().withText(\"Random Value\");\n    chart.plot().style(\"Total\").withLineWidth(2f).withColor(Color.BLACK);\n    chart.legend().on();\n    chart.show();\n});\n```\n\u003cp align=\"center\"\u003e\n    \u003cimg class=\"chart\" src=\"http://www.zavtech.com/morpheus/docs/images/charts/chart-basic-3.png\"/\u003e\n\u003c/p\u003e\n\n### Multiple Axis\n\nIt is often useful to be able to plot multiple series on the same chart even when those series happen to have very\ndifferent scales. This is supported by the Morpheus charting API in that is is possible to add many `DataFrames` to\na single chart, and each frame can be found to its own range axis. \n\nBelow we create a frame similar to prior examples, however we impose a larger scale on series `C` and `D` compared \nwith `A` and `B`. If we plotted this as a single frame, the scale of `C` and `D` would dominate and it would be hard \nto see changes in `A` and `B`. In order to address this, we filter the frame into 2 sets of columns, and bind the \nsecond `DataFrame` to a secondary axis via the `setRangeAxis()` method. The arguments to this method are the dataset \nindex, and the index of the range axis to bind it to. In principal you can bind as many frames to as many axis, however \nthis would rapidly become hard to read.\n\n```java\nint rowCount = 1000;\nRange\u003cInteger\u003e rowKeys = Range.of(0, rowCount);\nDataFrame\u003cInteger,String\u003e frame = DataFrame.of(rowKeys, String.class, columns -\u003e {\n    Stream.of(\"A\", \"B\").forEach(c -\u003e columns.add(c, Array.randn(rowCount).cumSum()));\n    Stream.of(\"C\", \"D\").forEach(c -\u003e {\n        columns.add(c, Array.randn(rowCount).mapToDoubles(v -\u003e v.getDouble() * 100).cumSum());\n    });\n});\n\nChart.create().withLinePlot(frame.cols().select(\"A\", \"B\"), chart -\u003e {\n    chart.plot().data().add(frame.cols().select(\"C\", \"D\"));\n    chart.plot().data().setRangeAxis(1, 1);\n    chart.title().withText(\"Time Series Chart - Multiple Axis\");\n    chart.subtitle().withText(\"Cumulative Sum of Random Normal Data\");\n    chart.plot().axes().domain().label().withText(\"Data Date\");\n    chart.plot().axes().range(0).label().withText(\"Random Value-1\");\n    chart.plot().axes().range(1).label().withText(\"Random Value-2\");\n    chart.legend().on();\n    chart.show();\n});\n```\n\n\u003cp align=\"center\"\u003e\n    \u003cimg class=\"chart\" src=\"http://www.zavtech.com/morpheus/docs/images/charts/chart-basic-4.png\"/\u003e\n\u003c/p\u003e\n\n### Multiple Renderers\n\nIn the prior example, the idea that multiple `DataFrames` can be added to a chart was introduced in order to demonstrate \nhow to bind different data series to different axis. This same idea can be used to bind different rendering strategies\nto different series.\n\nIn the example below, we generate a `DataFrame` with dimensions `20x6` and then plot this data by filtering the frame\ninto 3 pairs of columns and use different rendering strategies to draw each pair of series. The first frame containing \ncolumns `A` and `B` is plotted with straight **lines** and **shapes** rendered at each datum. The second frame containing \ncolumns `C` and `D` is plotted with a **spline** renderer thereby yielding the smooth trajectory for these series. Finally, \nthe third frame is rendered with **dashed lines** and no shapes at the datum points.\n\n```java\nint rowCount = 20;\nRange\u003cInteger\u003e rowKeys = Range.of(0, rowCount);\nDataFrame\u003cInteger,String\u003e frame = DataFrame.of(rowKeys, String.class, columns -\u003e {\n    Stream.of(\"A\", \"B\", \"C\", \"D\", \"E\", \"F\").forEach(label -\u003e {\n        columns.add(label, Array.randn(rowCount).cumSum());\n    });\n});\n\nChart.create().withLinePlot(frame.cols().select(\"A\", \"B\"), chart -\u003e {\n    chart.plot().data().add(frame.cols().select(\"C\", \"D\"));\n    chart.plot().data().add(frame.cols().select(\"E\", \"F\"));\n    chart.plot().render(0).withLines(true, false);\n    chart.plot().render(1).withSpline(false, false);\n    chart.plot().render(2).withLines(false, true);\n    chart.title().withText(\"Time Series Chart - Multiple Renderers\");\n    chart.subtitle().withText(\"Cumulative Sum of Random Normal Data\");\n    chart.plot().axes().domain().label().withText(\"Data Date\");\n    chart.plot().axes().range(0).label().withText(\"Random Value\");\n    chart.legend().on();\n    chart.show();\n});\n```\n\n\u003cp align=\"center\"\u003e\n    \u003cimg class=\"chart\" src=\"http://www.zavtech.com/morpheus/docs/images/charts/chart-basic-5.png\"/\u003e\n\u003c/p\u003e\n\n## Simple Bar Charts\n\n### Discrete Domain Axis\n\nTo create bar charts with Morpheus we use an identical API as in the prior examples, but in this case we simply make a \ncall to `Chart.ofBars()` instead of `Chart.ofLines()`. Plotting a `DataFrame` where the **domain axis** is initialized \nfrom the **row axis** keys or alternatively a column of data in the frame works the same way. In the example below we \ngenerate a bar chart based on a **discrete** or **categorical** domain axis using the frame row axis of type `Year`.\nGenerating bar charts based on **continuous** data is also possible and is demonstrated in the next example.\n\n```java\nRange\u003cYear\u003e years = Range.of(2000, 2006).map(Year::of);\nDataFrame\u003cYear,String\u003e data = DataFrame.of(years, String.class, columns -\u003e {\n    Stream.of(\"A\", \"B\", \"C\", \"D\").forEach(label -\u003e {\n        columns.add(label, Array.of(Double.class, 6).applyDoubles(v -\u003e Math.random()).cumSum());\n    });\n});\n\nChart.create().withBarPlot(data, false, chart -\u003e {\n    chart.plot().axes().domain().label().withText(\"Year\");\n    chart.plot().axes().range(0).label().withText(\"Random Value\");\n    chart.title().withText(\"Bar Chart - Categorical Domain Axis\");\n    chart.subtitle().withText(\"Cumulative Sum of Random Uniform Data\");\n    chart.legend().on();\n    chart.show();\n});\n```\n\n\u003cp align=\"center\"\u003e\n    \u003cimg class=\"chart\" src=\"http://www.zavtech.com/morpheus/docs/images/charts/chart-bars-1.png\"/\u003e\n\u003c/p\u003e\n\nSwitching to a horizontal orientation can be done by simply calling the `orient().horizontal()` method as shown below.\n\n```java\nChart.create().withBarPlot(data, false, chart -\u003e {\n    chart.plot().orient().horizontal();\n    chart.plot().axes().domain().label().withText(\"Year\");\n    chart.plot().axes().range(0).label().withText(\"Random Value\");\n    chart.title().withText(\"Bar Chart - Categorical Domain Axis\");\n    chart.subtitle().withText(\"Cumulative Sum of Random Uniform Data\");\n    chart.legend().on();\n    chart.show();\n});\n```\n\n\u003cp align=\"center\"\u003e\n    \u003cimg class=\"chart\" src=\"http://www.zavtech.com/morpheus/docs/images/charts/chart-bars-2.png\"/\u003e\n\u003c/p\u003e\n\n### Continuous Domain Axis\n\nBar charts are often used to display **categorical** or **discrete** data, however there are scenarios where a \n**continuous** variable in the **domain axis** is appropriate. In such cases, the question is how wide to make the bars? \nIn a discrete variable bar chart, the width of the bars is sized so that all content fits on the plot, so it is a \nfunction of how many discrete observations and series exist in the data. When creating a bar chart with a continuous \nvariable in the domain, we need to be explicit about how wide we want to make the bars, otherwise they will get \nrepresented as single vertical line.\n\nConsider the `DataFrame` below with dimensions of `20x1` and a row axis of type `LocalDateTime` where the row key \ninterval is 10 minutes wide. The data series `A` is simply initialized to the sum of a randomly generated array of \nvalues, and the first 10 rows of this frame are shown below.\n\n```java\nint rowCount = 20;\nLocalDateTime start = LocalDateTime.of(2014, 1, 1, 8, 30);\nRange\u003cLocalDateTime\u003e rowKeys = Range.of(0, rowCount).map(i -\u003e start.plusMinutes(i * 10));\nDataFrame\u003cLocalDateTime,String\u003e frame = DataFrame.of(rowKeys, String.class, columns -\u003e {\n    columns.add(\"A\", Array.of(Double.class, rowCount).applyDoubles(v -\u003e Math.random()).cumSum());\n});\n```\n\n\u003cdiv class=\"frame\"\u003e\u003cpre class=\"frame\"\u003e\n        Index         |      A       |\n--------------------------------------\n 2014-01-01T08:30:00  |  0.27653233  |\n 2014-01-01T08:40:00  |  0.63501106  |\n 2014-01-01T08:50:00  |  1.19265518  |\n 2014-01-01T09:00:00  |   1.9021335  |\n 2014-01-01T09:10:00  |  2.32102833  |\n 2014-01-01T09:20:00  |  3.02188896  |\n 2014-01-01T09:30:00  |  3.08679679  |\n 2014-01-01T09:40:00  |  3.43408424  |\n 2014-01-01T09:50:00  |  4.35521882  |\n 2014-01-01T10:00:00  |  5.13252283  |\n\u003c/pre\u003e\u003c/div\u003e\n\nIf we simply plot this as a bar chart in the usual fashion as per the code below, we end up with a plot where each\nobservation is represented by a vertical bar at each point in time that is 1 pixel wide. This may indeed be the result\none desires, but given that this `DataFrame` most likely represents some cumulative observations for each 10 minute\ninterval, it would be nice to explicitly make the bars 10 minutes wide.\n\n```java\nChart.create().withBarPlot(frame, false, chart -\u003e {\n    chart.plot().axes().domain().label().withText(\"Year\");\n    chart.plot().axes().range(0).label().withText(\"Random Value\");\n    chart.title().withText(\"Bar Chart - Continuous Domain Axis\");\n    chart.subtitle().withText(\"Cumulative Sum of Random Uniform Data\");\n    chart.legend().on();\n    chart.show();\n});\n```\n\n\u003cp align=\"center\"\u003e\n    \u003cimg class=\"chart\" src=\"http://www.zavtech.com/morpheus/docs/images/charts/chart-bars-3.png\"/\u003e\n\u003c/p\u003e\n\nThankfully this can be achieved very easily through the API, namely via two methods on the `ChartModel` interface\ncalled `withLowerDomainInterval()` and `withUpperDomainInterval()` which each accept a lambda expression with an\nadjustment function. In this particular example, let us assume that the instantaneous times in the `DataFrame` row\naxis are the end of the measurement intervals, so the values in series `A` represent the aggregate values for the\nprior 10 minutes. We therefore bind an adjustment function that subtracts 10 minutes from row keys as shown in the\ncode example below, with a call to `withLowerDomainInterval()`.\n\n```java\nChart.create().withBarPlot(frame, false, chart -\u003e {\n    chart.plot().data().at(0).withLowerDomainInterval(t -\u003e t.minusMinutes(10));\n    chart.plot().axes().domain().label().withText(\"Year\");\n    chart.plot().axes().range(0).label().withText(\"Random Value\");\n    chart.title().withText(\"Bar Chart - Continuous Domain Axis\");\n    chart.subtitle().withText(\"Cumulative Sum of Random Uniform Data\");\n    chart.legend().on();\n    chart.show();\n});\n```\n\n\u003cp align=\"center\"\u003e\n    \u003cimg class=\"chart\" src=\"http://www.zavtech.com/morpheus/docs/images/charts/chart-bars-4.png\"/\u003e\n\u003c/p\u003e\n\nThe flexibility of the `withLowerDomainInterval()` and `withUpperDomainInterval()` methods means lambdas with any\ndegree of complexity can be used to change the width of bars, and even make variable width bars on the same plot.\n\n## Stacked Bar Charts\n\n### Discrete Domain Axis\n\nStacked bar charts are useful for visualizing the decomposition of some quantity into various sub-categories. For \nexample, it might be useful to decompose the total revenue of a company into various product and service categories\nto visualize how well balanced revenue sources are. Creating a stacked bar plot with the Morpheus API simply boils\ndown to passing `true` for the second argument of the `withBarPlot()` function. To illustrate, consider a `DataFrame`\nwith 10 rows and 5 columns of randomly initialized values which can be generated as follows.\n\n```java\nRange\u003cYear\u003e years = Range.of(2000, 2010).map(Year::of);\nDataFrame\u003cYear,String\u003e frame = DataFrame.of(years, String.class, columns -\u003e {\n    Stream.of(\"A\", \"B\", \"C\", \"D\", \"E\", \"F\", \"G\").forEach(label -\u003e {\n        columns.add(label, Array.randn(10).applyDoubles(v -\u003e Math.abs(v.getDouble())).cumSum());\n    });\n});\n```\n\n\u003cdiv class=\"frame\"\u003e\u003cpre class=\"frame\"\u003e\n Index  |      A       |       B       |       C       |      D       |      E       |      F       |      G       |\n--------------------------------------------------------------------------------------------------------------------\n  2000  |  1.31995055  |   0.53881463  |    0.3279245  |  0.11259494  |  1.67395423  |  0.18586162  |  0.87989445  |\n  2001  |  1.72056405  |   1.68922095  |   0.49067667  |  0.23015735  |  2.13037774  |  0.44835517  |  1.36713607  |\n  2002  |  2.79809076  |   3.94390632  |   3.30168445  |  1.29757044  |  3.06911301  |   0.9306128  |  1.80211665  |\n  2003  |  3.59071864  |   5.88855153  |   4.37231856  |  2.52348568  |   4.7653336  |  0.94635161  |  3.99710948  |\n  2004  |  6.08102384  |    7.7523729  |    4.5286778  |  2.59847512  |  4.85460383  |  2.42353332  |  5.06462412  |\n  2005  |  6.55934094  |   9.17683641  |   6.23641685  |  3.34769981  |  4.96006758  |   3.0262002  |  5.14085585  |\n  2006  |  7.60618601  |   9.59875411  |   6.42311354  |  3.58823615  |  5.81808152  |  3.59782465  |  6.08934143  |\n  2007  |  8.50669158  |  10.79619646  |   8.76745963  |  4.71219626  |  6.06259072  |   4.7197505  |  6.80808702  |\n  2008  |   9.3043879  |  10.98733186  |   9.91013585  |  5.78904589  |  7.63390658  |  6.01700338  |  8.27038276  |\n  2009  |  9.40761316  |  11.53533956  |  10.32051078  |  5.85462161  |  8.54673512  |   6.1083706  |  9.53989261  |\n\u003c/pre\u003e\u003c/div\u003e\n\nWe can plot this data using stacked bars with the following code: \n\n```java\nChart.create().withBarPlot(frame, true, chart -\u003e {\n    chart.plot().axes().domain().label().withText(\"Year\");\n    chart.plot().axes().range(0).label().withText(\"Random Value\");\n    chart.title().withText(\"Stacked Bar Chart - Categorical Domain Axis\");\n    chart.subtitle().withText(\"Cumulative Sum of Random Uniform Data\");\n    chart.legend().on();\n    chart.show();\n});\n```\n\u003cp align=\"center\"\u003e\n    \u003cimg class=\"chart\" src=\"http://www.zavtech.com/morpheus/docs/images/charts/chart-stacked-bars-1.png\"/\u003e\n\u003c/p\u003e\n\nSwitching to a horizontal orientation can be done by simply calling the `orient().horizontal()` method as shown below.\n\n```java\nChart.create().withBarPlot(frame, true, chart -\u003e {\n    chart.plot().axes().domain().label().withText(\"Year\");\n    chart.plot().axes().range(0).label().withText(\"Random Value\");\n    chart.plot().orient().horizontal();\n    chart.title().withText(\"Stacked Bar Chart - Categorical Domain Axis\");\n    chart.subtitle().withText(\"Cumulative Sum of Random Uniform Data\");\n    chart.legend().on();\n    chart.show();\n});\n```\n\n\u003cp align=\"center\"\u003e\n    \u003cimg class=\"chart\" src=\"http://www.zavtech.com/morpheus/docs/images/charts/chart-stacked-bars-2.png\"/\u003e\n\u003c/p\u003e\n\n### Continuous Domain Axis\n\nStacked bar charts involving a continuous domain axis are also supported, much the same way as in the simple bar plot example\ndiscussed earlier. In order to control the **width of the bars**, it is necessary to inject a function that expresses the \ndomain interval for each datum in the domain. The bar width can be tailored by applying either a lower internal, upper interval\nor both to each observation in the dataset. In the example below we create a `DataFrame` with datetimes to serve as the domain, \nand then proceed to make the bars 10 minutes wide by injecting a **lower** domain interval function:\n\n```java\nint rowCount = 40;\nLocalDateTime start = LocalDateTime.of(2014, 1, 1, 8, 30);\nRange\u003cLocalDateTime\u003e rowKeys = Range.of(0, rowCount).map(i -\u003e start.plusMinutes(i * 10));\nDataFrame\u003cLocalDateTime,String\u003e frame = DataFrame.of(rowKeys, String.class, columns -\u003e {\n    Stream.of(\"A\", \"B\", \"C\", \"D\", \"E\", \"F\").forEach(label -\u003e {\n        columns.add(label, Array.randn(40, 1, 5).cumSum());\n    });\n});\n\nChart.create().withBarPlot(frame, true, chart -\u003e {\n    chart.plot().data().at(0).withLowerDomainInterval(t -\u003e t.minusMinutes(10));\n    chart.plot().axes().domain().label().withText(\"Year\");\n    chart.plot().axes().range(0).label().withText(\"Random Value\");\n    chart.title().withText(\"Stacked Bar Chart - Continuous Domain Axis\");\n    chart.subtitle().withText(\"Random Uniform Data\");\n    chart.legend().on();\n    chart.show();\n});\n```\n\n\u003cp align=\"center\"\u003e\n    \u003cimg class=\"chart\" src=\"http://www.zavtech.com/morpheus/docs/images/charts/chart-stacked-bars-3.png\"/\u003e\n\u003c/p\u003e\n\n**Compatibility Note**: The Google Chart adapter **does not** support lower / upper domain interval functions as the underlying library\nonly supports the ability to specify inter-bar spacing. Bar plots created by the Google adapter that involve a continuous \ndomain axis, stacked or otherwise, will by default render bars with no spacing between them, which for the most part seems \nlike an appropriate strategy for most scenarios. Calling `withLowerDomainInterval()` or `withUpperDomainInterval()` with the\nGoogle adapter will not have any effect on the output. Future support for more customizable HTML charting libraries may \naddress this limitation.\n\n## Histograms\n\n### Single Distribution\n\nThe Morpheus Charting API includes some convenience functions to plot frequency distributions of column data in a \n`DataFrame`. In the example below we generate a single column `1000000x1` frame of data randomly generated from a \nstandard normal distribution. Calling `withHistPlot()` and indicating the number of bins, 50 in this case, yields\nthe plot below.\n\n```java\nint recordCount = 1000000;\nDataFrame\u003cInteger,String\u003e frame = DataFrame.of(Range.of(0, recordCount), String.class, columns -\u003e {\n    columns.add(\"A\", Array.randn(recordCount));\n});\n\nChart.create().withHistPlot(frame, 50, chart -\u003e {\n    chart.title().withText(\"Normal Distribution\");\n    chart.subtitle().withText(\"Single Distribution\");\n    chart.show();\n});\n```\n\n\u003cp align=\"center\"\u003e\n    \u003cimg class=\"chart\" src=\"http://www.zavtech.com/morpheus/docs/images/charts/chart-hist-1.png\"/\u003e\n\u003c/p\u003e\n\n### Multiple Distributions\n\nA slight extension of the prior example is to consider a frequency distribution involving multiple series. In this case we \ncreate a `DataFrame` with 4 columns, where each column of 1 million observations is initialized from a normal distribution \nall with a mean of zero but different standard deviations. The different spreads of each series requires bins to be computed\non a per series basis, as it would not be optimal to create one set of bins across all series. We call the same `withHistPlot()`\nfunction as before, and in this example we generate 100 bins per series to yield the plot below.\n\n```java\nint recordCount = 1000000;\nDataFrame\u003cInteger,String\u003e frame = DataFrame.of(Range.of(0, recordCount), String.class, columns -\u003e {\n    columns.add(\"A\", Array.randn(recordCount, 0d, 1d));\n    columns.add(\"B\", Array.randn(recordCount, 0d, 0.8d));\n    columns.add(\"C\", Array.randn(recordCount, 0d, 0.6d));\n    columns.add(\"D\", Array.randn(recordCount, 0d, 0.4d));\n});\n\nChart.create().withHistPlot(frame, 100, chart -\u003e {\n    chart.title().withText(\"Normal Distribution\");\n    chart.subtitle().withText(\"Multiple Distributions\");\n    chart.legend().on();\n    chart.show();\n});\n```\n\n\u003cp align=\"center\"\u003e\n    \u003cimg class=\"chart\" src=\"http://www.zavtech.com/morpheus/docs/images/charts/chart-hist-2.png\"/\u003e\n\u003c/p\u003e\n\n### Fitted Distribution\n\n\u003cp align=\"center\"\u003e\n    \u003cimg class=\"chart\" src=\"http://www.zavtech.com/morpheus/docs/images/charts/chart-hist-3.png\"/\u003e\n\u003c/p\u003e\n\n## Scatter Charts\n\nTo demonstrate scatter plots, consider the random data generating function defined below, which yields an `nx2` \n`DataFrame` with a column of `X` and `Y` values based on a linear model with noise. The method expects the intercept \n(`alpha`), slope (`beta`), the standard deviation for the noise term (`sigma`), the step size for the domain interval\nand the number of data points to generate. The table below illustrates the basic structure of the data.\n\n```java\n/**\n * Returns a 2D sample dataset given a slope and intercept while adding white noise based on sigma.\n * @param alpha     the intercept term for data\n * @param beta      the slope term for for data\n * @param sigma     the standard deviation for noise\n * @param stepSize  the step size for domain variable\n * @param n         the size of the sample to generate\n * @return          the frame of XY values\n */\nprivate DataFrame\u003cInteger,String\u003e sample(double alpha, double beta, double sigma, double stepSize, int n) {\n    final Array\u003cDouble\u003e xValues = Array.of(Double.class, n).applyDoubles(v -\u003e 0d + v.index() * stepSize);\n    final Array\u003cDouble\u003e yValues = Array.of(Double.class, n).applyDoubles(v -\u003e {\n        final double yfit = alpha + beta * xValues.getDouble(v.index());\n        return new NormalDistribution(yfit, sigma).sample();\n    });\n    final Array\u003cInteger\u003e rowKeys = Range.of(0, n).toArray();\n    return DataFrame.of(rowKeys, String.class, columns -\u003e {\n        columns.add(\"X\", xValues);\n        columns.add(\"Y\", yValues);\n    }).cols().demean(true);\n}\n```\n\n\u003cdiv class=\"frame\"\u003e\u003cpre class=\"frame\"\u003e\n Index  |      X      |        Y        |\n-----------------------------------------\n     0  |  -249.7500  |  -134.80224667  |\n     1  |  -249.2500  |  -120.77578676  |\n     2  |  -248.7500  |  -119.33607918  |\n     3  |  -248.2500  |  -126.59963476  |\n     4  |  -247.7500  |  -131.08470325  |\n     5  |  -247.2500  |  -120.08991786  |\n     6  |  -246.7500  |  -124.15182976  |\n     7  |  -246.2500  |   -115.3617302  |\n     8  |  -245.7500  |   -119.0303664  |\n     9  |  -245.2500  |  -141.49936933  |\n\u003c/pre\u003e\u003c/div\u003e\n\nThe following examples leverage this data generating function to create various scatter plots.\n\n### Single Series\n\nThe example below generates a 1000 point sample dataset using our data generating function, and creates a scatter plot \nvia the `withScatterPlot()` method using the column labelled `X` for the domain axis. \n\n```java\nDataFrame\u003cInteger,String\u003e frame = scatter(4d, 0.5d, 20d, 0.5, 1000);\n\nChart.create().withScatterPlot(frame, false, \"X\", chart -\u003e {\n    chart.plot().axes().domain().label().withText(\"X-Value\");\n    chart.plot().axes().range(0).label().withText(\"Y-Value\");\n    chart.title().withText(\"Scatter Chart\");\n    chart.subtitle().withText(\"Single DataFrame, Single Series\");\n    chart.show();\n});\n```\n\n\u003cp align=\"center\"\u003e\n    \u003cimg class=\"chart\" src=\"http://www.zavtech.com/morpheus/docs/images/charts/chart-scatter-1.png\"/\u003e\n\u003c/p\u003e\n\n## Multiple Series\n\nBelow we combine 3 `DataFrames` using our data generating function and re-label the various `Y` columns `A`, \n`B` and `C` respectively so that they are distinct in the combined frame. We plot the result using `withScatterPlot()`\nin this case, and proceed to set specific colors for each series, and configure the `C` series to render with a \n**diamond** shape. Note that when we combine the frames, the 3 versions of the `X` column collapses into a single\ncolumn in the resulting frame based on a combine first rule. Since all 3 frames have identical x-values, this is \nimmaterial in this case.\n\n```java\nDataFrame\u003cInteger,String\u003e frame = DataFrame.concatColumns(\n    scatter(4d, 1d, 80d, 0.5, 500).cols().replaceKey(\"Y\", \"A\"),\n    scatter(4d, 6d, 100d, 0.5, 500).cols().replaceKey(\"Y\", \"B\"),\n    scatter(4d, 12d, 180d, 0.5, 500).cols().replaceKey(\"Y\", \"C\")\n);\n\nChart.create().withScatterPlot(frame, false, \"X\", chart -\u003e {\n    chart.plot().axes().domain().label().withText(\"X-Value\");\n    chart.plot().axes().range(0).label().withText(\"Y-Value\");\n    chart.plot().style(\"A\").withColor(new Color(255, 225, 25));\n    chart.plot().style(\"B\").withColor(new Color(0, 130, 200));\n    chart.plot().style(\"C\").withColor(new Color(245, 0, 48));\n    chart.title().withText(\"Scatter Chart\");\n    chart.subtitle().withText(\"Single DataFrame, Multiple Series, Custom Style\");\n    chart.legend().on();\n    chart.show();\n});\n```\n\n\u003cp align=\"center\"\u003e\n    \u003cimg class=\"chart\" src=\"http://www.zavtech.com/morpheus/docs/images/charts/chart-scatter-2.png\"/\u003e\n\u003c/p\u003e\n\n## Multiple Frames\n\nConsider a scenario where you have results from multiple experiments which you would like to combine in a scatter\nchart, and while the measurements share the same domain (i.e. x-dimension), the actual observations of `x` are\nnot the same across the samples. It is possible to create a single sparse `DataFrame` with all the results combined, \nhowever it is most likely to be more convenient to keep them separate. \n\nIn the example below we generate two `DataFrames`, each containing two series, and the x-values between the two frames \nare **not** coincident since we provide a different step size to our data generating function in each case. We can still \nplot both frames together with very little additional effort, namely by adding the second frame and instructing the chart \nto also render it with dots as follows.\n\n```java\nDataFrame\u003cInteger,String\u003e frame1 = DataFrame.concatColumns(\n    sample(4d, 1d, 80d, 0.5, 500).cols().replaceKey(\"Y\", \"A\"),\n    sample(4d, 3d, 100d, 0.5, 500).cols().replaceKey(\"Y\", \"B\")\n);\n\nDataFrame\u003cInteger,String\u003e frame2 = DataFrame.concatColumns(\n    sample(4d, 7d, 80d, 0.55, 600).cols().replaceKey(\"Y\", \"C\"),\n    sample(4d, -10d, 100d, 0.55, 600).cols().replaceKey(\"Y\", \"D\")\n);\n\nChart.create().withScatterPlot(frame1, false, \"X\", chart -\u003e {\n    chart.plot().data().add(frame2, \"X\");\n    chart.plot().render(1).withDots();\n    chart.plot().axes().domain().label().withText(\"X-Value\");\n    chart.plot().axes().range(0).label().withText(\"Y-Value\");\n    chart.title().withText(\"Scatter Chart\");\n    chart.subtitle().withText(\"Multiple DataFrames, Multiple Series\");\n    chart.legend().on();\n    chart.show();\n});\n```\n\n\u003cp align=\"center\"\u003e\n    \u003cimg class=\"chart\" src=\"http://www.zavtech.com/morpheus/docs/images/charts/chart-scatter-3.png\"/\u003e\n\u003c/p\u003e\n\n## Regression Charts\n\n### Single Frame\n\nThe Morpheus charting API supports fitting a linear trendline to a dataset based on an [Ordinary Least Squares](http://www.zavtech.com/morpheus/docs/regression/ols)\nregression model. Both the JFreeChart and Google adapters expose the model equation as a tooltip when hovering over\nany of the data points that make up the trendline. By default, the trend line is rendered in black with a thicker\nline stroke, however this can easily be adjusted using the standard series `style()` controller.\n\nIn the example below we use the same data generating function introduced earlier, and the only additional call is to the \n`trend()` function which accepts the key of the series to which a linear model should be fitted. The resulting plot is shown \nbelow with the standard style applied to the regression line.\n\n```java\nDataFrame\u003cInteger,String\u003e frame = scatter(4d, 1d, 80d, 0.5d, 1000);\nChart.create().withScatterPlot(frame, false, \"X\", chart -\u003e {\n    chart.plot().axes().domain().label().withText(\"X-Value\");\n    chart.plot().axes().range(0).label().withText(\"Y-Value\");\n    chart.plot().trend(\"Y\");\n    chart.title().withText(\"Regression Chart\");\n    chart.subtitle().withText(\"Single DataFrame, Single Series\");\n    chart.show();\n});\n```\n\n\u003cp align=\"center\"\u003e\n    \u003cimg class=\"chart\" src=\"http://www.zavtech.com/morpheus/docs/images/charts/chart-regress-1.png\"/\u003e\n\u003c/p\u003e\n\n### Multiple Frames\n\nFitting linear trend lines to multiple series or frames follows naturally through the same API as the single model\nexample. Below we generate two `DataFrames` each of which has a non-intersecting set of domain values. By calling the \n`trend()` function on the plot and passing the relevant series keys across the two frames, trend lines are generated as \nexpected. Once again, the model equations are exposed via tooltips when hovering over data points that make up the \nfitted line.\n\n```java\nDataFrame\u003cInteger,String\u003e frame1 = DataFrame.concatColumns(\n    scatter(4d, 1d, 80d, 0.5, 500).cols().replaceKey(\"Y\", \"A\"),\n    scatter(4d, 4d, 100d, 0.5, 500).cols().replaceKey(\"Y\", \"B\")\n);\n\nDataFrame\u003cInteger,String\u003e frame2 = DataFrame.concatColumns(\n    scatter(4d, -3d, 80d, 0.55, 600).cols().replaceKey(\"Y\", \"C\"),\n    scatter(4d, -10d, 100d, 0.45, 600).cols().replaceKey(\"Y\", \"D\")\n);\n\nChart.create().withScatterPlot(frame1, false, \"X\", chart -\u003e {\n    chart.plot().\u003cString\u003edata().add(frame2, \"X\");\n    chart.plot().render(1).withDots();\n    chart.plot().axes().domain().label().withText(\"X-Value\");\n    chart.plot().axes().range(0).label().withText(\"Y-Value\");\n    chart.plot().trend(\"A\");\n    chart.plot().trend(\"B\");\n    chart.plot().trend(\"C\");\n    chart.title().withText(\"Regression Chart\");\n    chart.subtitle().withText(\"Multiple DataFrame, Multiple Series\");\n    chart.legend().on();\n    chart.show();\n});\n```\n\n\u003cp align=\"center\"\u003e\n    \u003cimg class=\"chart\" src=\"http://www.zavtech.com/morpheus/docs/images/charts/chart-regress-2.png\"/\u003e\n\u003c/p\u003e\n\n## Area Charts\n\nAn area chart is basically the same as a line chart however the area between the domain axis and the line is filled\nwith a series specific color. There is one variation on this to note, which is that more often than not, area charts \nare generated as stacked areas, which is useful to visualize the decomposition of some aggregate quantity. The two\nexamples below show a stacked and non-stacked area chart for the identical dataset.\n\n```java\nint rowCount = 100;\nRange\u003cInteger\u003e rowKeys = Range.of(0, rowCount);\nDataFrame\u003cInteger,String\u003e frame = DataFrame.of(rowKeys, String.class, columns -\u003e {\n    Stream.of(\"A\", \"B\", \"C\", \"D\", \"E\").forEach(label -\u003e {\n        columns.add(label, Array.randn(rowCount, 10d, 100d).cumSum());\n    });\n});\n\nChart.create().withAreaPlot(frame, true, chart -\u003e {\n    chart.plot().axes().domain().label().withText(\"X-Value\");\n    chart.plot().axes().range(0).label().withText(\"Random Value\");\n    chart.title().withText(\"Stacked Area Chart\");\n    chart.subtitle().withText(\"Cumulative Sum of Random Normal Data\");\n    chart.legend().on();\n    chart.show();\n});\n```\n\n\u003cp align=\"center\"\u003e\n    \u003cimg class=\"chart\" src=\"http://www.zavtech.com/morpheus/docs/images/charts/chart-area-1.png\"/\u003e\n\u003c/p\u003e\n\nThe following code plots the identical dataset but with overlapping areas rather than stacked (which is simply achieved by\npassing `stacked=false` to the `withAreaPlot()` method) . Notice the different scale in the y-axis between the two plots, the \nformer being of a larger scale given the aggregate nature of that visualization.\n\n```java\nint rowCount = 100;\nRange\u003cInteger\u003e rowKeys = Range.of(0, rowCount);\nDataFrame\u003cInteger,String\u003e frame = DataFrame.of(rowKeys, String.class, columns -\u003e {\n    Stream.of(\"A\", \"B\", \"C\", \"D\", \"E\").forEach(label -\u003e {\n        columns.add(label, Array.randn(rowCount, 10d, 100d).cumSum());\n    });\n});\n\nChart.create().withAreaPlot(frame, false, chart -\u003e {\n    chart.plot().render(0).withArea(false);\n    chart.plot().axes().domain().label().withText(\"X-Value\");\n    chart.plot().axes().range(0).label().withText(\"Random Value\");\n    chart.title().withText(\"Overlapping Area Chart\");\n    chart.subtitle().withText(\"Cumulative Sum of Random Normal Data\");\n    chart.legend().on();\n    chart.show();\n});\n```\n\n\u003cp align=\"center\"\u003e\n    \u003cimg class=\"chart\" src=\"http://www.zavtech.com/morpheus/docs/images/charts/chart-area-2.png\"/\u003e\n\u003c/p\u003e\n\n\n## Pie Charts\n\nPie charts are perhaps the simplest of all the plots, although there are a few customizations that are worth being\naware of. Generally speaking pie plots are used to illustrate the decomposition of some measurement into different\ncategories, and the Morpheus API allows a specific column of a `DataFrame` to be presented in this way. Pie section \nlabels can be customized to show the actual value of the quantity, its percentage of the total or simply the name of \nthe category (which can also be shown via an optional legend).\n\nConsider a single column `DataFrame` of random values that we will assume represent some measurement for different\ncountries which we have defined as the row keys in terms of their 3-character ISO codes.  \n\n```java\nDataFrame\u003cString,String\u003e frame = DataFrame.ofDoubles(\n    Array.of(\"AUS\", \"GBR\", \"USA\", \"DEU\", \"ITA\", \"ESP\", \"ZAF\"),\n    Array.of(\"Random\"),\n    value -\u003e Math.random() * 10d\n);\n```\n\n\u003cdiv class=\"frame\"\u003e\u003cpre class=\"frame\"\u003e\n Index  |    Random    |\n------------------------\n   AUS  |  4.05807605  |\n   GBR  |  2.80847587  |\n   USA  |  2.92350137  |\n   DEU  |  3.39330077  |\n   ITA  |  7.52006927  |\n   ESP  |  1.86101073  |\n   ZAF  |  4.03358896  |\n\u003c/pre\u003e\u003c/div\u003e\n\n### Default Style\n\nThe simplest pie chart can be generated via the code below, which by default labels the pie sections with the percentage \nof the total for the column being plotted. In this example we not indicate a specific column in the `DataFrame` to plot, \nin which case the **first numeric column** is chosen. Both the Swing version and Google version of this plot includes \ninformative tooltips with additional data as you mouse over each section.\n\n```java\nChart.create().withPiePlot(frame, false, chart -\u003e {\n    chart.title().withText(\"Pie Chart of Random Data\");\n    chart.subtitle().withText(\"Labels with Section Percent\");\n    chart.legend().on().right();\n    chart.show();\n});\n```\n\n\u003cp align=\"center\"\u003e\n    \u003cimg class=\"chart\" src=\"http://www.zavtech.com/morpheus/docs/images/charts/chart-pie-1.png\"/\u003e\n\u003c/p\u003e\n\n### Donut Pie Plot\n\nA slight adjustment to the pie plot is to include a hole in order to create a donut chart. In this example we also\nexplicitly pass the name of the column to plot, although given that there is only one numeric column in the `DataFrame`\nthis is superfluous.\n\n```java\nChart.create().withPiePlot(frame, false, \"Random\", chart -\u003e {\n    chart.title().withText(\"Donut Pie Chart of Random Data\");\n    chart.subtitle().withText(\"Labels with Section Value\");\n    chart.plot().withPieHole(0.4);\n    chart.plot().labels().on().withValue();\n    chart.legend().on().right();\n    chart.show();\n});\n```\n\n\u003cp align=\"center\"\u003e\n    \u003cimg class=\"chart\" src=\"http://www.zavtech.com/morpheus/docs/images/charts/chart-pie-2.png\"/\u003e\n\u003c/p\u003e\n\n### 3D Pie Plot\n\nTurning this into a 3D plot is as easy as passing in `true` as the second argument to the `withPiePlot()` method. \n\n```java\nChart.create().withPiePlot(frame, true, chart -\u003e {\n    chart.title().withText(\"3D Pie Chart of Random Data\");\n    chart.subtitle().withText(\"Labels with Section Name\");\n    chart.plot().labels().on().withName();\n    chart.legend().on().right();\n    chart.show();\n});\n```\n\n\u003cp align=\"center\"\u003e\n    \u003cimg class=\"chart\" src=\"http://www.zavtech.com/morpheus/docs/images/charts/chart-pie-3.png\"/\u003e\n\u003c/p\u003e\n\n### Exploded Section\n\nCalling out one or more pie sections is possible by imposing an offset from the center as shown below. In this case\nwe explicitly access the section controller for `AUS` and apply an offset which must be a value between 0 and 1. In\naddition, we have rotated the plot clockwise by 90 degrees by calling the `withStartAngle()` method as illustrated. \n\n```java\nChart.create().withPiePlot(frame, false, chart -\u003e {\n    chart.title().withText(\"Pie Chart of Random Data\");\n    chart.subtitle().withText(\"Custom Label Style with Exploded Pie Section\");\n    chart.plot().labels().withBackgroundColor(Color.WHITE).withFont(new Font(\"Arial\", Font.BOLD, 11));\n    chart.plot().section(\"AUS\").withOffset(0.2);\n    chart.plot().withStartAngle(90);\n    chart.legend().on().right();\n    chart.show();\n});\n```\n\n\u003cp align=\"center\"\u003e\n    \u003cimg class=\"chart\" src=\"http://www.zavtech.com/morpheus/docs/images/charts/chart-pie-4.png\"/\u003e\n\u003c/p\u003e\n\n----\n\n\u003cp align=\"center\"\u003e\n    \u003cimg style=\"background: none; border: none;\" src=\"http://www.zavtech.com/morpheus/docs/images/morpheus-logo1.png\"/\u003e\n\u003c/p\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzavtech%2Fmorpheus-viz","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fzavtech%2Fmorpheus-viz","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzavtech%2Fmorpheus-viz/lists"}