-
Notifications
You must be signed in to change notification settings - Fork 34
/
solutions.Rmd
348 lines (262 loc) · 9.86 KB
/
solutions.Rmd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
# Solutions <i class="fa fa-check-square-o"></i>
### Web tech: shapes {-}
[Solution](solutions/webtech_shapes.html){target="_blank"}
*Contributed by Tracy Liu*
### D3 in the Console: green circles {-}
1. Select the circle with ID “henry” and make it blue.
``` js
d3.select("svg")
.select("circle#henry")
.transition() // optional
.duration(1000) // optional
.attr("fill","blue");
```
2. Select all circles of “apple” class make them red.
``` js
d3.select("svg")
.selectAll("circle.apple")
.attr("fill", "red");
```
3. Select the first circle and add an orange border (“stroke”), and stroke width (“stroke-width”) of 5.
``` js
d3.select("svg")
.select("circle")
.attr("stroke", "orange")
.attr("stroke-width", "5");
```
4. Select all circles of “apple” class and move them to the middle of the svg.
``` js
d3.select("svg")
.selectAll("circle.apple")
.transition()
.duration(2000)
.attr("cx", d3.select("svg").attr("width") / 2)
.attr("cy", d3.select("svg").attr("height") / 2);
```
*Contributed by Tracy Liu*
### D3 in the Console: blue circles {-}
1. Move all the circles to the right.
``` js
const svg = d3.select("svg")
svg.selectAll("circle")
.transition()
.duration(2000)
.attr("cx", 450);
```
2. Move them back to the left *and* change their color.
``` js
svg.selectAll("circle")
.transition()
.duration(2000)
.attr("cx", 50)
.attr("fill", "red");
```
3. In a text editor, add an id to the third circle in `six_blue_circles.html`, save the file, and then in the Console, move only that circle to the right.
``` js
svg.select("circle#third")
.transition()
.duration(2000)
.attr("cx", "450");
```
4. Move all the circles to the middle of the screen, *then* move them all to the same location.
``` js
svg.selectAll("circle")
.transition()
.duration(2000)
.attr("cx", 250)
.transition()
.duration(2000)
.attr("cy", 200);
```
(Extra: return the circles to their starting positions.)
``` js
const circ = svg.selectAll("circle")
.data([100, 150, 200, 250, 300, 350]);
circ.transition()
.duration(2000)
.attr("cx", 50)
.attr("cy", d => d)
.attr("fill", "blue");
```
*Contributed by Tracy Liu*
### D3 in the Console: data bind {-}
``` js
const bluedata = [30, 20, 40, 70, 120, 70];
const circ = d3.select("svg")
.selectAll("circle")
.data(bluedata);
circ.transition()
.duration(2000)
.attr("fill", "pink")
.attr("r", d => d / 2)
.attr("cx", d => 3 * d);
```
*Contributed by Tracy Liu*
### Update, Enter, and Exit: horizontal bar chart {-}
For this exercise open up your text editor. We will be operating inside the `<script>` tags.
Our goal is to create a horizontal bar chart.
The first step is to create the svg element where our graph will live in. We want to make sure to make it tall and wide enough to hold the data of the given array. We also store our data in an array.
``` js
const bardata = [300, 100, 150, 225, 75, 275];
const svg = d3.select("body")
.append("svg")
.attr("width", "700")
.attr("height", "400");
```
Next we will bind the data to the DOM rectangles and store the selection in a variable called `bars`. Since there are no DOM rectangles (yet), the update and exit selections will be empty. Note that nothing will appear on the screen yet since the enter elements are not combined with DOM elements.
``` js
const bars = svg.selectAll("rect")
.data(bardata);
```
Next step is to create DOM elements from the enter selection and give them the correct attributes to form a horizonal barchart. First we will set `x` to zero and then in order to set the `y` distances between the bars we will use each datapoint's index. The mapping function for the `y` coordinate is `(d, i) => i * 25 + 10` works as follows:
When i = 0, y = 10, when i = 1, y = 35, when i = 2, y = 60, etc.
Lastly, we want each rectangle's width to be equal to its data value.
``` js
bars.enter()
.append("rect")
.attr("x", 0)
.attr("y", (d, i) => i * 25 + 10)
.attr("width", d => d)
.attr("height", "20")
.attr("fill", "pink");
```
The complete solution is [here](solutions/horizontal_bar_chart.html){target="_blank"}
*Contributed by Kassie Papasotiriou*
### Update, Enter, and Exit: merge {-}
1. Change the data to any six other values and update the lengths of the bars.
console:
``` js
const bardata = [130, 210, 90, 300, 200, 50];
const svg = d3.select("svg");
const bars = svg.selectAll("rect")
.data(bardata);
bars.transition()
.duration(2000)
.attr("width", d => d);
```
2. Bind a new dataset, `newbardata` to the bars, update the bar lengths, and remove any extra bars.
``` js
const newbardata = [250, 125, 80, 100];
const svg = d3.select("svg");
const bars = svg.selectAll("rect")
.data(newbardata);
bars.transition()
.duration(2000)
.attr("width", d => d)
bars.exit()
.transition()
.duration(2000)
.attr("width", 0)
.remove();
```
3. Bind a new dataset, `reallynewbardata`, to the bars, then add additional bars so each data value has a bar. Make the outline (stroke) of the new bars a different color.
``` js
const reallynewbardata = [300, 100, 250, 50, 200, 150, 325, 275];
const svg = d3.select("svg");
const bars = svg.selectAll("rect")
.data(reallynewbardata);
bars.transition()
.duration(2000)
.attr("width", d => d)
bars.enter()
.append("rect")
.attr("width", 0)
.attr("x", 30)
.attr("y", (d, i) => 50 * i)
.attr("height", 35)
.attr("width", d => d)
.attr("fill", "lightgreen")
.attr("stroke", "pink")
.attr("stroke-width", 5);
```
4. Use `.merge()` to combine the update and enter selections into one selection and then transition the length of all of the bars to half their current length.
``` js
const reallynewbardata = [300, 100, 250, 50, 200, 150, 325, 275];
const svg = d3.select("svg");
const bars = svg.selectAll("rect")
.data(reallynewbardata);
bars.enter()
.append("rect")
.attr("x", 30)
.attr("y", (d, i) => 50 * i)
.attr("height", 35)
.attr("width", d => d)
.attr("fill", "lightgreen")
.attr("stroke", "pink")
.attr("stroke-width", 5)
.merge(bars)
.transition()
.duration(2000)
.attr("height", "35")
.attr("width", d => d);
```
5. Add text labels inside the bars at the right end with the length of the bar in pixels.
``` js
const reallynewbardata = [300, 100, 250, 50, 200, 150, 325, 275];
const svg = d3.select("svg");
const bartext = svg.selectAll("text")
.data(reallynewbardata);
bartext.enter()
.append("text")
.attr("x", d => d - 5)
.attr("y", (d, i) => 50 * i + 12)
.text(d => d);
```
*Contributed by Tracy Liu*
### Update, Enter, and Exit: functions {-}
**Question Answers**
Since there are no questions we will try out a few things and explain why the bars are behaving that way. Open up the file on Chrome and type the following in you Console:
1. By running the code below we are rebinding the new data to the DOM elements. In this case, the inital data and the new data have the same length so no new elements will be added and none will be removed. We update the data, change the fill and then change the width.
``` js
update([100, 200, 300, 400, 200, 300]);
```
2. By running the code below, the two data values are bound to the first two DOM elements. The color and width are changed the extra DOM elements that did not receive data values are removed.
``` js
update([200, 400]);
```
3. By running the code below (without refreshing the page from 2.), we bind the data, and two of the values will be in the enter selection. For those two values we create two more DOM elements, fill them with yellow and adjust their width. Next we merge all elements together and to the merge selection we readjust the widths to make sure that all elements have the correct length and we turn them to orange. Note that since in this scenario we had enter elements there will be nothing in the exit selection.
``` js
update([200, 300, 200, 125]);
```
### Update, Enter, and Exit: vertical bar chart {-}
**Question Answers**
In order to change a horizontal bar chart to a vertical barchart we need to first flip the `x` and `y` coordinates. If we only make this change we will notice that our barchart is not vertical but it is flipped (meaning that all bars seem to be upside-down). This is because the coordinate system in the svg starts from the top left and not the bottom left as we are used to.
Therefore in order to bring everything "down", we need to tweak the `y` coordinate. In order to do that we can subtract from the height of the svg the data value of each bar. In this way all bars will end at 400 and start `d` pixels before the end.
``` js
// Create svg with same data
const svg = d3.select("body")
.append("svg")
.attr("width", "500")
.attr("height", "400");
const bardata = [300, 100, 150, 225, 75, 275];
const bars = svg.selectAll("rect")
.data(bardata);
bars.enter()
.append("rect")
.attr("x", (d, i) => i * 50 + 50)
.attr("y", d => 400 - d)
.attr("width", "35")
.attr("height", d => d)
.attr("fill", "lightgreen");
```
In addition to that, we need to reflect those changes in the update function and in addition to updating all bar's heights we need to also update all bars` new `y` coordinates.
``` js
bars.enter()
.append("rect") // add new elements
.attr("x", (d, i) => i * 50 + 50)
.attr("y", d => 400 - d)
.attr("width", "35")
.attr("height", d => d)
.attr("fill", "yellow")
.merge(bars) // merge
.transition()
.duration(2000)
.attr("y", d => 400 - d)
.attr("height", d => d)
.attr("fill", "orange");
```
The complete solution is [here](solutions/general_update_pattern_vertical.html){target="_blank"}
*Contributed by Kassie Papasotiriou*
### Transitions: bar chart with transitions {-}
[code for download](https://raw.githubusercontent.com/jtr13/d3book/main/solutions/bar_transition_solution.html){target="_blank"}
[rendered version](solutions/bar_transition_solution.html){target="_blank"}