├── .gitignore
├── CHANGELOG.md
├── LICENSE
├── README.md
├── examples
├── bar-charts.clj
├── bubble-charts.clj
├── horizontal-bar-charts.clj
├── line-charts.clj
├── scatter-3d.clj
├── scatter.clj
├── subplot.clj
└── surface.clj
├── project.clj
├── resources
└── plotly.min.js
├── src
└── plotly_clj
│ ├── core.clj
│ └── scale.clj
└── test
└── plotly_clj
└── core_test.clj
/.gitignore:
--------------------------------------------------------------------------------
1 | /target
2 | /classes
3 | /checkouts
4 | pom.xml
5 | pom.xml.asc
6 | *.jar
7 | *.class
8 | /.lein-*
9 | /.nrepl-port
10 | .hgignore
11 | .hg/
12 | plotly.html
13 | .gorilla-port
14 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Change Log
2 | All notable changes to this project will be documented in this file. This change log follows the conventions of [keepachangelog.com](http://keepachangelog.com/).
3 |
4 | ## [0.1.1]
5 | ### Changed
6 |
7 | - `subplot` now support 3d plots.
8 |
9 | ## [0.1.0]
10 | ### Added
11 | - Add `plotly` to create a plotly object with dataset, traces and layout.
12 | - Add `save-html` to save a plotly object into a html page.
13 | - Add `online-init` and `offline-init` to insert plotly.min.js into a Gorilla-REPL notebook.
14 | - Add `iplot` to show a plotly object in Gorilla-REPL notebook.
15 | - Add `plot` to send a plotly object to https://plot.ly and get a sharable url.
16 | - Add `set-credentials` to save user-name and api-key in local file.
17 | - Add `group-dataset` to split a dataset into map.
18 | - Add `add-scatter` to plot lines and markers.
19 | - Add `set-dataset`, `set-layout`, `update-layout`, `set-configs`, `update-configs` to set or update a plotly object.
20 | - Add `embed-url`, embed a sharable url in Gorilla-REPL notebook.
21 | - Add `plot-seq`, enable for comprehension to apply a sequence of add-fn. The implementation is a little ugly. A macro is needed.
22 | - Add `add-annotations`, like geom_text in R.
23 | - Add `add-bar`, plot bar charts.
24 | - Add `subplot`, make subplots of existing traces.
25 | - Add `scatter-3d`, show 3d scatters.
26 | - Add `add-surface`.
27 | - Add `add-histogram-2d` and `add-histogram`
28 | - Add `add-mesh3d`, `add-contour`, `add-heatmap`
29 |
30 | [0.1.1]: https://github.com/findmyway/plotly-clj/tree/0.1.1
31 | [0.1.0]: https://github.com/findmyway/plotly-clj/tree/0.1.0
32 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 Tian Jun
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # plotly-clj
2 |
3 | [](https://clojars.org/plotly-clj)
4 |
5 | A Clojure library designed for creating interactive web graphics via the open source JavaScript graphing library [plotly.js](https://github.com/plotly/plotly.j://github.com/plotly/plotly.js).
6 |
7 | This library is still under development. Any suggestions are welcome!
8 |
9 | ## Installation
10 |
11 | Install with Leiningen, add the following to your `:dependencies`:
12 |
13 | ```
14 | [plotly-clj "0.1.1"]
15 | ```
16 |
17 | ## Usage
18 |
19 | There are three ways to use this library.
20 |
21 | ### Plot offline with Gorilla-REPL
22 |
23 | This is the most common way to use this library. By integrating with [Gorilla-REPL](http://gorilla-repl.org/),
24 | you can easily plot figures like what you do in ipython notebooks.
25 |
26 | ```clojure
27 | (ns x (:use [plotly-clj.core]))
28 |
29 | (offline-init)
30 |
31 | (-> (plotly [2 1 3])
32 | add-scatter
33 | iplot)
34 | ```
35 |
36 | Checkout the following examples for how to use:
37 |
38 | 1. [Scatter](http://viewer.gorilla-repl.org/view.html?source=github&user=findmyway&repo=plotly-clj&path=examples/scatter.clj)
39 | 2. [Bubble-Charts](http://viewer.gorilla-repl.org/view.html?source=github&user=findmyway&repo=plotly-clj&path=examples/bubble-charts.clj)
40 | 3. [Line-Charts](http://viewer.gorilla-repl.org/view.html?source=github&user=findmyway&repo=plotly-clj&path=examples/line-charts.clj)
41 | 4. [Bar-Charts](http://viewer.gorilla-repl.org/view.html?source=github&user=findmyway&repo=plotly-clj&path=examples/bar-charts.clj)
42 | 4. [Horizontal-Bar-Charts](http://viewer.gorilla-repl.org/view.html?source=github&user=findmyway&repo=plotly-clj&path=examples/horizontal-bar-charts.clj)
43 | 5. [Subplot](http://viewer.gorilla-repl.org/view.html?source=github&user=findmyway&repo=plotly-clj&path=examples/subplot.clj)
44 | 6. [Scater-3d](http://viewer.gorilla-repl.org/view.html?source=github&user=findmyway&repo=plotly-clj&path=examples/scatter-3d.clj)
45 | 7. [Surface](http://viewer.gorilla-repl.org/view.html?source=github&user=findmyway&repo=plotly-clj&path=examples/surface.clj)
46 |
47 | ### Plot offline in a html page
48 |
49 | In this way, you can save the plot in a html page and check it out in a browser.
50 |
51 | ```clojure
52 | (ns x (:use [plotly-clj.core]))
53 |
54 | (-> (plotly [2 1 3])
55 | add-scatter
56 | (save-html "plotly.html" :open))
57 | ```
58 |
59 | ### Plot online
60 |
61 | You can also send your figure to the [plot.ly](https://plot.ly) and get a sharable url.
62 | Notice that you should set the user-name and api-key first. You can register a count first
63 | and find your api-key at [here](https://plot.ly/settings/api).
64 |
65 | ```clojure
66 | (ns x (:use [plotly-clj.core]))
67 |
68 | (set-credentials "your-name" "your-api-key")
69 |
70 | (-> (plotly [2 1 3])
71 | add-scatter
72 | (plot "filename"))
73 | ;; https://plot.ly/~findmyway/98
74 | ```
75 |
76 | ## License
77 |
78 | ```
79 | MIT License
80 |
81 | Copyright (c) 2017 Tian Jun
82 |
83 | Permission is hereby granted, free of charge, to any person obtaining a copy
84 | of this software and associated documentation files (the "Software"), to deal
85 | in the Software without restriction, including without limitation the rights
86 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
87 | copies of the Software, and to permit persons to whom the Software is
88 | furnished to do so, subject to the following conditions:
89 |
90 | The above copyright notice and this permission notice shall be included in all
91 | copies or substantial portions of the Software.
92 |
93 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
94 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
95 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
96 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
97 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
98 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
99 | SOFTWARE.
100 | ```
101 |
--------------------------------------------------------------------------------
/examples/bar-charts.clj:
--------------------------------------------------------------------------------
1 | ;; gorilla-repl.fileformat = 1
2 |
3 | ;; **
4 | ;;; # Bar Charts
5 | ;; **
6 |
7 | ;; @@
8 | (ns bar
9 | (:require [clojure.core.matrix :as m]
10 | [clojure.core.matrix.dataset :as d]
11 | [clojure.core.matrix.random :as rnd]
12 | [clojure.data.csv :as csv])
13 | (:use [plotly-clj.core] :reload-all))
14 | ;; @@
15 | ;; =>
16 | ;;; {"type":"html","content":"nil","value":"nil"}
17 | ;; <=
18 |
19 | ;; @@
20 | (online-init)
21 | ;; @@
22 | ;; =>
23 | ;;; {"type":"html","content":"","value":"pr'ed value"}
24 | ;; <=
25 |
26 | ;; @@
27 | (-> (plotly {:x ["giraffes" "orangutans" "monkeys"]})
28 | (add-bar
29 | :x :x
30 | :y [20 14 23]
31 | :name "SF Zoo")
32 | (plot "Basic-Bar-Chart" :fileopt "overwrite")
33 | embed-url)
34 | ;; @@
35 | ;; =>
36 | ;;; {"type":"html","content":"","value":"pr'ed value"}
37 | ;; <=
38 |
39 | ;; @@
40 | (-> (plotly {:x ["giraffes" "orangutans" "monkeys"]})
41 | (add-bar
42 | :x :x
43 | :y [20 14 23]
44 | :name "SF Zoo")
45 | (add-bar
46 | :x :x
47 | :y [12 18 29]
48 | :name "LA Zoo")
49 | (plot "Grouped-Bar-Chart" :fileopt "overwrite")
50 | embed-url)
51 | ;; @@
52 | ;; =>
53 | ;;; {"type":"html","content":"","value":"pr'ed value"}
54 | ;; <=
55 |
56 | ;; @@
57 | (-> (plotly {:x ["giraffes" "orangutans" "monkeys"]})
58 | (add-bar
59 | :x :x
60 | :y [20 14 23]
61 | :name "SF Zoo")
62 | (add-bar
63 | :x :x
64 | :y [12 18 29]
65 | :name "LA Zoo")
66 | (set-layout :barmode "stack")
67 | (plot "Stacked-Bar-Chart" :fileopt "overwrite")
68 | embed-url)
69 | ;; @@
70 | ;; =>
71 | ;;; {"type":"html","content":"","value":"pr'ed value"}
72 | ;; <=
73 |
74 | ;; **
75 | ;;; ##Bar Chart with Hover Text
76 | ;; **
77 |
78 | ;; @@
79 | (-> (plotly)
80 | (add-bar
81 | :x ["Product A" "Product B" "Product C"]
82 | :y [20 14 23]
83 | :text ["27% market share" "24% market share" "19% market share"]
84 | :marker {:color "rgb(158,202,225)"
85 | :line {:color "rgb(8,48,107)" :width 1.5}}
86 | :opacity 0.5)
87 | (set-layout :title "January 2013 Sales Report")
88 | (plot "Bar-Chart-with-Hover-Text" :fileopt "overwrite")
89 | embed-url)
90 | ;; @@
91 | ;; =>
92 | ;;; {"type":"html","content":"","value":"pr'ed value"}
93 | ;; <=
94 |
95 | ;; **
96 | ;;; ## Styled Bar Chart with Direct Labels
97 | ;; **
98 |
99 | ;; @@
100 | (let [xs ["Product A" "Product B" "Product C"]
101 | ys [20 14 23]]
102 | (-> (plotly)
103 | (add-bar
104 | :x xs
105 | :y ys
106 | :text ["27% market share" "24% market share" "19% market share"]
107 | :marker {:color "rgb(158,202,225)"
108 | :line {:color "rgb(8,48,107)" :width 1.5}}
109 | :opacity 0.5)
110 | (set-layout :title "January 2013 Sales Report")
111 | (add-annotations
112 | (for [[x y] (map vector xs ys)]
113 | {:x x :y y :text y :showarrow false :yanchor "bottom"}))
114 | (plot "Styled-Bar-Chart-with-Direct-Labels" :fileopt "overwrite")
115 | embed-url))
116 | ;; @@
117 | ;; =>
118 | ;;; {"type":"html","content":"","value":"pr'ed value"}
119 | ;; <=
120 |
121 | ;; **
122 | ;;; ## Rotated Bar Chart Labels
123 | ;; **
124 |
125 | ;; @@
126 | (-> (plotly {:x ["Jan" "Feb" "Mar" "Apr" "May" "Jun""Jul" "Aug" "Sep" "Oct" "Nov" "Dec"]})
127 | (add-bar
128 | :x :x
129 | :y [20 14 25 16 18 22 19 15 12 16 14 17]
130 | :name "Primary Product"
131 | :marker {:color "rgb(49,130,189)"})
132 | (add-bar
133 | :x :x
134 | :y [19 14 22 14 16 19 15 14 10 12 12 16]
135 | :name "Secondary Product"
136 | :marker {:color "rgb(204,204,204)"})
137 | (set-layout :xaxis {:tickangle -45})
138 | (plot "Rotated Bar Chart Labels" :fileopt "overwrite")
139 | embed-url)
140 | ;; @@
141 | ;; =>
142 | ;;; {"type":"html","content":"","value":"pr'ed value"}
143 | ;; <=
144 |
145 | ;; **
146 | ;;; ##Customizing Individual Bar Colors
147 | ;; **
148 |
149 | ;; @@
150 | (-> (plotly)
151 | (add-bar
152 | :x ["Feature A" "Feature B" "Feature C""Feature D" "Feature E"]
153 | :y [20 14 23 25 22]
154 | :marker {:color ["rgba(204,204,204,1)" "rgba(222,45,38,0.8)"
155 | "rgba(204,204,204,1)" "rgba(204,204,204,1)" "rgba(204,204,204,1)"]})
156 | (plot "Customizing-Individual-Bar-Colors" :fileopt "overwrite")
157 | embed-url)
158 | ;; @@
159 | ;; =>
160 | ;;; {"type":"html","content":"","value":"pr'ed value"}
161 | ;; <=
162 |
163 | ;; **
164 | ;;; ##Colored and Styled Bar Chart
165 | ;; **
166 |
167 | ;; @@
168 | (-> (plotly {:x [1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012]})
169 | (add-bar
170 | :x :x
171 | :y [219 146 112 127 124 180 236 207 236 263 350 430 474 526 488 537 500 439]
172 | :name "Rest of world"
173 | :marker {:color "rgb(55, 83, 109)"})
174 | (add-bar
175 | :x :x
176 | :y [16 13 10 11 28 37 43 55 56 88 105 156 270 299 340 403 549 499]
177 | :name "China"
178 | :marker {:color "rgb(26, 118, 255)"})
179 | (set-layout
180 | :title "US Export of Plastic Scrap"
181 | :xaxis {:tickfont {:size 14 :color "rgb(107, 107, 107)"}}
182 | :yaxis {:title "USD (millions)"
183 | :titlefont {:size 14 :color "rgb(107, 107, 107)"}
184 | :tickfont {:size 14 :color "rgb(107, 107, 107)"}}
185 | :legend {:x 0
186 | :y 1.0}
187 | :bargap 0.15
188 | :bargroupgap 0.1)
189 | (plot "Colored-and-Styled-Bar-Chart" :fileopt "overwrite")
190 | embed-url)
191 | ;; @@
192 | ;; =>
193 | ;;; {"type":"html","content":"","value":"pr'ed value"}
194 | ;; <=
195 |
196 | ;; **
197 | ;;; ##Waterfall Bar Chart
198 | ;; **
199 |
200 | ;; @@
201 |
202 | (let [ds (d/dataset {:xs ["Product
Revenue" "Services
Revenue"
203 | "Total
Revenue" "Fixed
Costs"
204 | "Variable
Costs" "Total
Costs" "Total"]
205 | :ys [400 660 660 590 400 400 340]
206 | :text ["$430K" "$260K" "$690K" "$-120K" "$-200K" "$-320K" "$370K"]})]
207 | (->(plotly ds)
208 | (add-bar :x :xs
209 | :y [0 430 0 570 370 370 0]
210 | :marker {:color "rgba(1,1,1, 0.0)"})
211 | (add-bar :x :xs
212 | :y [430 260 690 0 0 0 0]
213 | :marker {:color "rgba(55, 128, 191, 0.7)"
214 | :line {:color "rgba(55, 128, 191, 1.0)" :width 2}})
215 | (add-bar :x :xs
216 | :y [0 0 0 120 200 320 0]
217 | :marker {:color "rgba(219, 64, 82, 0.7)"
218 | :line {:color "rgba(219, 64, 82, 1.0)" :width 2}})
219 | (add-bar :x :xs
220 | :y [0 0 0 0 0 0 370]
221 | :marker {:color "rgba(50, 171, 96, 0.7)"
222 | :line {:color "rgba(50, 171, 96, 1.0)" :width 2}})
223 | (set-layout :title "Annual Profit- 2015"
224 | :barmode "stack"
225 | :showlegend false
226 | :paper_bgcolor "rgba(245, 246, 249, 1)"
227 | :plot_bgcolor "rgba(245, 246, 249, 1)")
228 | (add-annotations
229 | (for [[x y t] ds]
230 | {:x x :y y :text t :showarrow false
231 | :font {:family "Arial" :size 14 :color "rgba(245, 246, 249, 1)"}}))
232 | (plot "Waterfall Bar Chart" :fileopt "overwrite")
233 | embed-url))
234 | ;; @@
235 | ;; =>
236 | ;;; {"type":"html","content":"","value":"pr'ed value"}
237 | ;; <=
238 |
239 | ;; **
240 | ;;; ##Bar Chart with Relative Barmode
241 | ;;;
242 | ;; **
243 |
244 | ;; @@
245 | (let [x [1 2 3 4]]
246 | (-> (plotly)
247 | (add-bar :x x :y [1 4 9 16])
248 | (add-bar :x x :y [6 -8 -4.5 8])
249 | (add-bar :x x :y [-15 -3 4.5 -8])
250 | (add-bar :x x :y [-1 3 -3 -4])
251 | (set-layout :barmode "relative")
252 | (plot "Bar Chart with Relative Barmode" :fileopt "overwrite")
253 | embed-url))
254 | ;; @@
255 | ;; =>
256 | ;;; {"type":"html","content":"","value":"pr'ed value"}
257 | ;; <=
258 |
259 | ;; @@
260 |
261 | ;; @@
262 |
--------------------------------------------------------------------------------
/examples/bubble-charts.clj:
--------------------------------------------------------------------------------
1 | ;; gorilla-repl.fileformat = 1
2 |
3 | ;; **
4 | ;;; #Bubble-Charts
5 | ;; **
6 |
7 | ;; **
8 | ;;; If you haven't read the **scatter.clj**, I strongly suggest you go through that file first.
9 | ;; **
10 |
11 | ;; @@
12 | (ns bubble
13 | (:require [clojure.core.matrix :as m]
14 | [clojure.core.matrix.dataset :as d]
15 | [clojure.core.matrix.random :as rnd]
16 | [clojure.data.csv :as csv])
17 | (:use [plotly-clj.core] :reload-all))
18 | ;; @@
19 | ;; =>
20 | ;;; {"type":"html","content":"nil","value":"nil"}
21 | ;; <=
22 |
23 | ;; **
24 | ;;;
25 | ;; **
26 |
27 | ;; @@
28 | (online-init)
29 | ;; @@
30 | ;; =>
31 | ;;; {"type":"html","content":"","value":"pr'ed value"}
32 | ;; <=
33 |
34 | ;; **
35 | ;;; ## Simple Bubble Chart
36 | ;; **
37 |
38 | ;; @@
39 | (-> (plotly [10, 11, 12, 13])
40 | (add-scatter :mode "markers"
41 | :text ["A
size: 40" "B
size: 60""C
size: 80""D
size: 100"]
42 | :marker {:size [40, 60, 80, 100]
43 | :color ["rgb(93, 164, 214)" "rgb(255, 144, 14)" "rgb(44, 160, 101)" "rgb(255, 65, 54)"]
44 | :opacity [1 0.8 0.6 0.4]})
45 | (set-layout :xaxis {:zeroline false})
46 | (plot "simple-bubble-chart" :fileopt "overwrite")
47 | embed-url)
48 | ;; @@
49 | ;; =>
50 | ;;; {"type":"html","content":"","value":"pr'ed value"}
51 | ;; <=
52 |
53 | ;; **
54 | ;;; ## Scaling the Size and Text of Bubble Charts
55 | ;; **
56 |
57 | ;; @@
58 | (-> (plotly {:x (vec (range 4))
59 | :y [10 11 12 13]
60 | :s [400 600 800 1000]})
61 | (add-scatter :mode "markers"
62 | :marker {:size :s
63 | :sizemode "area"})
64 | (add-scatter :mode "markers"
65 | :y #(m/add (d/column % :y) 4)
66 | :marker {:size #(m/mul (d/column % :s) 0.2)
67 | :sizemode "area"})
68 | (add-scatter :mode "markers"
69 | :y #(m/add (d/column % :y) 8)
70 | :marker {:size #(m/mul (d/column % :s) 2)
71 | :sizemode "area"})
72 | (plot "scaling-the-size-and-text-of-bubbe-charts" :fileopt "overwrite")
73 | embed-url)
74 | ;; @@
75 | ;; =>
76 | ;;; {"type":"html","content":"","value":"pr'ed value"}
77 | ;; <=
78 |
79 | ;; **
80 | ;;; ## Bubble Charts with Colorscale
81 | ;; **
82 |
83 | ;; @@
84 | (-> (plotly {:x [1 3.2 5.4 7.6 9.8 12.5]
85 | :y [1 3.2 5.4 7.6 9.8 12.5]
86 | :colors [120 125 130 135 140 145]
87 | :sizes [15 30 55 70 90 110]})
88 | (add-scatter :mode "markers"
89 | :marker {:size :sizes
90 | :color :colors
91 | :showscale true})
92 | (plot "bubble-charts-with-colorscale" :fileopt "overwrite")
93 | embed-url)
94 | ;; @@
95 | ;; =>
96 | ;;; {"type":"html","content":"","value":"pr'ed value"}
97 | ;; <=
98 |
99 | ;; @@
100 | (def data
101 | (let [in-file (slurp "https://raw.githubusercontent.com/plotly/datasets/master/gapminderDataFiveYear.csv")
102 | data (doall (csv/read-csv in-file))]
103 | (d/dataset (map keyword (first data)) (rest data))))
104 | ;; @@
105 | ;; =>
106 | ;;; {"type":"html","content":"#'bubble/data","value":"#'bubble/data"}
107 | ;; <=
108 |
109 | ;; @@
110 | (defn parse-float
111 | [x]
112 | (Float/parseFloat x))
113 |
114 | (def data-2007 (d/dataset (map (fn [row] (-> row
115 | (update :pop parse-float)
116 | (update :lifeExp parse-float)
117 | (update :gdpPercap parse-float)))
118 | (filter #(= "2007" (:year %)) (d/row-maps data)))))
119 | ;; @@
120 | ;; =>
121 | ;;; {"type":"html","content":"#'bubble/data-2007","value":"#'bubble/data-2007"}
122 | ;; <=
123 |
124 | ;; @@
125 | (defn format-row
126 | [r]
127 | (format "Country: %s
Pop: %s
Life Expectancy: %s
GDP per capita: %s
"
128 | (first r) (nth r 2) (nth r 4) (nth r 5)))
129 |
130 | (-> (plotly data-2007)
131 | (group-dataset :continent)
132 | (add-scatter :mode "markers"
133 | :x :gdpPercap
134 | :y :lifeExp
135 | :text (map format-row data-2007)
136 | :marker {:symbol 0
137 | :sizemode "diameter"
138 | :sizeref 1.5
139 | :size #(m/sqrt (m/mul (d/column % :pop) 2.666051223553066e-05))})
140 | (set-layout
141 | :title "Life Expectancy v. Per Capita GDP, 2007"
142 | :xaxis {:title "GDP per capita (2000 dollars)"
143 | :type "log"
144 | :range [2.003297660701705 5.191505530708712]
145 | :zerolinewidth 1
146 | :ticklen 5
147 | :gridwidth 2}
148 | :yaxis {:title "Life Expectancy (years)"
149 | :range [36.12621671352166 91.72921793264332]
150 | :zerolinewidth 1
151 | :ticklen 5
152 | :gridwidth 2}
153 | :paper_bgcolor "rgb(243, 243, 243)"
154 | :plot_bgcolor "rgb(243, 243, 243)")
155 | (plot "Life-Expectancy-v.-Per-Capita-GDP,-2007" :fileopt "overwrite")
156 | embed-url)
157 | ;; @@
158 | ;; =>
159 | ;;; {"type":"html","content":"","value":"pr'ed value"}
160 | ;; <=
161 |
162 | ;; @@
163 |
164 | ;; @@
165 |
--------------------------------------------------------------------------------
/examples/horizontal-bar-charts.clj:
--------------------------------------------------------------------------------
1 | ;; gorilla-repl.fileformat = 1
2 |
3 | ;; **
4 | ;;; #Horizontal-Bar-Charts
5 | ;; **
6 |
7 | ;; @@
8 | (ns horizontal-bar
9 | (:require [clojure.core.matrix :as m]
10 | [clojure.core.matrix.dataset :as d]
11 | [clojure.core.matrix.random :as rnd]
12 | [clojure.data.csv :as csv])
13 | (:use [plotly-clj.core] :reload-all))
14 | ;; @@
15 | ;; =>
16 | ;;; {"type":"html","content":"nil","value":"nil"}
17 | ;; <=
18 |
19 | ;; @@
20 | (online-init)
21 | ;; @@
22 | ;; =>
23 | ;;; {"type":"html","content":"","value":"pr'ed value"}
24 | ;; <=
25 |
26 | ;; @@
27 | (-> (plotly)
28 | (add-bar :x [20 14 23]
29 | :y ["giraffes" "orangutans" "monkeys"]
30 | :orientation "h")
31 | (plot "Basic Horizontal Bar Chart" :fileopt "overwrite")
32 | embed-url)
33 | ;; @@
34 | ;; =>
35 | ;;; {"type":"html","content":"","value":"pr'ed value"}
36 | ;; <=
37 |
38 | ;; **
39 | ;;; ##Colored Horizontal Bar Chart
40 | ;; **
41 |
42 | ;; @@
43 | (-> (plotly)
44 | (add-bar :x [20 14 23]
45 | :y ["giraffes" "orangutans" "monkeys"]
46 | :name "SF Zoo"
47 | :orientation "h"
48 | :marker {:color "rgba(246, 78, 139, 0.6)" :line {:color "rgba(246, 78, 139, 1.0)" :width 3}})
49 | (add-bar :x [12 18 19]
50 | :y ["giraffes" "orangutans" "monkeys"]
51 | :name "LA Zoo"
52 | :orientation "h"
53 | :marker {:color "rgba(58, 71, 80, 0.6)" :line {:color "rgba(58, 71, 80, 1.0)" :width 3}})
54 | (set-layout :barmode "stack")
55 | (plot "Colored Horizontal Bar Chart" :fileopt "overwrite")
56 | embed-url)
57 | ;; @@
58 | ;; =>
59 | ;;; {"type":"html","content":"","value":"pr'ed value"}
60 | ;; <=
61 |
62 | ;; **
63 | ;;; ##Color Palette for Bar Chart
64 | ;; **
65 |
66 | ;; @@
67 | (let [top-labels ["Strongly
agree" "Agree" "Neutral" "Disagree" "Strongly
disagree"]
68 | colors ["rgba(38 24 74 0.8)" "rgba(71 58 131 0.8)" "rgba(122 120 168 0.8)"
69 | "rgba(164 163 204 0.85)" "rgba(190 192 213 1)"]
70 | x-data [[21 30 21 16 12] [24 31 19 15 11] [27 26 23 11 13] [29 24 15 18 14]]
71 | y-data ["The course was effectively
organized"
72 | "The course developed my
abilities and skills for
the subject"
73 | "The course developed my
ability to think critically about
the subject"
74 | "I would recommend this
course to a friend"]
75 | get-mid (fn [xs]
76 | (let [s (butlast (reductions + 0 xs))
77 | halves (m/div xs 2)]
78 | (m/add s halves)))
79 | xs-mid (map get-mid x-data)]
80 | (-> (plotly)
81 | (plot-seq
82 | (for [[x c] (map vector (m/transpose x-data) colors)]
83 | #(add-bar %
84 | :x x
85 | :y y-data
86 | :orientation "h"
87 | :marker {:color c
88 | :line {:color "rgb(248, 248, 249)" :width 1}})))
89 | (set-layout :xaxis {:showgrid false
90 | :showline false
91 | :showticklabels false
92 | :zeroline false
93 | :domain [0.15 1]}
94 | :yaxis {:showgrid false
95 | :showline false
96 | :showticklabels false
97 | :zeroline false}
98 | :barmode "stack"
99 | :showlegend false
100 | :paper_bgcolor "rgb(248, 248, 255)"
101 | :plot_bgcolor "rgb(248, 248, 255)"
102 | :margin {:l 120 :r 10 :t 120 :b 80})
103 | (add-annotations
104 | (for [[x y] (map vector x-data y-data)]
105 | {:xref "paper" :yref "y" :x 0.14 :y y :xanchor "right" :text y :showarrow false :align "right"}))
106 | (add-annotations
107 | (for [idy (range (count y-data)) idx (range (count (first xs-mid)))]
108 | {:xref "x"
109 | :yref "y"
110 | :x (nth (nth xs-mid idy) idx)
111 | :y (nth y-data idy)
112 | :text (str (nth (nth x-data idy) idx) "%")
113 | :showarrow false
114 | :font {:color "rgb(248, 248, 255)"
115 | :family "Arial"
116 | :size 14}}))
117 | (add-annotations
118 | (for [[x label] (map vector (last xs-mid) top-labels)]
119 | {:xref "x" :yref "paper"
120 | :x x :y 1.1 :text label :showarrow false}))
121 |
122 | (plot "Color Palette for Bar Chart" :fileopt "overwrite")
123 | embed-url))
124 | ;; @@
125 | ;; =>
126 | ;;; {"type":"html","content":"","value":"pr'ed value"}
127 | ;; <=
128 |
129 | ;; @@
130 | (let [y-saving [1.3586 2.2623 4.9822 6.5097 7.4812 7.5133 15.2148 17.5205]
131 | y-net-worth [93453.92 81666.57 69889.62 78381.53 141395.3 92969.02 66090.18 122379.3]
132 | x-saving ["Japan" "United Kingdom" "Canada" "Netherlands"
133 | "United States" "Belgium" "Sweden" "Switzerland"]
134 | x-net-worth ["Japan" "United Kingdom" "Canada" "Netherlands"
135 | "United States" "Belgium" "Sweden" "Switzerland"]]
136 | (-> (plotly)
137 | (add-bar :x y-saving
138 | :y x-saving
139 | :xaxis "x1"
140 | :yaxis "y1"
141 | :name "Household savings, percentage of household disposable income"
142 | :orientation "h"
143 | :marker {:color "rgba(50, 171, 96, 0.6)"
144 | :line {:color "rgba(50, 171, 96, 1.0)" :width 1}})
145 | (add-scatter :x y-net-worth
146 | :xaxis "x2"
147 | :yaxis "y2"
148 | :y x-saving
149 | :mode "lines+markers"
150 | :name "Household net worth, Million USD/capita"
151 | :line {:color "rgb(128, 0, 128)"})
152 | (set-layout :title "Household savings & net worth for eight OECD countries"
153 | :yaxis1 {:showticklabels true
154 | :domain [0 0.85]}
155 | :yaxis2 {:anchor "x2"
156 | :linecolor "rgba(102, 102, 102, 0.8)"
157 | :domain [0 0.85]
158 | :showticklabels false}
159 | :xaxis1 {:domain [0 0.45]
160 | :zeroline false
161 | :showline false
162 | :showticklabels true
163 | :showgrid true}
164 | :xaxis2 {:domain [0.47 1]
165 | :zeroline false
166 | :showline false
167 | :showticklabels true
168 | :showgrid true
169 | :dtick 25000
170 | :side "top"}
171 | :margin {:l 100 :r 20 :t 70 :b 70}
172 | :legend {:x 0.029 :y 1.05 :font {:size 10}})
173 | (plot "Bar Chart with Line Plot" :fileopt "overwrite")
174 | embed-url))
175 | ;; @@
176 | ;; =>
177 | ;;; {"type":"html","content":"","value":"pr'ed value"}
178 | ;; <=
179 |
180 | ;; @@
181 |
182 | ;; @@
183 |
--------------------------------------------------------------------------------
/examples/line-charts.clj:
--------------------------------------------------------------------------------
1 | ;; gorilla-repl.fileformat = 1
2 |
3 | ;; **
4 | ;;; # Line-Charts
5 | ;; **
6 |
7 | ;; @@
8 | (ns bubble
9 | (:require [clojure.core.matrix :as m]
10 | [clojure.core.matrix.dataset :as d]
11 | [clojure.core.matrix.random :as rnd]
12 | [clojure.data.csv :as csv])
13 | (:use [plotly-clj.core] :reload-all))
14 | ;; @@
15 | ;; =>
16 | ;;; {"type":"html","content":"nil","value":"nil"}
17 | ;; <=
18 |
19 | ;; @@
20 | (online-init)
21 | ;; @@
22 | ;; =>
23 | ;;; {"type":"html","content":"","value":"pr'ed value"}
24 | ;; <=
25 |
26 | ;; @@
27 | (-> (plotly (rnd/sample-normal 500))
28 | (add-scatter)
29 | (plot "simple-line-charts")
30 | embed-url)
31 | ;; @@
32 | ;; =>
33 | ;;; {"type":"html","content":"","value":"pr'ed value"}
34 | ;; <=
35 |
36 | ;; @@
37 | (def data
38 | (d/dataset
39 | {:month ["January" "February" "March" "April" "May" "June" "July" "August" "September" "October" "November" "December"]
40 | :high-2000 [32.5 37.6 49.9 53.0 69.1 75.4 76.5 76.6 70.7 60.6 45.1 29.3]
41 | :low-2000 [13.8 22.3 32.5 37.2 49.9 56.1 57.7 58.3 51.2 42.8 31.6 15.9]
42 | :high-2007 [36.5 26.6 43.6 52.3 71.5 81.4 80.5 82.2 76.0 67.3 46.1 35.0]
43 | :low-2007 [23.6 14.0 27.0 36.8 47.6 57.7 58.9 61.2 53.3 48.5 31.0 23.6]
44 | :high-2014 [28.8 28.5 37.0 56.8 69.7 79.7 78.5 77.8 74.1 62.6 45.3 39.9]
45 | :low-2014 [12.7 14.3 18.6 35.5 49.9 58.0 60.0 58.6 51.7 45.2 32.2 29.1]}))
46 |
47 | (let [colors (flatten (repeat 3 ["rgb(205, 12, 24)" "rgb(22, 96, 167)"]))
48 | line-types ["dashdot" "dashdot" "dash" "dash" "dot" "dot"]
49 | cols (map #(d/column-name data %) (range 1 7))]
50 | (-> (plotly data)
51 | (plot-seq
52 | (for [[col color line-type] (map vector cols colors line-types)]
53 | #(add-scatter %
54 | :x :month
55 | :y col
56 | :name (str col)
57 | :line {:color color :width 4 :dash line-type})))
58 | (plot "Average-High-and-Low-Temperatures-in-New-York" :fileopt "overwrite")
59 | embed-url))
60 | ;; @@
61 | ;; =>
62 | ;;; {"type":"html","content":"","value":"pr'ed value"}
63 | ;; <=
64 |
65 | ;; @@
66 | (-> (plotly [10 20 nil 15 10 5 15 nil 20 10 10 15 25 20 10])
67 | (add-scatter :connectgaps true)
68 | (add-scatter :y [15 25 nil 20 15 10 20 nil 25 15 15 20 30 25 15])
69 | (plot "line-gaps" :fileopt "overwrite")
70 | embed-url)
71 | ;; @@
72 | ;; =>
73 | ;;; {"type":"html","content":"","value":"pr'ed value"}
74 | ;; <=
75 |
76 | ;; @@
77 | (-> (plotly)
78 | (plot-seq
79 | (for [[line-type y] (map vector
80 | ["linear" "spline" "vhv" "hvh" "vh" "hv"]
81 | (map #(m/add (* 5 %) [1 3 2 3 1]) (range 6)))]
82 | #(add-scatter % :y y :line {:shape line-type})))
83 | (set-layout :legend {:y 0.5 :traceorder "reversed"})
84 | (plot "Interpolation-with-Line-Plots" :fileopt "overwrite")
85 | embed-url)
86 | ;; @@
87 | ;; =>
88 | ;;; {"type":"html","content":"","value":"pr'ed value"}
89 | ;; <=
90 |
91 | ;; @@
92 | (let [rates [[74 82 80 74 73 72 74 70 70 66 66 69]
93 | [45 42 50 46 36 36 34 35 32 31 31 28]
94 | [13 14 20 24 20 24 24 40 35 41 43 50]
95 | [18 21 18 21 16 14 13 18 17 16 19 23]]
96 | labels ["Television" "Newspaper" "Internet" "Radio"]
97 | marker-sizes [8 8 12 8]
98 | colors ["rgba(67,67,67,1)" "rgba(115,115,115,1)" "rgba(49,130,189, 1)" "rgba(189,189,189,1)"]]
99 | (-> (plotly {:years [2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2013]})
100 | (plot-seq
101 | (for [[rate color] (map vector rates colors)]
102 | #(add-scatter %
103 | :x :years
104 | :y rate
105 | :mode "lines"
106 | :line {:color color :width 2})))
107 | (plot-seq
108 | (for [[rate color marker-size] (map vector rates colors marker-sizes)]
109 | #(add-scatter %
110 | :x [2001 2013]
111 | :y [(first rate) (last rate)]
112 | :mode "markers"
113 | :marker {:color color :size marker-size})))
114 | (set-layout :xaxis {:showline true
115 | :showgrid false
116 | :showticklabels true
117 | :linecolor "rgb(204, 204, 204)"
118 | :autotick false
119 | :ticks "outside"
120 | :tickcolor "rgb(204, 204, 204)"
121 | :tickwidth 2
122 | :ticklen 5
123 | :tickfont {:family "Arial" :size 12 :color "rgb(82, 82, 82)"}}
124 | :yaxis {:showgrid false
125 | :zeroline false
126 | :showline false
127 | :showticklabels false}
128 | :showlegend false
129 | :autosize false
130 | :margin {:autoexpand false :l 100 :r 20 :t 110})
131 | (add-annotations
132 | (for [[rate color label] (map vector rates colors labels)]
133 | {:xref "paper"
134 | :x 0.05
135 | :y (first rate)
136 | :xanchor "right"
137 | :yanchor "middle"
138 | :text (str label (first rate) "%")
139 | :font {:family "Arial" :size 16 :color color}
140 | :showarrow false}))
141 |
142 | (add-annotations
143 | (for [[rate color label] (map vector rates colors labels)]
144 | {:xref "paper"
145 | :x 0.95
146 | :y (last rate)
147 | :xanchor "left"
148 | :yanchor "middle"
149 | :text (str (last rate) "%")
150 | :font {:family "Arial" :size 16 :color color}
151 | :showarrow false}))
152 | (add-annotations
153 | {:xref "paper"
154 | :yref "paper"
155 | :x 0.5
156 | :y -0.1
157 | :xanchor "center"
158 | :yanchor "top"
159 | :text "Source: PewResearch Center & Storytelling with data"
160 | :font {:family "Arial" :size 12 :color "rgb(150,150,150)"}
161 | :showarrow false})
162 | (add-annotations
163 | {:xref "paper"
164 | :yref "paper"
165 | :x 0.0
166 | :y 1.05
167 | :xanchor "left"
168 | :yanchor "bottom"
169 | :text "Main Source for News"
170 | :font {:family "Arial" :size 30 :color "rgb(37, 37, 37)"}
171 | :showarrow false})
172 | (plot "Label-Lines-with-Annotations" :fileopt "overwrite")
173 | embed-url))
174 | ;; @@
175 | ;; =>
176 | ;;; {"type":"html","content":"","value":"pr'ed value"}
177 | ;; <=
178 |
179 | ;; @@
180 |
181 | ;; @@
182 |
--------------------------------------------------------------------------------
/examples/scatter-3d.clj:
--------------------------------------------------------------------------------
1 | ;; gorilla-repl.fileformat = 1
2 |
3 | ;; **
4 | ;;; #Scatter-3d
5 | ;; **
6 |
7 | ;; @@
8 | (ns bar
9 | (:require [clojure.core.matrix :as m]
10 | [clojure.core.matrix.dataset :as d]
11 | [clojure.core.matrix.random :as rnd]
12 | [clojure.data.csv :as csv])
13 | (:use [plotly-clj.core] :reload-all))
14 | ;; @@
15 | ;; =>
16 | ;;; {"type":"html","content":"nil","value":"nil"}
17 | ;; <=
18 |
19 | ;; @@
20 | (online-init)
21 | ;; @@
22 | ;; =>
23 | ;;; {"type":"html","content":"","value":"pr'ed value"}
24 | ;; <=
25 |
26 | ;; @@
27 | (let [steps (range 0 20 0.1)]
28 | (-> (plotly)
29 | (add-scatter-3d :x (m/sin steps) :y (m/cos steps) :z steps :mode "lines")
30 | (plot "simple lines 3d" :fileopt "overwrite")
31 | embed-url))
32 | ;; @@
33 | ;; =>
34 | ;;; {"type":"html","content":"","value":"pr'ed value"}
35 | ;; <=
36 |
37 | ;; @@
38 | (def data
39 | (let [in-file (slurp "https://raw.githubusercontent.com/plotly/datasets/master/3d-scatter.csv")
40 | data (doall (csv/read-csv in-file))]
41 | (d/dataset (map keyword (first data)) (rest data))
42 | ))
43 | ;; @@
44 | ;; =>
45 | ;;; {"type":"html","content":"#'bar/data","value":"#'bar/data"}
46 | ;; <=
47 |
48 | ;; @@
49 | (-> (plotly data)
50 | (add-scatter-3d :x :x1 :y :y1 :z :z1 :mode "markers"
51 | :marker {:size 12
52 | :opacity 0.8
53 | :line {:color "rgba(217, 217, 217, 0.14)" :width 0.5}})
54 | (plot "simple scatter 3d" :fileopt "overwrite")
55 | embed-url)
56 | ;; @@
57 | ;; =>
58 | ;;; {"type":"html","content":"","value":"pr'ed value"}
59 | ;; <=
60 |
--------------------------------------------------------------------------------
/examples/scatter.clj:
--------------------------------------------------------------------------------
1 | ;; gorilla-repl.fileformat = 1
2 |
3 | ;; **
4 | ;;; # SCATTER
5 | ;;;
6 | ;;; This page shows almost all the examples related to scatter in [Plotly Python Library](https://plot.ly/python/) and [Plotly R Library](https://plot.ly/r/)
7 | ;; **
8 |
9 | ;; @@
10 | (ns scatter
11 | (:require [clojure.core.matrix :as m]
12 | [clojure.core.matrix.dataset :as d]
13 | [clojure.core.matrix.random :as rnd]
14 | [clojure.data.csv :as csv])
15 | (:use [plotly-clj.core] :reload-all))
16 | ;; @@
17 | ;; =>
18 | ;;; {"type":"html","content":"nil","value":"nil"}
19 | ;; <=
20 |
21 | ;; **
22 | ;;; ## Initial
23 | ;;;
24 | ;;; In order to play offline, we need to call ``offline-init`` first and it will insert the whole ``plotly.min.js`` in this page.
25 | ;;; However, this will make the page a little large. The size of ``plotly.min.js`` is about **1.7M** . To reduce the page size, I use the ``online-init`` instead here.
26 | ;; **
27 |
28 | ;; @@
29 | (online-init) ;; Call offline-init here if you want to play offline
30 | ;; @@
31 | ;; =>
32 | ;;; {"type":"html","content":"","value":"pr'ed value"}
33 | ;; <=
34 |
35 | ;; **
36 | ;;; ##Simple Scatter Plot
37 | ;; **
38 |
39 | ;; @@
40 | ;; If you just want to play offline, just use ``iplot``
41 | ;;
42 | ;;(let [n 200]
43 | ;; (-> (plotly (rnd/sample-normal n)
44 | ;; (rnd/sample-normal n))
45 | ;; (add-scatter :mode "markers")
46 | ;; iplot))
47 |
48 | ;; In order to make the output more visual when viewed through gorilla-repl viewer.
49 | ;; I publish the figure to plot.ly server first and then embed the iframe here
50 | (let [n 200]
51 | (-> (plotly (rnd/sample-normal n)
52 | (rnd/sample-normal n))
53 | (add-scatter :mode "markers")
54 | (plot "simple-scatter-plot" :fileopt "overwrite")
55 | embed-url))
56 | ;; @@
57 | ;; =>
58 | ;;; {"type":"html","content":"","value":"pr'ed value"}
59 | ;; <=
60 |
61 | ;; **
62 | ;;; ## Line and Scatter Plots
63 | ;; **
64 |
65 | ;; @@
66 | (let [n 100]
67 | (-> (plotly (rnd/sample-normal n))
68 | (add-scatter :mode "lines+markers")
69 | (add-scatter
70 | :y #(m/add 5 (d/column % 0)) ;; you can use a function which accept a dataset to create data
71 | :mode "markers")
72 | (add-scatter
73 | :y #(m/add -5 (d/column % :x)) ;; you can also use the default column name :x
74 | :mode "lines")
75 | (plot "line-and-scatter-plots" :fileopt "overwrite")
76 | embed-url))
77 | ;; @@
78 | ;; =>
79 | ;;; {"type":"html","content":"","value":"pr'ed value"}
80 | ;; <=
81 |
82 | ;; **
83 | ;;; ## Style Scatter Plots
84 | ;; **
85 |
86 | ;; @@
87 | (let [n 200]
88 | (-> (plotly)
89 | (add-scatter :x (rnd/sample-normal n)
90 | :y (m/add 2 (rnd/sample-normal n))
91 | :mode "markers"
92 | :name "above"
93 | :marker {:size 10
94 | :color "rgba(152, 0, 0, .8)"
95 | :line {:width 2
96 | :color "rgb(0, 0, 0)"}})
97 | (add-scatter :x (rnd/sample-normal n)
98 | :y (m/add -2 (rnd/sample-normal n))
99 | :mode "markers"
100 | :name "below"
101 | :marker {:color "rgba(255, 182, 193, .9)"
102 | :size 10
103 | :line {:width 2}})
104 | (set-layout :xaxis {:zeroline false}
105 | :yaxis {:zeroline false}
106 | :title "Styled Scatter")
107 | (plot "style-scatter-plots" :fileopt "overwrite")
108 | embed-url))
109 | ;; @@
110 | ;; =>
111 | ;;; {"type":"html","content":"","value":"pr'ed value"}
112 | ;; <=
113 |
114 | ;; **
115 | ;;; ## Data Labels on Hover
116 | ;; **
117 |
118 | ;; @@
119 | (def usa-states
120 | (let [in-file (slurp "https://raw.githubusercontent.com/plotly/datasets/master/2014_usa_states.csv")
121 | data (doall (csv/read-csv in-file))]
122 | (d/dataset (map keyword (first data)) (rest data))
123 | ))
124 | ;; @@
125 | ;; =>
126 | ;;; {"type":"html","content":"#'scatter/usa-states","value":"#'scatter/usa-states"}
127 | ;; <=
128 |
129 | ;; @@
130 | (m/pm usa-states)
131 | ;; @@
132 | ;; ->
133 | ;;; [[:rank :state :postal :pop]
134 | ;;; [ 1 Alabama AL 4849377.0]
135 | ;;; [ 2 Alaska AK 736732.0]
136 | ;;; [ 3 Arizona AZ 6731484.0]
137 | ;;; [ 4 Arkansas AR 2966369.0]
138 | ;;; [ 5 California CA 38802500.0]
139 | ;;; [ 6 Colorado CO 5355866.0]
140 | ;;; [ 7 Connecticut CT 3596677.0]
141 | ;;; [ 8 Delaware DE 935614.0]
142 | ;;; [ 9 District of Columbia DC 658893.0]
143 | ;;; [ 10 Florida FL 19893297.0]
144 | ;;; [ 11 Georgia GA 10097343.0]
145 | ;;; [ 12 Hawaii HI 1419561.0]
146 | ;;; [ 13 Idaho ID 1634464.0]
147 | ;;; [ 14 Illinois IL 12880580.0]
148 | ;;; [ 15 Indiana IN 6596855.0]
149 | ;;; [ 16 Iowa IA 3107126.0]
150 | ;;; [ 17 Kansas KS 2904021.0]
151 | ;;; [ 18 Kentucky KY 4413457.0]
152 | ;;; [ 19 Louisiana LA 4649676.0]
153 | ;;; [ 20 Maine ME 1330089.0]
154 | ;;; [ 21 Maryland MD 5976407.0]
155 | ;;; [ 22 Massachusetts MA 6745408.0]
156 | ;;; [ 23 Michigan MI 9909877.0]
157 | ;;; [ 24 Minnesota MN 5457173.0]
158 | ;;; [ 25 Mississippi MS 2994079.0]
159 | ;;; [ 26 Missouri MO 6063589.0]
160 | ;;; [ 27 Montana MT 1023579.0]
161 | ;;; [ 28 Nebraska NE 1881503.0]
162 | ;;; [ 29 Nevada NV 2839098.0]
163 | ;;; [ 30 New Hampshire NH 1326813.0]
164 | ;;; [ 31 New Jersey NJ 8938175.0]
165 | ;;; [ 32 New Mexico NM 2085572.0]
166 | ;;; [ 33 New York NY 19746227.0]
167 | ;;; [ 34 North Carolina NC 9943964.0]
168 | ;;; [ 35 North Dakota ND 739482.0]
169 | ;;; [ 36 Ohio OH 11594163.0]
170 | ;;; [ 37 Oklahoma OK 3878051.0]
171 | ;;; [ 38 Oregon OR 3970239.0]
172 | ;;; [ 39 Pennsylvania PA 12787209.0]
173 | ;;; [ 40 Puerto Rico PR 3548397.0]
174 | ;;; [ 41 Rhode Island RI 1055173.0]
175 | ;;; [ 42 South Carolina SC 4832482.0]
176 | ;;; [ 43 South Dakota SD 853175.0]
177 | ;;; [ 44 Tennessee TN 6549352.0]
178 | ;;; [ 45 Texas TX 26956958.0]
179 | ;;; [ 46 Utah UT 2942902.0]
180 | ;;; [ 47 Vermont VT 626562.0]
181 | ;;; [ 48 Virginia VA 8326289.0]
182 | ;;; [ 49 Washington WA 7061530.0]
183 | ;;; [ 50 West Virginia WV 1850326.0]
184 | ;;; [ 51 Wisconsin WI 5757564.0]
185 | ;;; [ 52 Wyoming WY 584153.0]]
186 | ;;;
187 | ;; <-
188 | ;; =>
189 | ;;; {"type":"html","content":"nil","value":"nil"}
190 | ;; <=
191 |
192 | ;; @@
193 | (defn parse-float
194 | [x]
195 | (Float/parseFloat x))
196 |
197 | (defn mark-usa
198 | [p i]
199 | (-> p
200 | (add-scatter :x :rank
201 | :y #(m/add (* i 1000000) (map parse-float (d/column % :pop)))
202 | :mode "markers"
203 | :marker {:size 14
204 | :color (str "hsl(" (float (* i (/ 360 (count usa-states)))) ",50%,50%)")
205 | :line {:width 1}
206 | :opacity 0.3}
207 | :name (+ 2000 i)
208 | :text (nth (d/column usa-states :state) i))))
209 |
210 | (-> (reduce mark-usa
211 | (plotly usa-states)
212 | (range (count usa-states)))
213 | (set-layout :showlegend false
214 | :hovermode "closest")
215 | (plot "data-labels-on-hover" :fileopt "overwrite")
216 | embed-url)
217 | ;; @@
218 | ;; =>
219 | ;;; {"type":"html","content":"","value":"pr'ed value"}
220 | ;; <=
221 |
222 | ;; **
223 | ;;; ## Scatter with a Color Dimension
224 | ;; **
225 |
226 | ;; @@
227 | (-> (plotly (rnd/sample-normal 100))
228 | (add-scatter :mode "markers"
229 | :marker {:size 16
230 | :color :x
231 | :colorscale "Viridis"
232 | :showscale true})
233 | (plot "scatter-with-a-color-dimension" :fileopt "overwrite")
234 | embed-url)
235 | ;; @@
236 | ;; =>
237 | ;;; {"type":"html","content":"","value":"pr'ed value"}
238 | ;; <=
239 |
240 | ;; **
241 | ;;; ## Categorical Dot Plot
242 | ;; **
243 |
244 | ;; @@
245 | (let [data {:country ["Switzerland (2011)" "Chile (2013)" "Japan (2014)" "United States (2012)" "Slovenia (2014)" "Canada (2011)" "Poland (2010)" "Estonia (2015)" "Luxembourg (2013)" "Portugal (2011)"]
246 | :voting_pop [40 45.7 52 53.6 54.1 54.2 54.5 54.7 55.1 56.6]
247 | :reg_voters [49.1 42 52.7 84.3 51.7 61.1 55.3 64.2 91.1 58.9]}]
248 |
249 | (-> (plotly data)
250 | (add-scatter
251 | :x :voting_pop
252 | :y :country
253 | :mode "markers"
254 | :name "Percent of estimated voting age population"
255 | :marker {:size 16
256 | :symbol "circle"
257 | :color "rgba(156, 165, 196, 0.95)"
258 | :line {:color "rgba(156, 165, 196, 1.0)"
259 | :width 1}})
260 | (add-scatter
261 | :x :reg_voters
262 | :y :country
263 | :name "Percent of estimated registered voters"
264 | :mode "markers"
265 | :marker {:color "rgba(204, 204, 204, 0.95)"
266 | :symbol "circle"
267 | :size 16
268 | :line {:color "rgba(217, 217, 217, 1.0)"
269 | :width 1}})
270 | (set-layout :title "Votes cast for ten lowest voting age population in OECD countries"
271 | :xaxis {:showgrid false
272 | :showline true
273 | :linecolor "rgb(102, 102, 102)"
274 | :titlefont {:color "rgb ()102,102,102"}
275 | :tickfont {:color "rgb(204, 204, 204)"
276 | :autotick false :dtick 10
277 | :ticks "outside"
278 | :tickcolor "rgb(102, 102, 102)"}}
279 | :margin {:l 140 :r 40 :b 50 :t 80}
280 | :legend {:font {:size 10}
281 | :yanchor "middle"
282 | :xanchor "right"}
283 | :width 800
284 | :height 600
285 | :paper_bgcolor "rgb(254, 247, 234)"
286 | :plot_bgcolor "rgb(254, 247, 234)"
287 | :hovermode "closest")
288 | (plot "categorial-dot-plot" :fileopt "overwrite")
289 | embed-url))
290 | ;; @@
291 | ;; =>
292 | ;;; {"type":"html","content":"","value":"pr'ed value"}
293 | ;; <=
294 |
295 | ;; **
296 | ;;; ## Large Data Sets
297 | ;;;
298 | ;;; Because the params of ``add-scatter`` will overwrite any predetermined settings, we can just set the ``:type`` to ``"scattergl"`` and then plotly will use WebGL instead of SVG.
299 | ;;;
300 | ;;; **Be careful to use this feature!**
301 | ;; **
302 |
303 | ;; @@
304 | (let [n 10000]
305 | (-> (plotly (rnd/sample-normal n) (rnd/sample-normal n))
306 | (add-scatter :mode "markers"
307 | :type "scattergl" ;; you can set a larger n and comment out this line,
308 | ;; then you will see the load difference
309 | :marker {:color "FFBAD2"
310 | :line {:width 1}})
311 | (plot "large-data-sets" :fileopt "overwrite")
312 | embed-url))
313 | ;; @@
314 | ;; =>
315 | ;;; {"type":"html","content":"","value":"pr'ed value"}
316 | ;; <=
317 |
318 | ;; **
319 | ;;; ## Custom Color Scales
320 | ;; **
321 |
322 | ;; @@
323 | (def iris
324 | (let [in-file (slurp "https://raw.githubusercontent.com/uiuc-cse/data-fa14/gh-pages/data/iris.csv")
325 | data (doall (csv/read-csv in-file))]
326 | (d/dataset (map keyword (first data)) (rest data))
327 | ))
328 | ;; @@
329 | ;; =>
330 | ;;; {"type":"html","content":"#'scatter/iris","value":"#'scatter/iris"}
331 | ;; <=
332 |
333 | ;; @@
334 | (-> (plotly iris)
335 | (group-dataset :species)
336 | (add-scatter :x :sepal_length
337 | :y :petal_length
338 | :mode "markers"
339 | :gp-colors ["red" "green" "purple"]
340 | :gp-symbols ["circle" "x" "circle-open"])
341 | (set-layout :hovermode "closest")
342 | (plot "custom-color-scales" :fileopt "overwrite")
343 | embed-url)
344 | ;; @@
345 | ;; =>
346 | ;;; {"type":"html","content":"","value":"pr'ed value"}
347 | ;; <=
348 |
--------------------------------------------------------------------------------
/examples/subplot.clj:
--------------------------------------------------------------------------------
1 | ;; gorilla-repl.fileformat = 1
2 |
3 | ;; **
4 | ;;; # Subplots
5 | ;; **
6 |
7 | ;; @@
8 | (ns scatter
9 | (:require [clojure.core.matrix :as m]
10 | [clojure.core.matrix.dataset :as d]
11 | [clojure.core.matrix.random :as rnd]
12 | [clojure.data.csv :as csv])
13 | (:use [plotly-clj.core] :reload-all))
14 | ;; @@
15 | ;; =>
16 | ;;; {"type":"html","content":"nil","value":"nil"}
17 | ;; <=
18 |
19 | ;; @@
20 | (online-init)
21 | ;; @@
22 | ;; =>
23 | ;;; {"type":"html","content":"","value":"pr'ed value"}
24 | ;; <=
25 |
26 | ;; @@
27 | (-> (plotly)
28 | (add-scatter :x [1 2 3] :y [2 3 4])
29 | (add-scatter :x [20 30 40] :y [50 60 70])
30 | (subplot :nrow 1 :ncol 2)
31 | (plot "simple subplots 1" :fileopt "overwrite")
32 | embed-url)
33 | ;; @@
34 | ;; =>
35 | ;;; {"type":"html","content":"","value":"pr'ed value"}
36 | ;; <=
37 |
38 | ;; @@
39 | (-> (plotly)
40 | (add-scatter :x [1 2 3] :y [2 3 4])
41 | (add-scatter :x [20 30 40] :y [5 5 5])
42 | (add-scatter :x [2 3 4] :y [600 700 800])
43 | (add-scatter :x [4000 5000 6000] :y [7000 8000 9000])
44 | (subplot :nrow 2 :ncol 2)
45 | (plot "simple subplots 2" :fileopt "overwrite")
46 | embed-url)
47 | ;; @@
48 | ;; =>
49 | ;;; {"type":"html","content":"","value":"pr'ed value"}
50 | ;; <=
51 |
52 | ;; @@
53 | (-> (plotly)
54 | (plot-seq
55 | (for [_ (range 10)]
56 | #(add-scatter % :x [1 2 3] :y [2 3 4])))
57 | (subplot :nrow 2 :ncol 5 :sharex true)
58 | (plot "simple subplots 3" :fileopt "overwrite")
59 | embed-url)
60 | ;; @@
61 | ;; =>
62 | ;;; {"type":"html","content":"","value":"pr'ed value"}
63 | ;; <=
64 |
65 | ;; @@
66 | (-> (plotly)
67 | (add-scatter :x [1 2 3] :y [2 3 4])
68 | (add-scatter-3d :x [1 2 3] :y [2 3 4] :z [1 1 1])
69 | (add-scatter :x [1 2 3] :y [2 3 4])
70 | (add-scatter-3d :x [1 2 3] :y [2 3 4] :z [1 1 1])
71 | (subplot :nrow 2)
72 | (plot "simple subplots 4" :fileopt "overwrite")
73 | embed-url)
74 | ;; @@
75 | ;; =>
76 | ;;; {"type":"html","content":"","value":"pr'ed value"}
77 | ;; <=
78 |
79 | ;; @@
80 |
81 | ;; @@
82 |
--------------------------------------------------------------------------------
/examples/surface.clj:
--------------------------------------------------------------------------------
1 | ;; gorilla-repl.fileformat = 1
2 |
3 | ;; **
4 | ;;; #Surface
5 | ;; **
6 |
7 | ;; @@
8 | (ns scatter
9 | (:require [clojure.core.matrix :as m]
10 | [clojure.core.matrix.dataset :as d]
11 | [clojure.core.matrix.random :as rnd]
12 | [clojure.data.csv :as csv])
13 | (:use [plotly-clj.core] :reload-all))
14 | ;; @@
15 | ;; =>
16 | ;;; {"type":"html","content":"nil","value":"nil"}
17 | ;; <=
18 |
19 | ;; @@
20 | (online-init)
21 | ;; @@
22 | ;; =>
23 | ;;; {"type":"html","content":"","value":"pr'ed value"}
24 | ;; <=
25 |
26 | ;; @@
27 | (def data
28 | (let [in-file (slurp "https://raw.githubusercontent.com/plotly/datasets/master/api_docs/mt_bruno_elevation.csv")
29 | data (doall (csv/read-csv in-file))]
30 | (m/emap #(Float/parseFloat %) (d/dataset (rest data)))
31 | ))
32 | ;; @@
33 | ;; =>
34 | ;;; {"type":"html","content":"#'scatter/data","value":"#'scatter/data"}
35 | ;; <=
36 |
37 | ;; @@
38 | (-> (plotly)
39 | (add-surface :z (m/to-nested-vectors data))
40 | (plot "3D surface" :fileopt "overwrite")
41 | embed-url)
42 | ;; @@
43 | ;; =>
44 | ;;; {"type":"html","content":"","value":"pr'ed value"}
45 | ;; <=
46 |
47 | ;; @@
48 | (-> (plotly)
49 | (add-surface :z [[0 0 0]
50 | [0 1 0]
51 | [0 0 0]
52 | [0 -1 0]
53 | [0 0 0]])
54 | (plot "3D surface 2" :fileopt "overwrite")
55 | embed-url)
56 | ;; @@
57 | ;; =>
58 | ;;; {"type":"html","content":"","value":"pr'ed value"}
59 | ;; <=
60 |
61 | ;; @@
62 |
63 | ;; @@
64 |
--------------------------------------------------------------------------------
/project.clj:
--------------------------------------------------------------------------------
1 | (defproject plotly-clj "0.1.1"
2 | :description "Plotly Clojure Client"
3 | :url "https://github.com/findmyway/plotly-clj"
4 | :license {:name "MIT License"
5 | :url "https://opensource.org/licenses/MIT"
6 | :year 2017
7 | :key "mit"}
8 | :dependencies [[org.clojure/clojure "1.8.0"]
9 | [hiccup "1.0.5"]
10 | [org.clojure/data.json "0.2.6"]
11 | [net.mikera/core.matrix "0.57.0"]
12 | [com.rpl/specter "0.13.2"]
13 | [http-kit "2.2.0"]
14 | [lein-gorilla "0.4.0"]
15 | [org.clojure/data.csv "0.1.3"]]
16 | :plugins [[lein-gorilla "0.4.0"]]
17 | :repositories [["releases" {:url "https://clojars.org/repo"
18 | :creds :gpg}]])
19 |
--------------------------------------------------------------------------------
/src/plotly_clj/core.clj:
--------------------------------------------------------------------------------
1 | (ns plotly-clj.core
2 | (:require [hiccup.core :refer [html]]
3 | [clojure.data.json :as json]
4 | [clojure.core.matrix :as m]
5 | [clojure.core.matrix.random :as rnd]
6 | [clojure.core.matrix.dataset :as d]
7 | [com.rpl.specter :as sp]
8 | [clojure.set :as s]
9 | [plotly-clj.scale :refer [scale-color factor]]
10 | [gorilla-renderable.core :as render]
11 | [clojure.java.io :refer [resource make-parents as-file]]
12 | [org.httpkit.client :as http]
13 | [clojure.java.browse :refer [browse-url]]
14 | [clojure.string :as string])
15 | (:import [java.io File]))
16 |
17 | (declare group?)
18 |
19 | (defn make-div
20 | "Make a div element given traces, layout and configs"
21 | [[traces layout configs]]
22 | (let [uuid (str (java.util.UUID/randomUUID))]
23 | (html [:div.plotly-graph-div {:id uuid}]
24 | [:script {:type "text/javascript"}
25 | (str "window.PLOTLYENV=window.PLOTLYENV || {};"
26 | "window.PLOTLYENV.BASE_URL=\"https://plot.ly\";"
27 | "Plotly.newPlot(\"" uuid "\"," traces "," layout "," configs ")")])))
28 | (defn wrap-div
29 | "Wrap a div element in a html page"
30 | [div]
31 | (let [content (html [:html
32 | [:head [:meta {:charset "utf-8"}]]
33 | [:body
34 | [:script {:type "text/javascript"}
35 | (slurp (resource "plotly.min.js"))]
36 | div]])]
37 | content))
38 |
39 | (defn to-string
40 | "Format a plot object"
41 | [p]
42 | (map json/write-str (vals (select-keys p [:traces :layout :configs]))))
43 |
44 | (defn- make-plotly
45 | "Initial a plotly object with dataset"
46 | [ds]
47 | {:ds ds :traces [] :layout {} :configs {}})
48 |
49 | (defn- make-dataset
50 | "A flexiable function to create a dataset from different kinds of "
51 | ([] nil)
52 | ([x]
53 | (cond
54 | (nil? x) nil
55 | (d/dataset? x) x
56 | (group? x) x
57 | (= 1 (m/dimensionality x)) (d/dataset {:x x})
58 | ;; The x maybe a column-based map or row-based matrix.
59 | :else (d/dataset x)))
60 | ([x y]
61 | (d/dataset {:x x :y y}))
62 | ([x y z]
63 | (d/dataset {:x x :y y :z z})))
64 |
65 | (defn plotly
66 | "Make a plotly object from:
67 | 1. a vector (will be given name :x)
68 | 2. two vectors (will be given name :x and :y)
69 | 3. three vectors (will be given name :x, :y and :z)
70 | 4. a dataset(clojure.core.matrix.dataset)
71 | TODO: add checkers
72 | "
73 | [& xs]
74 | (make-plotly (apply make-dataset xs)))
75 |
76 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
77 | (defn deep-merge [a b]
78 | "Used to merge recursive maps"
79 | (merge-with (fn [x y]
80 | (cond
81 | (map? y) (deep-merge x y)
82 | (sequential? y) (vec (concat x y))
83 | :else y))
84 | a b))
85 |
86 | (defn set-dataset
87 | "Set the dataset of a plotly object.
88 | xs is the same input as ``plotly``"
89 | [p & xs]
90 | (assoc p :ds (apply make-dataset xs)))
91 |
92 | (defn set-layout
93 | "Set the layout of a plotly object.
94 | This function will clear all the layout and reset the layout!
95 | So functions like `add-annotations` will not work if called before
96 | this function."
97 | [p & {:as params}]
98 | (assoc p :layout params))
99 |
100 | (defn update-layout
101 | "Update the layout of a plotly object"
102 | [p & {:as params}]
103 | (update p :layout #(deep-merge % params)))
104 |
105 | (defn set-configs
106 | "Set configs of a plotly object"
107 | [p & {:as params}]
108 | (assoc p :configs params))
109 |
110 | (defn update-configs
111 | "Update configs of a plotly object"
112 | [p & {:as params}]
113 | (update p :configs #(deep-merge % params)))
114 |
115 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
116 | (defn- group-dataset-by
117 | "Helper function for group-dataset"
118 | [p ks]
119 | (let [group-idx (group-by #(nth ks %) (range (count (:ds p))))
120 | idx->dataset (fn [[k idx]] (vector k (d/select-rows (:ds p) idx)))]
121 | (assoc p :ds (into {} (map idx->dataset group-idx)))))
122 |
123 | (defn group-dataset
124 | "Group a dataset based on k.
125 | k can be
126 | 1. a column name in dataset
127 | 2. a sequence of column names
128 | 3. a seq which has the same length as the dataset
129 | 4. a function takes dataset as input and
130 | returns a seq which has the same length as the dataset
131 | TODO:
132 | defmulti
133 | add checkers"
134 | [p k]
135 | (cond
136 | (keyword? k) ;; a column name
137 | (group-dataset-by p (d/column (:ds p) k))
138 | (and (seq?) (every? keyword? k)) ;; a seq of column names
139 | (group-dataset-by p (d/select-columns (:ds p) k))
140 | (fn? k)
141 | (group-dataset-by p (k (:ds p)))
142 | :else
143 | (group-dataset-by p k)))
144 |
145 | (defn group?
146 | "Check if dataset is a grouped dataset"
147 | [d]
148 | (and (map? d)
149 | (every? d/dataset? (vals d))))
150 |
151 | (defn merge-group-dataset
152 | "Merge the dataset group of a plotly object into dataset"
153 | [p]
154 | (let [ds (:ds p)]
155 | (if (group? ds)
156 | (assoc p :ds (apply d/merge-datasets (vals ds)))
157 | p)))
158 |
159 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
160 | (defn trans-params
161 | "TODO: Add checkers.
162 | marker size must be numerical
163 | marker color can be numerical or categorized"
164 | [params dataset]
165 | (->> params
166 |
167 | (sp/transform
168 | (sp/multi-path
169 | [(sp/must :marker) (sp/must :size) keyword?]
170 | [(sp/must :x) keyword?]
171 | [(sp/must :y) keyword?]
172 | [(sp/must :z) keyword?])
173 | #(d/column dataset %))
174 |
175 | (sp/transform
176 | [(sp/must :marker) (sp/must :color) keyword?]
177 | #(factor (d/column dataset %)))
178 |
179 | (sp/transform
180 | (sp/multi-path
181 | [(sp/must :x) fn?]
182 | [(sp/must :y) fn?]
183 | [(sp/must :z) fn?]
184 | [(sp/must :marker) (sp/must :size) fn?]
185 | [(sp/must :marker) (sp/must :color) fn?])
186 | #(% dataset))))
187 |
188 | (defn trans-gp-params
189 | "Transform grouped params"
190 | [params ks]
191 | (let [gp-colors (or (:gp-colors params) (scale-color ks))
192 | gp-names (or (:gp-names params) ks)
193 | gp-symbols (or (:gp-symbols params) (range (count ks)))
194 | gp-texts (or (:gp-texts params) ks)]
195 | (for [i (range (count ks))]
196 | (-> params
197 | (dissoc :gp-colors :gp-names :gp-texts)
198 | (update :name #(or % (nth gp-names i)))
199 | (update :text #(or % (nth gp-texts i)))
200 | (update-in [:marker :color] #(or % (nth gp-colors i)))
201 | (update-in [:marker :symbol] #(or % (nth gp-symbols i)))
202 | (#(mapcat identity %))))))
203 |
204 | (defn get-default-xy
205 | "Get default x and y from dataset.
206 |
207 | If the columns of dataset contains both :x and :y,
208 | then the coresponding columns will be selected.
209 | Else the first two columns will be selected
210 | (only if the column size of the dataset is >= 2).
211 |
212 | If the dataset contains only one column
213 | :y will be the only column and
214 | :x will be a vector of indexes."
215 | [dataset]
216 | (cond
217 | (s/subset? #{:x :y} (set (d/column-names dataset)))
218 | (d/to-map (d/select-columns dataset [:x :y]))
219 | (<= 2 (count (d/column-names dataset)))
220 | {:x (d/column dataset 0) :y (d/column dataset 1)}
221 | (= 1 (count (d/column-names dataset)))
222 | {:x (vec (range (count dataset))) :y (d/column dataset 0)}
223 | :else {}))
224 |
225 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
226 | (defn plot-seq
227 | "Apply a sequence add-fn commands to a plotly object.
228 | cmds is a sequence of functions that take a plotly object as param."
229 | [p cmds]
230 | (reduce #(%2 %1) p cmds))
231 |
232 | (def traces-3d #{"scatter3d" "surface" "mesh3d"})
233 |
234 | (defn subplot
235 | "Bind each trace to a xaxis and a yaxis.
236 | This function supports only limited params currently.
237 |
238 | Example:
239 | traces: [trace1 trace2 trace3 trace4 trace5 trace6]
240 | nrow: 2
241 | ncol: 3
242 | x-bind(sharex true):
243 | [[1 2]
244 | [1 2]
245 | [1 2]]
246 | y-bind(sharey true):
247 | [[1 1]
248 | [2 2]
249 | [3 3]]
250 | x-bind(sharex false):
251 | [[1 2]
252 | [3 4]
253 | [5 6]]
254 | y-bind(sharey false):
255 | [[1 4]
256 | [2 5]
257 | [3 6]]
258 |
259 | x-domains: ([0 0.4][0.6 1][0 0.4][0.6 1][0 0.4][0.6 1])
260 | y-domains: ([0.7333 1][0.3666 0.6333][0 0.2666][0.7333 1][0.3666 0.6333][0 0.2666])
261 | TODO: Check nrow ncol zero?"
262 | [p & {:keys [nrow ncol sharex sharey reversey titles]}]
263 | (let [n (count (:traces p))
264 | [ncol nrow] (cond
265 | (and ncol nrow) [ncol nrow]
266 | ncol [ncol (inc (quot (dec n) ncol))]
267 | nrow [(inc (quot (dec n) nrow)) nrow]
268 | :else [1 n])
269 | x-bind (if sharex
270 | (m/array (repeat nrow (range 1 (inc ncol))))
271 | (m/reshape (range 1 (inc (* ncol nrow))) [nrow ncol]))
272 | y-bind (if sharey
273 | (m/transpose (m/array (repeat ncol (range 1 (inc nrow)))))
274 | (m/transpose (m/reshape (range 1 (inc (* ncol nrow))) [ncol nrow])))
275 | get-domains (fn [n] (let [gap (if (= 1 n) 0 (/ 0.1 (dec n)))
276 | span (if (= 1 n) 1 (/ 0.9 n))]
277 | (map #(vector (* % (+ span gap))
278 | (+ (* gap %) (* span (inc %))))
279 | (range n))))
280 | x-domains (apply concat (repeat nrow (get-domains ncol)))
281 | y-domains (apply concat (repeat ncol (if reversey
282 | (get-domains nrow)
283 | (reverse (get-domains nrow)))))
284 |
285 | is-trace-3d (map #(traces-3d (:type %)) (:traces p))
286 | scenes (map-indexed (fn [i is-3d]
287 | (when is-3d
288 | (let [x (nth (m/eseq x-bind) i)
289 | y (nth (m/eseq y-bind) i)]
290 | {(keyword (str "scene" (inc i)))
291 | {:domain {:x (nth x-domains (dec x))
292 | :y (nth y-domains (dec y))}}})))
293 | is-trace-3d)
294 | x-axis (map-indexed (fn [i is-3d]
295 | (when-not is-3d
296 | (let [x (nth (m/eseq x-bind) i)
297 | y (nth (m/eseq y-bind) i)]
298 | {(keyword (str "xaxis" x))
299 | {:domain (nth x-domains (dec x))
300 | :anchor (str "y" y)}})))
301 | is-trace-3d)
302 | y-axis (map-indexed (fn [i is-3d]
303 | (when-not is-3d
304 | (let [x (nth (m/eseq x-bind) i)
305 | y (nth (m/eseq y-bind) i)]
306 | {(keyword (str "yaxis" y))
307 | {:domain (nth y-domains (dec y))
308 | :anchor (str "x" x)}})))
309 | is-trace-3d)]
310 | (-> p
311 | (update :traces #(map (fn [t i x y]
312 | (if (traces-3d (:type t))
313 | (assoc t :scene (str "scene" i))
314 | (-> t
315 | (assoc :xaxis (str "x" x))
316 | (assoc :yaxis (str "y" y)))))
317 | % (range 1 (inc (count %))) (m/eseq x-bind) (m/eseq y-bind)))
318 | (update :layout #(apply merge % scenes))
319 | (update :layout #(apply merge % x-axis))
320 | ;; The reverse is needed here, which makes y-axis align to left if sharey is set.
321 | (update :layout #(apply merge % (reverse y-axis))))))
322 |
323 | (defn add-annotations
324 | "This function will update the layout of a plotly object."
325 | [p ann]
326 | (cond
327 | (map? ann) ;; only one annotation
328 | (update-layout p :annotations [ann])
329 | (sequential? ann)
330 | (update-layout p :annotations ann)
331 | :else
332 | p))
333 |
334 | (defn add-fn
335 | "Add general trace to a plotly object.
336 | This function supports grouped dataset.
337 | "
338 | [{:keys [ds traces] :as p}
339 | add-type
340 | params]
341 | (cond
342 | (group? ds)
343 | (let [gp-ds (vec (vals ds))
344 | gp-params (vec (trans-gp-params params (keys ds)))
345 | p-new (reduce #(apply add-fn
346 | (assoc %1 :ds (nth gp-ds %2))
347 | (nth gp-params %2))
348 | p
349 | (range (count ds)))]
350 | ;; recover dataset
351 | (set-dataset p-new ds))
352 |
353 | (nil? ds)
354 | (update p :traces #(conj % (merge {:type add-type} params)))
355 |
356 | :else
357 | (let [params (trans-params params ds)
358 | data (get-default-xy ds)
359 | trace (merge {:type add-type} data params)]
360 | (update p :traces #(conj % trace)))))
361 |
362 | (def add-scatter (fn [p & {:as params}] (add-fn p "scatter" params)))
363 | (def add-bar (fn [p & {:as params}] (add-fn p "bar" params)))
364 | (def add-histogram (fn [p & {:as params}] (add-fn p "histogram" params)))
365 | (def add-histogram-2d (fn [p & {:as params}] (add-fn p "histogram2d" params)))
366 |
367 | (defn add-fn-3d
368 | "Add general 3d trace."
369 | [{:keys [ds traces] :as p}
370 | add-type
371 | params]
372 | (let [params (trans-params params ds)
373 | trace (merge {:type add-type} params)]
374 | (update p :traces #(conj % trace))))
375 |
376 | (def add-scatter-3d (fn [p & {:as params}] (add-fn-3d p "scatter3d" params)))
377 | (def add-surface (fn [p & {:as params}] (add-fn-3d p "surface" params)))
378 | (def add-mesh3d (fn [p & {:as params}] (add-fn-3d p "mesh3d" params)))
379 | (def add-contour (fn [p & {:as params}] (add-fn-3d p "contour" params)))
380 | (def add-heatmap (fn [p & {:as params}] (add-fn-3d p "heatmap" params)))
381 |
382 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
383 | (def cred-path (str (System/getProperty "user.home") (File/separator) ".plotly" (File/separator) ".credentials"))
384 |
385 | (defn set-credentials
386 | "Write the username and api-key into local file ~/.plotly/.credentials
387 | This function only needs to be called once."
388 | [username api-key]
389 | (let [cred {:username username :api-key api-key}]
390 | (when-not (.exists (as-file cred-path))
391 | (make-parents cred-path))
392 | (spit cred-path (json/write-str cred))))
393 |
394 | (defn get-credentials
395 | "Read username and api-key from local file.
396 | The default credential file path is ~/.plotly/.credentials"
397 | []
398 | (json/read-str (slurp cred-path) :key-fn keyword))
399 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
400 | (defn save-html
401 | "Save to local html."
402 | ([p filename] (save-html p filename nil))
403 | ([p filename is-open]
404 | (do (make-parents filename)
405 | (-> p
406 | to-string
407 | make-div
408 | wrap-div
409 | (#(spit filename %)))
410 | (when is-open (browse-url filename)))))
411 |
412 | (defn plot
413 | "Publish to server.
414 | Return the generated url of figure.
415 | TODO: Add support for api of V2"
416 | [{:keys [traces layout]}
417 | filename
418 | & {:keys [fileopt world-readable] :as params}]
419 | (let [{:keys [username api-key]} (get-credentials)
420 | data (merge {:origin "plot"
421 | :platform "clojure"
422 | :args (json/write-str traces)
423 | :kwargs (json/write-str {:filename filename
424 | :fileopt (or fileopt "new")
425 | :layout layout
426 | :world_readable (if (nil? world-readable) true world-readable)})}
427 | {:un username :key api-key})
428 | resp @(http/post "https://plot.ly/clientresp" {:form-params data})
429 | res (json/read-str (:body resp) :key-fn keyword)]
430 | (let [url (:url res)]
431 | (if (empty? url)
432 | (:error res)
433 | url))))
434 |
435 | ;;;;;;;;;;;;;;;;;;;;;;;;;;===== Gorilla REPL rendering =====;;;;;;;;;;;;;;;;;;;;;;;;
436 | (defrecord PlotlyView [contents])
437 |
438 | (defn plotly-view [p] (PlotlyView. p))
439 |
440 | (extend-type PlotlyView
441 | render/Renderable
442 | (render [self] {:type :html :content (:contents self) :value "pr'ed value"}))
443 |
444 | (defn online-init
445 | "Insert the plotly.min.js into gorilla repl"
446 | []
447 | (plotly-view
448 | (html [:script {:type "text/javascript"
449 | :src "https://cdn.plot.ly/plotly-latest.min.js"}])))
450 |
451 | (defn offline-init
452 | "Insert the plotly.min.js into gorilla repl"
453 | []
454 | (plotly-view
455 | (html [:script {:type "text/javascript"}
456 | (slurp (resource "plotly.min.js"))])))
457 |
458 | (defn iplot
459 | "Inline plot"
460 | [p]
461 | (-> p
462 | to-string
463 | make-div
464 | plotly-view))
465 |
466 | (defn embed-url
467 | [url & {:keys [width height]}]
468 | (plotly-view
469 | (html [:iframe {:width (or width "800")
470 | :height (or height "600")
471 | :src (str (string/replace-first url "https:" "")
472 | ".embed")}])))
473 |
--------------------------------------------------------------------------------
/src/plotly_clj/scale.clj:
--------------------------------------------------------------------------------
1 | (ns plotly-clj.scale
2 | (:require [clojure.core.matrix :as m]
3 | [clojure.core.matrix.dataset :as d]))
4 |
5 | (defn scale-size
6 | "TODO:
7 | Check every? number?
8 | 0 <= min-size <= max-size"
9 | ([xs]
10 | (scale-size xs 10 50))
11 | ([xs min-size max-size]
12 | (let [min-x (m/emin xs)
13 | max-x (m/emax xs)]
14 | (if (= max-x min-x)
15 | (m/add (m/zero-array (m/shape xs)) min-size)
16 | (m/add min-size
17 | (m/mul (- max-size min-size)
18 | (m/div (m/add xs (- min-x))
19 | (- max-x min-x))))))))
20 |
21 | (defn factor
22 | [xs]
23 | (if (every? number? xs)
24 | xs
25 | (let [val-idx (into {} (map-indexed #(vector %2 %1) (set xs)))]
26 | (mapv val-idx xs))))
27 |
28 | (defn scale-color
29 | [xs]
30 | (if (every? number? xs)
31 | xs
32 | (let [val-idx (into {} (map-indexed #(vector %2 %1) (set xs)))
33 | interval (float (/ 360 (count val-idx)))]
34 | (map #(str "hsl(" (* (val-idx %) interval) ",0.5,0.5)") xs))))
35 |
--------------------------------------------------------------------------------
/test/plotly_clj/core_test.clj:
--------------------------------------------------------------------------------
1 | (ns plotly-clj.core-test
2 | (:require [clojure.test :refer :all]
3 | [plotly-clj.core :refer :all]))
4 |
5 | (deftest a-test
6 | (testing "FIXME, I fail."
7 | (is (= 0 1))))
8 |
--------------------------------------------------------------------------------