├── .Rprofile ├── .gitignore ├── README.md ├── _book ├── case_studies.html ├── case_studies_files │ └── figure-html │ │ └── unnamed-chunk-20-1.png ├── fancy_stuff.html ├── fancy_stuff_files │ └── figure-html │ │ ├── unnamed-chunk-22-1.png │ │ └── unnamed-chunk-27-1.png ├── formatting.html ├── getting_started.html ├── getting_started_files │ └── figure-html │ │ └── unnamed-chunk-12-1.png ├── img │ ├── be-the-leaf-dance.gif │ ├── screenshot_table.png │ └── stupid_table_screenshot.png ├── index.html ├── penguins_screenshot.png ├── quarto_gt.html ├── search.json ├── site_libs │ ├── bootstrap │ │ ├── bootstrap-icons.css │ │ ├── bootstrap-icons.woff │ │ ├── bootstrap.min.css │ │ └── bootstrap.min.js │ ├── clipboard │ │ └── clipboard.min.js │ ├── cookie-consent │ │ ├── cookie-consent.css │ │ └── cookie-consent.js │ ├── quarto-html │ │ ├── anchor.min.js │ │ ├── popper.min.js │ │ ├── quarto-syntax-highlighting.css │ │ ├── quarto.js │ │ ├── tippy.css │ │ └── tippy.umd.min.js │ ├── quarto-nav │ │ ├── headroom.min.js │ │ └── quarto-nav.js │ └── quarto-search │ │ ├── autocomplete.umd.js │ │ ├── fuse.min.js │ │ └── quarto-search.js └── styling.html ├── _publish.yml ├── _quarto.yml ├── brands.png ├── brands_background.png ├── case_studies.qmd ├── cover.png ├── data └── ratios.csv ├── fancy_stuff.qmd ├── formatting.qmd ├── formula.svg ├── formula.txt ├── formula2.svg ├── getting_started.qmd ├── gt_book.Rproj ├── img ├── 1960_1.png ├── 1960_2.png ├── 1960_3.png ├── 1960_4.png ├── 1960_5.png ├── 1970_1.png ├── 1970_2.png ├── 1970_3.png ├── 1970_4.png ├── 1970_5.png ├── 1980_1.png ├── 1980_2.png ├── 1980_3.png ├── 1980_4.png ├── 1980_5.png ├── 1990_1.png ├── 1990_2.png ├── 1990_3.png ├── 1990_4.png ├── 1990_5.png ├── 2000_1.png ├── 2000_2.png ├── 2000_3.png ├── 2000_4.png ├── 2000_5.png ├── 2010_1.png ├── 2010_2.png ├── 2010_3.png ├── 2010_4.png ├── 2010_5.png ├── be-the-leaf-dance.gif ├── screenshot_table.png └── stupid_table_screenshot.png ├── index.qmd ├── index_SO_problems.qmd ├── penguins.png ├── penguins_screenshot.png ├── penguins_tbl.png ├── quarto_blog_versions.rds ├── quarto_gt.qmd ├── references.bib ├── references.qmd ├── renv.lock ├── renv ├── .gitignore ├── activate.R └── settings.dcf ├── second_formula.svg ├── session-info.txt ├── sparkline.png ├── staticwebapp.config.json ├── styling.qmd └── theme.scss /.Rprofile: -------------------------------------------------------------------------------- 1 | source("renv/activate.R") 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .Rproj.user 2 | .Rhistory 3 | .RData 4 | .Ruserdata 5 | 6 | /.quarto/ 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # gt_book 2 | -------------------------------------------------------------------------------- /_book/case_studies_files/figure-html/unnamed-chunk-20-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlbertRapp/gt_book/7bec14ad39260f4f6f54d4da582308fbb0f69b50/_book/case_studies_files/figure-html/unnamed-chunk-20-1.png -------------------------------------------------------------------------------- /_book/fancy_stuff_files/figure-html/unnamed-chunk-22-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlbertRapp/gt_book/7bec14ad39260f4f6f54d4da582308fbb0f69b50/_book/fancy_stuff_files/figure-html/unnamed-chunk-22-1.png -------------------------------------------------------------------------------- /_book/fancy_stuff_files/figure-html/unnamed-chunk-27-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlbertRapp/gt_book/7bec14ad39260f4f6f54d4da582308fbb0f69b50/_book/fancy_stuff_files/figure-html/unnamed-chunk-27-1.png -------------------------------------------------------------------------------- /_book/getting_started_files/figure-html/unnamed-chunk-12-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlbertRapp/gt_book/7bec14ad39260f4f6f54d4da582308fbb0f69b50/_book/getting_started_files/figure-html/unnamed-chunk-12-1.png -------------------------------------------------------------------------------- /_book/img/be-the-leaf-dance.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlbertRapp/gt_book/7bec14ad39260f4f6f54d4da582308fbb0f69b50/_book/img/be-the-leaf-dance.gif -------------------------------------------------------------------------------- /_book/img/screenshot_table.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlbertRapp/gt_book/7bec14ad39260f4f6f54d4da582308fbb0f69b50/_book/img/screenshot_table.png -------------------------------------------------------------------------------- /_book/img/stupid_table_screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlbertRapp/gt_book/7bec14ad39260f4f6f54d4da582308fbb0f69b50/_book/img/stupid_table_screenshot.png -------------------------------------------------------------------------------- /_book/penguins_screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlbertRapp/gt_book/7bec14ad39260f4f6f54d4da582308fbb0f69b50/_book/penguins_screenshot.png -------------------------------------------------------------------------------- /_book/site_libs/bootstrap/bootstrap-icons.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlbertRapp/gt_book/7bec14ad39260f4f6f54d4da582308fbb0f69b50/_book/site_libs/bootstrap/bootstrap-icons.woff -------------------------------------------------------------------------------- /_book/site_libs/clipboard/clipboard.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * clipboard.js v2.0.10 3 | * https://clipboardjs.com/ 4 | * 5 | * Licensed MIT © Zeno Rocha 6 | */ 7 | !function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.ClipboardJS=e():t.ClipboardJS=e()}(this,function(){return n={686:function(t,e,n){"use strict";n.d(e,{default:function(){return o}});var e=n(279),i=n.n(e),e=n(370),u=n.n(e),e=n(817),c=n.n(e);function a(t){try{return document.execCommand(t)}catch(t){return}}var f=function(t){t=c()(t);return a("cut"),t};var l=function(t){var e,n,o,r=1.anchorjs-link,.anchorjs-link:focus{opacity:1}",u.sheet.cssRules.length),u.sheet.insertRule("[data-anchorjs-icon]::after{content:attr(data-anchorjs-icon)}",u.sheet.cssRules.length),u.sheet.insertRule('@font-face{font-family:anchorjs-icons;src:url(data:n/a;base64,AAEAAAALAIAAAwAwT1MvMg8yG2cAAAE4AAAAYGNtYXDp3gC3AAABpAAAAExnYXNwAAAAEAAAA9wAAAAIZ2x5ZlQCcfwAAAH4AAABCGhlYWQHFvHyAAAAvAAAADZoaGVhBnACFwAAAPQAAAAkaG10eASAADEAAAGYAAAADGxvY2EACACEAAAB8AAAAAhtYXhwAAYAVwAAARgAAAAgbmFtZQGOH9cAAAMAAAAAunBvc3QAAwAAAAADvAAAACAAAQAAAAEAAHzE2p9fDzz1AAkEAAAAAADRecUWAAAAANQA6R8AAAAAAoACwAAAAAgAAgAAAAAAAAABAAADwP/AAAACgAAA/9MCrQABAAAAAAAAAAAAAAAAAAAAAwABAAAAAwBVAAIAAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAMCQAGQAAUAAAKZAswAAACPApkCzAAAAesAMwEJAAAAAAAAAAAAAAAAAAAAARAAAAAAAAAAAAAAAAAAAAAAQAAg//0DwP/AAEADwABAAAAAAQAAAAAAAAAAAAAAIAAAAAAAAAIAAAACgAAxAAAAAwAAAAMAAAAcAAEAAwAAABwAAwABAAAAHAAEADAAAAAIAAgAAgAAACDpy//9//8AAAAg6cv//f///+EWNwADAAEAAAAAAAAAAAAAAAAACACEAAEAAAAAAAAAAAAAAAAxAAACAAQARAKAAsAAKwBUAAABIiYnJjQ3NzY2MzIWFxYUBwcGIicmNDc3NjQnJiYjIgYHBwYUFxYUBwYGIwciJicmNDc3NjIXFhQHBwYUFxYWMzI2Nzc2NCcmNDc2MhcWFAcHBgYjARQGDAUtLXoWOR8fORYtLTgKGwoKCjgaGg0gEhIgDXoaGgkJBQwHdR85Fi0tOAobCgoKOBoaDSASEiANehoaCQkKGwotLXoWOR8BMwUFLYEuehYXFxYugC44CQkKGwo4GkoaDQ0NDXoaShoKGwoFBe8XFi6ALjgJCQobCjgaShoNDQ0NehpKGgobCgoKLYEuehYXAAAADACWAAEAAAAAAAEACAAAAAEAAAAAAAIAAwAIAAEAAAAAAAMACAAAAAEAAAAAAAQACAAAAAEAAAAAAAUAAQALAAEAAAAAAAYACAAAAAMAAQQJAAEAEAAMAAMAAQQJAAIABgAcAAMAAQQJAAMAEAAMAAMAAQQJAAQAEAAMAAMAAQQJAAUAAgAiAAMAAQQJAAYAEAAMYW5jaG9yanM0MDBAAGEAbgBjAGgAbwByAGoAcwA0ADAAMABAAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAH//wAP) format("truetype")}',u.sheet.cssRules.length)),u=document.querySelectorAll("[id]"),t=[].map.call(u,function(A){return A.id}),i=0;i\]./()*\\\n\t\b\v\u00A0]/g,"-").replace(/-{2,}/g,"-").substring(0,this.options.truncate).replace(/^-+|-+$/gm,"").toLowerCase()},this.hasAnchorJSLink=function(A){var e=A.firstChild&&-1<(" "+A.firstChild.className+" ").indexOf(" anchorjs-link "),A=A.lastChild&&-1<(" "+A.lastChild.className+" ").indexOf(" anchorjs-link ");return e||A||!1}}}); 9 | // @license-end -------------------------------------------------------------------------------- /_book/site_libs/quarto-html/popper.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @popperjs/core v2.11.4 - MIT License 3 | */ 4 | 5 | !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).Popper={})}(this,(function(e){"use strict";function t(e){if(null==e)return window;if("[object Window]"!==e.toString()){var t=e.ownerDocument;return t&&t.defaultView||window}return e}function n(e){return e instanceof t(e).Element||e instanceof Element}function r(e){return e instanceof t(e).HTMLElement||e instanceof HTMLElement}function o(e){return"undefined"!=typeof ShadowRoot&&(e instanceof t(e).ShadowRoot||e instanceof ShadowRoot)}var i=Math.max,a=Math.min,s=Math.round;function f(e,t){void 0===t&&(t=!1);var n=e.getBoundingClientRect(),o=1,i=1;if(r(e)&&t){var a=e.offsetHeight,f=e.offsetWidth;f>0&&(o=s(n.width)/f||1),a>0&&(i=s(n.height)/a||1)}return{width:n.width/o,height:n.height/i,top:n.top/i,right:n.right/o,bottom:n.bottom/i,left:n.left/o,x:n.left/o,y:n.top/i}}function c(e){var n=t(e);return{scrollLeft:n.pageXOffset,scrollTop:n.pageYOffset}}function p(e){return e?(e.nodeName||"").toLowerCase():null}function u(e){return((n(e)?e.ownerDocument:e.document)||window.document).documentElement}function l(e){return f(u(e)).left+c(e).scrollLeft}function d(e){return t(e).getComputedStyle(e)}function h(e){var t=d(e),n=t.overflow,r=t.overflowX,o=t.overflowY;return/auto|scroll|overlay|hidden/.test(n+o+r)}function m(e,n,o){void 0===o&&(o=!1);var i,a,d=r(n),m=r(n)&&function(e){var t=e.getBoundingClientRect(),n=s(t.width)/e.offsetWidth||1,r=s(t.height)/e.offsetHeight||1;return 1!==n||1!==r}(n),v=u(n),g=f(e,m),y={scrollLeft:0,scrollTop:0},b={x:0,y:0};return(d||!d&&!o)&&(("body"!==p(n)||h(v))&&(y=(i=n)!==t(i)&&r(i)?{scrollLeft:(a=i).scrollLeft,scrollTop:a.scrollTop}:c(i)),r(n)?((b=f(n,!0)).x+=n.clientLeft,b.y+=n.clientTop):v&&(b.x=l(v))),{x:g.left+y.scrollLeft-b.x,y:g.top+y.scrollTop-b.y,width:g.width,height:g.height}}function v(e){var t=f(e),n=e.offsetWidth,r=e.offsetHeight;return Math.abs(t.width-n)<=1&&(n=t.width),Math.abs(t.height-r)<=1&&(r=t.height),{x:e.offsetLeft,y:e.offsetTop,width:n,height:r}}function g(e){return"html"===p(e)?e:e.assignedSlot||e.parentNode||(o(e)?e.host:null)||u(e)}function y(e){return["html","body","#document"].indexOf(p(e))>=0?e.ownerDocument.body:r(e)&&h(e)?e:y(g(e))}function b(e,n){var r;void 0===n&&(n=[]);var o=y(e),i=o===(null==(r=e.ownerDocument)?void 0:r.body),a=t(o),s=i?[a].concat(a.visualViewport||[],h(o)?o:[]):o,f=n.concat(s);return i?f:f.concat(b(g(s)))}function x(e){return["table","td","th"].indexOf(p(e))>=0}function w(e){return r(e)&&"fixed"!==d(e).position?e.offsetParent:null}function O(e){for(var n=t(e),i=w(e);i&&x(i)&&"static"===d(i).position;)i=w(i);return i&&("html"===p(i)||"body"===p(i)&&"static"===d(i).position)?n:i||function(e){var t=-1!==navigator.userAgent.toLowerCase().indexOf("firefox");if(-1!==navigator.userAgent.indexOf("Trident")&&r(e)&&"fixed"===d(e).position)return null;var n=g(e);for(o(n)&&(n=n.host);r(n)&&["html","body"].indexOf(p(n))<0;){var i=d(n);if("none"!==i.transform||"none"!==i.perspective||"paint"===i.contain||-1!==["transform","perspective"].indexOf(i.willChange)||t&&"filter"===i.willChange||t&&i.filter&&"none"!==i.filter)return n;n=n.parentNode}return null}(e)||n}var j="top",E="bottom",D="right",A="left",L="auto",P=[j,E,D,A],M="start",k="end",W="viewport",B="popper",H=P.reduce((function(e,t){return e.concat([t+"-"+M,t+"-"+k])}),[]),T=[].concat(P,[L]).reduce((function(e,t){return e.concat([t,t+"-"+M,t+"-"+k])}),[]),R=["beforeRead","read","afterRead","beforeMain","main","afterMain","beforeWrite","write","afterWrite"];function S(e){var t=new Map,n=new Set,r=[];function o(e){n.add(e.name),[].concat(e.requires||[],e.requiresIfExists||[]).forEach((function(e){if(!n.has(e)){var r=t.get(e);r&&o(r)}})),r.push(e)}return e.forEach((function(e){t.set(e.name,e)})),e.forEach((function(e){n.has(e.name)||o(e)})),r}function C(e){return e.split("-")[0]}function q(e,t){var n=t.getRootNode&&t.getRootNode();if(e.contains(t))return!0;if(n&&o(n)){var r=t;do{if(r&&e.isSameNode(r))return!0;r=r.parentNode||r.host}while(r)}return!1}function V(e){return Object.assign({},e,{left:e.x,top:e.y,right:e.x+e.width,bottom:e.y+e.height})}function N(e,r){return r===W?V(function(e){var n=t(e),r=u(e),o=n.visualViewport,i=r.clientWidth,a=r.clientHeight,s=0,f=0;return o&&(i=o.width,a=o.height,/^((?!chrome|android).)*safari/i.test(navigator.userAgent)||(s=o.offsetLeft,f=o.offsetTop)),{width:i,height:a,x:s+l(e),y:f}}(e)):n(r)?function(e){var t=f(e);return t.top=t.top+e.clientTop,t.left=t.left+e.clientLeft,t.bottom=t.top+e.clientHeight,t.right=t.left+e.clientWidth,t.width=e.clientWidth,t.height=e.clientHeight,t.x=t.left,t.y=t.top,t}(r):V(function(e){var t,n=u(e),r=c(e),o=null==(t=e.ownerDocument)?void 0:t.body,a=i(n.scrollWidth,n.clientWidth,o?o.scrollWidth:0,o?o.clientWidth:0),s=i(n.scrollHeight,n.clientHeight,o?o.scrollHeight:0,o?o.clientHeight:0),f=-r.scrollLeft+l(e),p=-r.scrollTop;return"rtl"===d(o||n).direction&&(f+=i(n.clientWidth,o?o.clientWidth:0)-a),{width:a,height:s,x:f,y:p}}(u(e)))}function I(e,t,o){var s="clippingParents"===t?function(e){var t=b(g(e)),o=["absolute","fixed"].indexOf(d(e).position)>=0&&r(e)?O(e):e;return n(o)?t.filter((function(e){return n(e)&&q(e,o)&&"body"!==p(e)})):[]}(e):[].concat(t),f=[].concat(s,[o]),c=f[0],u=f.reduce((function(t,n){var r=N(e,n);return t.top=i(r.top,t.top),t.right=a(r.right,t.right),t.bottom=a(r.bottom,t.bottom),t.left=i(r.left,t.left),t}),N(e,c));return u.width=u.right-u.left,u.height=u.bottom-u.top,u.x=u.left,u.y=u.top,u}function _(e){return e.split("-")[1]}function F(e){return["top","bottom"].indexOf(e)>=0?"x":"y"}function U(e){var t,n=e.reference,r=e.element,o=e.placement,i=o?C(o):null,a=o?_(o):null,s=n.x+n.width/2-r.width/2,f=n.y+n.height/2-r.height/2;switch(i){case j:t={x:s,y:n.y-r.height};break;case E:t={x:s,y:n.y+n.height};break;case D:t={x:n.x+n.width,y:f};break;case A:t={x:n.x-r.width,y:f};break;default:t={x:n.x,y:n.y}}var c=i?F(i):null;if(null!=c){var p="y"===c?"height":"width";switch(a){case M:t[c]=t[c]-(n[p]/2-r[p]/2);break;case k:t[c]=t[c]+(n[p]/2-r[p]/2)}}return t}function z(e){return Object.assign({},{top:0,right:0,bottom:0,left:0},e)}function X(e,t){return t.reduce((function(t,n){return t[n]=e,t}),{})}function Y(e,t){void 0===t&&(t={});var r=t,o=r.placement,i=void 0===o?e.placement:o,a=r.boundary,s=void 0===a?"clippingParents":a,c=r.rootBoundary,p=void 0===c?W:c,l=r.elementContext,d=void 0===l?B:l,h=r.altBoundary,m=void 0!==h&&h,v=r.padding,g=void 0===v?0:v,y=z("number"!=typeof g?g:X(g,P)),b=d===B?"reference":B,x=e.rects.popper,w=e.elements[m?b:d],O=I(n(w)?w:w.contextElement||u(e.elements.popper),s,p),A=f(e.elements.reference),L=U({reference:A,element:x,strategy:"absolute",placement:i}),M=V(Object.assign({},x,L)),k=d===B?M:A,H={top:O.top-k.top+y.top,bottom:k.bottom-O.bottom+y.bottom,left:O.left-k.left+y.left,right:k.right-O.right+y.right},T=e.modifiersData.offset;if(d===B&&T){var R=T[i];Object.keys(H).forEach((function(e){var t=[D,E].indexOf(e)>=0?1:-1,n=[j,E].indexOf(e)>=0?"y":"x";H[e]+=R[n]*t}))}return H}var G={placement:"bottom",modifiers:[],strategy:"absolute"};function J(){for(var e=arguments.length,t=new Array(e),n=0;n=0?-1:1,i="function"==typeof n?n(Object.assign({},t,{placement:e})):n,a=i[0],s=i[1];return a=a||0,s=(s||0)*o,[A,D].indexOf(r)>=0?{x:s,y:a}:{x:a,y:s}}(n,t.rects,i),e}),{}),s=a[t.placement],f=s.x,c=s.y;null!=t.modifiersData.popperOffsets&&(t.modifiersData.popperOffsets.x+=f,t.modifiersData.popperOffsets.y+=c),t.modifiersData[r]=a}},ie={left:"right",right:"left",bottom:"top",top:"bottom"};function ae(e){return e.replace(/left|right|bottom|top/g,(function(e){return ie[e]}))}var se={start:"end",end:"start"};function fe(e){return e.replace(/start|end/g,(function(e){return se[e]}))}function ce(e,t){void 0===t&&(t={});var n=t,r=n.placement,o=n.boundary,i=n.rootBoundary,a=n.padding,s=n.flipVariations,f=n.allowedAutoPlacements,c=void 0===f?T:f,p=_(r),u=p?s?H:H.filter((function(e){return _(e)===p})):P,l=u.filter((function(e){return c.indexOf(e)>=0}));0===l.length&&(l=u);var d=l.reduce((function(t,n){return t[n]=Y(e,{placement:n,boundary:o,rootBoundary:i,padding:a})[C(n)],t}),{});return Object.keys(d).sort((function(e,t){return d[e]-d[t]}))}var pe={name:"flip",enabled:!0,phase:"main",fn:function(e){var t=e.state,n=e.options,r=e.name;if(!t.modifiersData[r]._skip){for(var o=n.mainAxis,i=void 0===o||o,a=n.altAxis,s=void 0===a||a,f=n.fallbackPlacements,c=n.padding,p=n.boundary,u=n.rootBoundary,l=n.altBoundary,d=n.flipVariations,h=void 0===d||d,m=n.allowedAutoPlacements,v=t.options.placement,g=C(v),y=f||(g===v||!h?[ae(v)]:function(e){if(C(e)===L)return[];var t=ae(e);return[fe(e),t,fe(t)]}(v)),b=[v].concat(y).reduce((function(e,n){return e.concat(C(n)===L?ce(t,{placement:n,boundary:p,rootBoundary:u,padding:c,flipVariations:h,allowedAutoPlacements:m}):n)}),[]),x=t.rects.reference,w=t.rects.popper,O=new Map,P=!0,k=b[0],W=0;W=0,S=R?"width":"height",q=Y(t,{placement:B,boundary:p,rootBoundary:u,altBoundary:l,padding:c}),V=R?T?D:A:T?E:j;x[S]>w[S]&&(V=ae(V));var N=ae(V),I=[];if(i&&I.push(q[H]<=0),s&&I.push(q[V]<=0,q[N]<=0),I.every((function(e){return e}))){k=B,P=!1;break}O.set(B,I)}if(P)for(var F=function(e){var t=b.find((function(t){var n=O.get(t);if(n)return n.slice(0,e).every((function(e){return e}))}));if(t)return k=t,"break"},U=h?3:1;U>0;U--){if("break"===F(U))break}t.placement!==k&&(t.modifiersData[r]._skip=!0,t.placement=k,t.reset=!0)}},requiresIfExists:["offset"],data:{_skip:!1}};function ue(e,t,n){return i(e,a(t,n))}var le={name:"preventOverflow",enabled:!0,phase:"main",fn:function(e){var t=e.state,n=e.options,r=e.name,o=n.mainAxis,s=void 0===o||o,f=n.altAxis,c=void 0!==f&&f,p=n.boundary,u=n.rootBoundary,l=n.altBoundary,d=n.padding,h=n.tether,m=void 0===h||h,g=n.tetherOffset,y=void 0===g?0:g,b=Y(t,{boundary:p,rootBoundary:u,padding:d,altBoundary:l}),x=C(t.placement),w=_(t.placement),L=!w,P=F(x),k="x"===P?"y":"x",W=t.modifiersData.popperOffsets,B=t.rects.reference,H=t.rects.popper,T="function"==typeof y?y(Object.assign({},t.rects,{placement:t.placement})):y,R="number"==typeof T?{mainAxis:T,altAxis:T}:Object.assign({mainAxis:0,altAxis:0},T),S=t.modifiersData.offset?t.modifiersData.offset[t.placement]:null,q={x:0,y:0};if(W){if(s){var V,N="y"===P?j:A,I="y"===P?E:D,U="y"===P?"height":"width",z=W[P],X=z+b[N],G=z-b[I],J=m?-H[U]/2:0,K=w===M?B[U]:H[U],Q=w===M?-H[U]:-B[U],Z=t.elements.arrow,$=m&&Z?v(Z):{width:0,height:0},ee=t.modifiersData["arrow#persistent"]?t.modifiersData["arrow#persistent"].padding:{top:0,right:0,bottom:0,left:0},te=ee[N],ne=ee[I],re=ue(0,B[U],$[U]),oe=L?B[U]/2-J-re-te-R.mainAxis:K-re-te-R.mainAxis,ie=L?-B[U]/2+J+re+ne+R.mainAxis:Q+re+ne+R.mainAxis,ae=t.elements.arrow&&O(t.elements.arrow),se=ae?"y"===P?ae.clientTop||0:ae.clientLeft||0:0,fe=null!=(V=null==S?void 0:S[P])?V:0,ce=z+ie-fe,pe=ue(m?a(X,z+oe-fe-se):X,z,m?i(G,ce):G);W[P]=pe,q[P]=pe-z}if(c){var le,de="x"===P?j:A,he="x"===P?E:D,me=W[k],ve="y"===k?"height":"width",ge=me+b[de],ye=me-b[he],be=-1!==[j,A].indexOf(x),xe=null!=(le=null==S?void 0:S[k])?le:0,we=be?ge:me-B[ve]-H[ve]-xe+R.altAxis,Oe=be?me+B[ve]+H[ve]-xe-R.altAxis:ye,je=m&&be?function(e,t,n){var r=ue(e,t,n);return r>n?n:r}(we,me,Oe):ue(m?we:ge,me,m?Oe:ye);W[k]=je,q[k]=je-me}t.modifiersData[r]=q}},requiresIfExists:["offset"]};var de={name:"arrow",enabled:!0,phase:"main",fn:function(e){var t,n=e.state,r=e.name,o=e.options,i=n.elements.arrow,a=n.modifiersData.popperOffsets,s=C(n.placement),f=F(s),c=[A,D].indexOf(s)>=0?"height":"width";if(i&&a){var p=function(e,t){return z("number"!=typeof(e="function"==typeof e?e(Object.assign({},t.rects,{placement:t.placement})):e)?e:X(e,P))}(o.padding,n),u=v(i),l="y"===f?j:A,d="y"===f?E:D,h=n.rects.reference[c]+n.rects.reference[f]-a[f]-n.rects.popper[c],m=a[f]-n.rects.reference[f],g=O(i),y=g?"y"===f?g.clientHeight||0:g.clientWidth||0:0,b=h/2-m/2,x=p[l],w=y-u[c]-p[d],L=y/2-u[c]/2+b,M=ue(x,L,w),k=f;n.modifiersData[r]=((t={})[k]=M,t.centerOffset=M-L,t)}},effect:function(e){var t=e.state,n=e.options.element,r=void 0===n?"[data-popper-arrow]":n;null!=r&&("string"!=typeof r||(r=t.elements.popper.querySelector(r)))&&q(t.elements.popper,r)&&(t.elements.arrow=r)},requires:["popperOffsets"],requiresIfExists:["preventOverflow"]};function he(e,t,n){return void 0===n&&(n={x:0,y:0}),{top:e.top-t.height-n.y,right:e.right-t.width+n.x,bottom:e.bottom-t.height+n.y,left:e.left-t.width-n.x}}function me(e){return[j,D,E,A].some((function(t){return e[t]>=0}))}var ve={name:"hide",enabled:!0,phase:"main",requiresIfExists:["preventOverflow"],fn:function(e){var t=e.state,n=e.name,r=t.rects.reference,o=t.rects.popper,i=t.modifiersData.preventOverflow,a=Y(t,{elementContext:"reference"}),s=Y(t,{altBoundary:!0}),f=he(a,r),c=he(s,o,i),p=me(f),u=me(c);t.modifiersData[n]={referenceClippingOffsets:f,popperEscapeOffsets:c,isReferenceHidden:p,hasPopperEscaped:u},t.attributes.popper=Object.assign({},t.attributes.popper,{"data-popper-reference-hidden":p,"data-popper-escaped":u})}},ge=K({defaultModifiers:[Z,$,ne,re]}),ye=[Z,$,ne,re,oe,pe,le,de,ve],be=K({defaultModifiers:ye});e.applyStyles=re,e.arrow=de,e.computeStyles=ne,e.createPopper=be,e.createPopperLite=ge,e.defaultModifiers=ye,e.detectOverflow=Y,e.eventListeners=Z,e.flip=pe,e.hide=ve,e.offset=oe,e.popperGenerator=K,e.popperOffsets=$,e.preventOverflow=le,Object.defineProperty(e,"__esModule",{value:!0})})); 6 | 7 | -------------------------------------------------------------------------------- /_book/site_libs/quarto-html/quarto-syntax-highlighting.css: -------------------------------------------------------------------------------- 1 | /* quarto syntax highlight colors */ 2 | :root { 3 | --quarto-hl-ot-color: #003B4F; 4 | --quarto-hl-at-color: #657422; 5 | --quarto-hl-ss-color: #20794D; 6 | --quarto-hl-an-color: #5E5E5E; 7 | --quarto-hl-fu-color: #4758AB; 8 | --quarto-hl-st-color: #20794D; 9 | --quarto-hl-cf-color: #003B4F; 10 | --quarto-hl-op-color: #5E5E5E; 11 | --quarto-hl-er-color: #AD0000; 12 | --quarto-hl-bn-color: #AD0000; 13 | --quarto-hl-al-color: #AD0000; 14 | --quarto-hl-va-color: #111111; 15 | --quarto-hl-bu-color: inherit; 16 | --quarto-hl-ex-color: inherit; 17 | --quarto-hl-pp-color: #AD0000; 18 | --quarto-hl-in-color: #5E5E5E; 19 | --quarto-hl-vs-color: #20794D; 20 | --quarto-hl-wa-color: #5E5E5E; 21 | --quarto-hl-do-color: #5E5E5E; 22 | --quarto-hl-im-color: #00769E; 23 | --quarto-hl-ch-color: #20794D; 24 | --quarto-hl-dt-color: #AD0000; 25 | --quarto-hl-fl-color: #AD0000; 26 | --quarto-hl-co-color: #5E5E5E; 27 | --quarto-hl-cv-color: #5E5E5E; 28 | --quarto-hl-cn-color: #8f5902; 29 | --quarto-hl-sc-color: #5E5E5E; 30 | --quarto-hl-dv-color: #AD0000; 31 | --quarto-hl-kw-color: #003B4F; 32 | } 33 | 34 | /* other quarto variables */ 35 | :root { 36 | --quarto-font-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; 37 | } 38 | 39 | pre > code.sourceCode > span { 40 | color: #003B4F; 41 | } 42 | 43 | code span { 44 | color: #003B4F; 45 | } 46 | 47 | code.sourceCode > span { 48 | color: #003B4F; 49 | } 50 | 51 | div.sourceCode, 52 | div.sourceCode pre.sourceCode { 53 | color: #003B4F; 54 | } 55 | 56 | code span.ot { 57 | color: #003B4F; 58 | } 59 | 60 | code span.at { 61 | color: #657422; 62 | } 63 | 64 | code span.ss { 65 | color: #20794D; 66 | } 67 | 68 | code span.an { 69 | color: #5E5E5E; 70 | } 71 | 72 | code span.fu { 73 | color: #4758AB; 74 | } 75 | 76 | code span.st { 77 | color: #20794D; 78 | } 79 | 80 | code span.cf { 81 | color: #003B4F; 82 | } 83 | 84 | code span.op { 85 | color: #5E5E5E; 86 | } 87 | 88 | code span.er { 89 | color: #AD0000; 90 | } 91 | 92 | code span.bn { 93 | color: #AD0000; 94 | } 95 | 96 | code span.al { 97 | color: #AD0000; 98 | } 99 | 100 | code span.va { 101 | color: #111111; 102 | } 103 | 104 | code span.pp { 105 | color: #AD0000; 106 | } 107 | 108 | code span.in { 109 | color: #5E5E5E; 110 | } 111 | 112 | code span.vs { 113 | color: #20794D; 114 | } 115 | 116 | code span.wa { 117 | color: #5E5E5E; 118 | font-style: italic; 119 | } 120 | 121 | code span.do { 122 | color: #5E5E5E; 123 | font-style: italic; 124 | } 125 | 126 | code span.im { 127 | color: #00769E; 128 | } 129 | 130 | code span.ch { 131 | color: #20794D; 132 | } 133 | 134 | code span.dt { 135 | color: #AD0000; 136 | } 137 | 138 | code span.fl { 139 | color: #AD0000; 140 | } 141 | 142 | code span.co { 143 | color: #5E5E5E; 144 | } 145 | 146 | code span.cv { 147 | color: #5E5E5E; 148 | font-style: italic; 149 | } 150 | 151 | code span.cn { 152 | color: #8f5902; 153 | } 154 | 155 | code span.sc { 156 | color: #5E5E5E; 157 | } 158 | 159 | code span.dv { 160 | color: #AD0000; 161 | } 162 | 163 | code span.kw { 164 | color: #003B4F; 165 | } 166 | 167 | .prevent-inlining { 168 | content: ".tippy-arrow{bottom:0}.tippy-box[data-placement^=top]>.tippy-arrow:before{bottom:-7px;left:0;border-width:8px 8px 0;border-top-color:initial;transform-origin:center top}.tippy-box[data-placement^=bottom]>.tippy-arrow{top:0}.tippy-box[data-placement^=bottom]>.tippy-arrow:before{top:-7px;left:0;border-width:0 8px 8px;border-bottom-color:initial;transform-origin:center bottom}.tippy-box[data-placement^=left]>.tippy-arrow{right:0}.tippy-box[data-placement^=left]>.tippy-arrow:before{border-width:8px 0 8px 8px;border-left-color:initial;right:-7px;transform-origin:center left}.tippy-box[data-placement^=right]>.tippy-arrow{left:0}.tippy-box[data-placement^=right]>.tippy-arrow:before{left:-7px;border-width:8px 8px 8px 0;border-right-color:initial;transform-origin:center right}.tippy-box[data-inertia][data-state=visible]{transition-timing-function:cubic-bezier(.54,1.5,.38,1.11)}.tippy-arrow{width:16px;height:16px;color:#333}.tippy-arrow:before{content:"";position:absolute;border-color:transparent;border-style:solid}.tippy-content{position:relative;padding:5px 9px;z-index:1} -------------------------------------------------------------------------------- /_book/site_libs/quarto-html/tippy.umd.min.js: -------------------------------------------------------------------------------- 1 | !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t(require("@popperjs/core")):"function"==typeof define&&define.amd?define(["@popperjs/core"],t):(e=e||self).tippy=t(e.Popper)}(this,(function(e){"use strict";var t={passive:!0,capture:!0},n=function(){return document.body};function r(e,t,n){if(Array.isArray(e)){var r=e[t];return null==r?Array.isArray(n)?n[t]:n:r}return e}function o(e,t){var n={}.toString.call(e);return 0===n.indexOf("[object")&&n.indexOf(t+"]")>-1}function i(e,t){return"function"==typeof e?e.apply(void 0,t):e}function a(e,t){return 0===t?e:function(r){clearTimeout(n),n=setTimeout((function(){e(r)}),t)};var n}function s(e,t){var n=Object.assign({},e);return t.forEach((function(e){delete n[e]})),n}function u(e){return[].concat(e)}function c(e,t){-1===e.indexOf(t)&&e.push(t)}function p(e){return e.split("-")[0]}function f(e){return[].slice.call(e)}function l(e){return Object.keys(e).reduce((function(t,n){return void 0!==e[n]&&(t[n]=e[n]),t}),{})}function d(){return document.createElement("div")}function v(e){return["Element","Fragment"].some((function(t){return o(e,t)}))}function m(e){return o(e,"MouseEvent")}function g(e){return!(!e||!e._tippy||e._tippy.reference!==e)}function h(e){return v(e)?[e]:function(e){return o(e,"NodeList")}(e)?f(e):Array.isArray(e)?e:f(document.querySelectorAll(e))}function b(e,t){e.forEach((function(e){e&&(e.style.transitionDuration=t+"ms")}))}function y(e,t){e.forEach((function(e){e&&e.setAttribute("data-state",t)}))}function w(e){var t,n=u(e)[0];return null!=n&&null!=(t=n.ownerDocument)&&t.body?n.ownerDocument:document}function E(e,t,n){var r=t+"EventListener";["transitionend","webkitTransitionEnd"].forEach((function(t){e[r](t,n)}))}function O(e,t){for(var n=t;n;){var r;if(e.contains(n))return!0;n=null==n.getRootNode||null==(r=n.getRootNode())?void 0:r.host}return!1}var x={isTouch:!1},C=0;function T(){x.isTouch||(x.isTouch=!0,window.performance&&document.addEventListener("mousemove",A))}function A(){var e=performance.now();e-C<20&&(x.isTouch=!1,document.removeEventListener("mousemove",A)),C=e}function L(){var e=document.activeElement;if(g(e)){var t=e._tippy;e.blur&&!t.state.isVisible&&e.blur()}}var D=!!("undefined"!=typeof window&&"undefined"!=typeof document)&&!!window.msCrypto,R=Object.assign({appendTo:n,aria:{content:"auto",expanded:"auto"},delay:0,duration:[300,250],getReferenceClientRect:null,hideOnClick:!0,ignoreAttributes:!1,interactive:!1,interactiveBorder:2,interactiveDebounce:0,moveTransition:"",offset:[0,10],onAfterUpdate:function(){},onBeforeUpdate:function(){},onCreate:function(){},onDestroy:function(){},onHidden:function(){},onHide:function(){},onMount:function(){},onShow:function(){},onShown:function(){},onTrigger:function(){},onUntrigger:function(){},onClickOutside:function(){},placement:"top",plugins:[],popperOptions:{},render:null,showOnCreate:!1,touch:!0,trigger:"mouseenter focus",triggerTarget:null},{animateFill:!1,followCursor:!1,inlinePositioning:!1,sticky:!1},{allowHTML:!1,animation:"fade",arrow:!0,content:"",inertia:!1,maxWidth:350,role:"tooltip",theme:"",zIndex:9999}),k=Object.keys(R);function P(e){var t=(e.plugins||[]).reduce((function(t,n){var r,o=n.name,i=n.defaultValue;o&&(t[o]=void 0!==e[o]?e[o]:null!=(r=R[o])?r:i);return t}),{});return Object.assign({},e,t)}function j(e,t){var n=Object.assign({},t,{content:i(t.content,[e])},t.ignoreAttributes?{}:function(e,t){return(t?Object.keys(P(Object.assign({},R,{plugins:t}))):k).reduce((function(t,n){var r=(e.getAttribute("data-tippy-"+n)||"").trim();if(!r)return t;if("content"===n)t[n]=r;else try{t[n]=JSON.parse(r)}catch(e){t[n]=r}return t}),{})}(e,t.plugins));return n.aria=Object.assign({},R.aria,n.aria),n.aria={expanded:"auto"===n.aria.expanded?t.interactive:n.aria.expanded,content:"auto"===n.aria.content?t.interactive?null:"describedby":n.aria.content},n}function M(e,t){e.innerHTML=t}function V(e){var t=d();return!0===e?t.className="tippy-arrow":(t.className="tippy-svg-arrow",v(e)?t.appendChild(e):M(t,e)),t}function I(e,t){v(t.content)?(M(e,""),e.appendChild(t.content)):"function"!=typeof t.content&&(t.allowHTML?M(e,t.content):e.textContent=t.content)}function S(e){var t=e.firstElementChild,n=f(t.children);return{box:t,content:n.find((function(e){return e.classList.contains("tippy-content")})),arrow:n.find((function(e){return e.classList.contains("tippy-arrow")||e.classList.contains("tippy-svg-arrow")})),backdrop:n.find((function(e){return e.classList.contains("tippy-backdrop")}))}}function N(e){var t=d(),n=d();n.className="tippy-box",n.setAttribute("data-state","hidden"),n.setAttribute("tabindex","-1");var r=d();function o(n,r){var o=S(t),i=o.box,a=o.content,s=o.arrow;r.theme?i.setAttribute("data-theme",r.theme):i.removeAttribute("data-theme"),"string"==typeof r.animation?i.setAttribute("data-animation",r.animation):i.removeAttribute("data-animation"),r.inertia?i.setAttribute("data-inertia",""):i.removeAttribute("data-inertia"),i.style.maxWidth="number"==typeof r.maxWidth?r.maxWidth+"px":r.maxWidth,r.role?i.setAttribute("role",r.role):i.removeAttribute("role"),n.content===r.content&&n.allowHTML===r.allowHTML||I(a,e.props),r.arrow?s?n.arrow!==r.arrow&&(i.removeChild(s),i.appendChild(V(r.arrow))):i.appendChild(V(r.arrow)):s&&i.removeChild(s)}return r.className="tippy-content",r.setAttribute("data-state","hidden"),I(r,e.props),t.appendChild(n),n.appendChild(r),o(e.props,e.props),{popper:t,onUpdate:o}}N.$$tippy=!0;var B=1,H=[],U=[];function _(o,s){var v,g,h,C,T,A,L,k,M=j(o,Object.assign({},R,P(l(s)))),V=!1,I=!1,N=!1,_=!1,F=[],W=a(we,M.interactiveDebounce),X=B++,Y=(k=M.plugins).filter((function(e,t){return k.indexOf(e)===t})),$={id:X,reference:o,popper:d(),popperInstance:null,props:M,state:{isEnabled:!0,isVisible:!1,isDestroyed:!1,isMounted:!1,isShown:!1},plugins:Y,clearDelayTimeouts:function(){clearTimeout(v),clearTimeout(g),cancelAnimationFrame(h)},setProps:function(e){if($.state.isDestroyed)return;ae("onBeforeUpdate",[$,e]),be();var t=$.props,n=j(o,Object.assign({},t,l(e),{ignoreAttributes:!0}));$.props=n,he(),t.interactiveDebounce!==n.interactiveDebounce&&(ce(),W=a(we,n.interactiveDebounce));t.triggerTarget&&!n.triggerTarget?u(t.triggerTarget).forEach((function(e){e.removeAttribute("aria-expanded")})):n.triggerTarget&&o.removeAttribute("aria-expanded");ue(),ie(),J&&J(t,n);$.popperInstance&&(Ce(),Ae().forEach((function(e){requestAnimationFrame(e._tippy.popperInstance.forceUpdate)})));ae("onAfterUpdate",[$,e])},setContent:function(e){$.setProps({content:e})},show:function(){var e=$.state.isVisible,t=$.state.isDestroyed,o=!$.state.isEnabled,a=x.isTouch&&!$.props.touch,s=r($.props.duration,0,R.duration);if(e||t||o||a)return;if(te().hasAttribute("disabled"))return;if(ae("onShow",[$],!1),!1===$.props.onShow($))return;$.state.isVisible=!0,ee()&&(z.style.visibility="visible");ie(),de(),$.state.isMounted||(z.style.transition="none");if(ee()){var u=re(),p=u.box,f=u.content;b([p,f],0)}A=function(){var e;if($.state.isVisible&&!_){if(_=!0,z.offsetHeight,z.style.transition=$.props.moveTransition,ee()&&$.props.animation){var t=re(),n=t.box,r=t.content;b([n,r],s),y([n,r],"visible")}se(),ue(),c(U,$),null==(e=$.popperInstance)||e.forceUpdate(),ae("onMount",[$]),$.props.animation&&ee()&&function(e,t){me(e,t)}(s,(function(){$.state.isShown=!0,ae("onShown",[$])}))}},function(){var e,t=$.props.appendTo,r=te();e=$.props.interactive&&t===n||"parent"===t?r.parentNode:i(t,[r]);e.contains(z)||e.appendChild(z);$.state.isMounted=!0,Ce()}()},hide:function(){var e=!$.state.isVisible,t=$.state.isDestroyed,n=!$.state.isEnabled,o=r($.props.duration,1,R.duration);if(e||t||n)return;if(ae("onHide",[$],!1),!1===$.props.onHide($))return;$.state.isVisible=!1,$.state.isShown=!1,_=!1,V=!1,ee()&&(z.style.visibility="hidden");if(ce(),ve(),ie(!0),ee()){var i=re(),a=i.box,s=i.content;$.props.animation&&(b([a,s],o),y([a,s],"hidden"))}se(),ue(),$.props.animation?ee()&&function(e,t){me(e,(function(){!$.state.isVisible&&z.parentNode&&z.parentNode.contains(z)&&t()}))}(o,$.unmount):$.unmount()},hideWithInteractivity:function(e){ne().addEventListener("mousemove",W),c(H,W),W(e)},enable:function(){$.state.isEnabled=!0},disable:function(){$.hide(),$.state.isEnabled=!1},unmount:function(){$.state.isVisible&&$.hide();if(!$.state.isMounted)return;Te(),Ae().forEach((function(e){e._tippy.unmount()})),z.parentNode&&z.parentNode.removeChild(z);U=U.filter((function(e){return e!==$})),$.state.isMounted=!1,ae("onHidden",[$])},destroy:function(){if($.state.isDestroyed)return;$.clearDelayTimeouts(),$.unmount(),be(),delete o._tippy,$.state.isDestroyed=!0,ae("onDestroy",[$])}};if(!M.render)return $;var q=M.render($),z=q.popper,J=q.onUpdate;z.setAttribute("data-tippy-root",""),z.id="tippy-"+$.id,$.popper=z,o._tippy=$,z._tippy=$;var G=Y.map((function(e){return e.fn($)})),K=o.hasAttribute("aria-expanded");return he(),ue(),ie(),ae("onCreate",[$]),M.showOnCreate&&Le(),z.addEventListener("mouseenter",(function(){$.props.interactive&&$.state.isVisible&&$.clearDelayTimeouts()})),z.addEventListener("mouseleave",(function(){$.props.interactive&&$.props.trigger.indexOf("mouseenter")>=0&&ne().addEventListener("mousemove",W)})),$;function Q(){var e=$.props.touch;return Array.isArray(e)?e:[e,0]}function Z(){return"hold"===Q()[0]}function ee(){var e;return!(null==(e=$.props.render)||!e.$$tippy)}function te(){return L||o}function ne(){var e=te().parentNode;return e?w(e):document}function re(){return S(z)}function oe(e){return $.state.isMounted&&!$.state.isVisible||x.isTouch||C&&"focus"===C.type?0:r($.props.delay,e?0:1,R.delay)}function ie(e){void 0===e&&(e=!1),z.style.pointerEvents=$.props.interactive&&!e?"":"none",z.style.zIndex=""+$.props.zIndex}function ae(e,t,n){var r;(void 0===n&&(n=!0),G.forEach((function(n){n[e]&&n[e].apply(n,t)})),n)&&(r=$.props)[e].apply(r,t)}function se(){var e=$.props.aria;if(e.content){var t="aria-"+e.content,n=z.id;u($.props.triggerTarget||o).forEach((function(e){var r=e.getAttribute(t);if($.state.isVisible)e.setAttribute(t,r?r+" "+n:n);else{var o=r&&r.replace(n,"").trim();o?e.setAttribute(t,o):e.removeAttribute(t)}}))}}function ue(){!K&&$.props.aria.expanded&&u($.props.triggerTarget||o).forEach((function(e){$.props.interactive?e.setAttribute("aria-expanded",$.state.isVisible&&e===te()?"true":"false"):e.removeAttribute("aria-expanded")}))}function ce(){ne().removeEventListener("mousemove",W),H=H.filter((function(e){return e!==W}))}function pe(e){if(!x.isTouch||!N&&"mousedown"!==e.type){var t=e.composedPath&&e.composedPath()[0]||e.target;if(!$.props.interactive||!O(z,t)){if(u($.props.triggerTarget||o).some((function(e){return O(e,t)}))){if(x.isTouch)return;if($.state.isVisible&&$.props.trigger.indexOf("click")>=0)return}else ae("onClickOutside",[$,e]);!0===$.props.hideOnClick&&($.clearDelayTimeouts(),$.hide(),I=!0,setTimeout((function(){I=!1})),$.state.isMounted||ve())}}}function fe(){N=!0}function le(){N=!1}function de(){var e=ne();e.addEventListener("mousedown",pe,!0),e.addEventListener("touchend",pe,t),e.addEventListener("touchstart",le,t),e.addEventListener("touchmove",fe,t)}function ve(){var e=ne();e.removeEventListener("mousedown",pe,!0),e.removeEventListener("touchend",pe,t),e.removeEventListener("touchstart",le,t),e.removeEventListener("touchmove",fe,t)}function me(e,t){var n=re().box;function r(e){e.target===n&&(E(n,"remove",r),t())}if(0===e)return t();E(n,"remove",T),E(n,"add",r),T=r}function ge(e,t,n){void 0===n&&(n=!1),u($.props.triggerTarget||o).forEach((function(r){r.addEventListener(e,t,n),F.push({node:r,eventType:e,handler:t,options:n})}))}function he(){var e;Z()&&(ge("touchstart",ye,{passive:!0}),ge("touchend",Ee,{passive:!0})),(e=$.props.trigger,e.split(/\s+/).filter(Boolean)).forEach((function(e){if("manual"!==e)switch(ge(e,ye),e){case"mouseenter":ge("mouseleave",Ee);break;case"focus":ge(D?"focusout":"blur",Oe);break;case"focusin":ge("focusout",Oe)}}))}function be(){F.forEach((function(e){var t=e.node,n=e.eventType,r=e.handler,o=e.options;t.removeEventListener(n,r,o)})),F=[]}function ye(e){var t,n=!1;if($.state.isEnabled&&!xe(e)&&!I){var r="focus"===(null==(t=C)?void 0:t.type);C=e,L=e.currentTarget,ue(),!$.state.isVisible&&m(e)&&H.forEach((function(t){return t(e)})),"click"===e.type&&($.props.trigger.indexOf("mouseenter")<0||V)&&!1!==$.props.hideOnClick&&$.state.isVisible?n=!0:Le(e),"click"===e.type&&(V=!n),n&&!r&&De(e)}}function we(e){var t=e.target,n=te().contains(t)||z.contains(t);"mousemove"===e.type&&n||function(e,t){var n=t.clientX,r=t.clientY;return e.every((function(e){var t=e.popperRect,o=e.popperState,i=e.props.interactiveBorder,a=p(o.placement),s=o.modifiersData.offset;if(!s)return!0;var u="bottom"===a?s.top.y:0,c="top"===a?s.bottom.y:0,f="right"===a?s.left.x:0,l="left"===a?s.right.x:0,d=t.top-r+u>i,v=r-t.bottom-c>i,m=t.left-n+f>i,g=n-t.right-l>i;return d||v||m||g}))}(Ae().concat(z).map((function(e){var t,n=null==(t=e._tippy.popperInstance)?void 0:t.state;return n?{popperRect:e.getBoundingClientRect(),popperState:n,props:M}:null})).filter(Boolean),e)&&(ce(),De(e))}function Ee(e){xe(e)||$.props.trigger.indexOf("click")>=0&&V||($.props.interactive?$.hideWithInteractivity(e):De(e))}function Oe(e){$.props.trigger.indexOf("focusin")<0&&e.target!==te()||$.props.interactive&&e.relatedTarget&&z.contains(e.relatedTarget)||De(e)}function xe(e){return!!x.isTouch&&Z()!==e.type.indexOf("touch")>=0}function Ce(){Te();var t=$.props,n=t.popperOptions,r=t.placement,i=t.offset,a=t.getReferenceClientRect,s=t.moveTransition,u=ee()?S(z).arrow:null,c=a?{getBoundingClientRect:a,contextElement:a.contextElement||te()}:o,p=[{name:"offset",options:{offset:i}},{name:"preventOverflow",options:{padding:{top:2,bottom:2,left:5,right:5}}},{name:"flip",options:{padding:5}},{name:"computeStyles",options:{adaptive:!s}},{name:"$$tippy",enabled:!0,phase:"beforeWrite",requires:["computeStyles"],fn:function(e){var t=e.state;if(ee()){var n=re().box;["placement","reference-hidden","escaped"].forEach((function(e){"placement"===e?n.setAttribute("data-placement",t.placement):t.attributes.popper["data-popper-"+e]?n.setAttribute("data-"+e,""):n.removeAttribute("data-"+e)})),t.attributes.popper={}}}}];ee()&&u&&p.push({name:"arrow",options:{element:u,padding:3}}),p.push.apply(p,(null==n?void 0:n.modifiers)||[]),$.popperInstance=e.createPopper(c,z,Object.assign({},n,{placement:r,onFirstUpdate:A,modifiers:p}))}function Te(){$.popperInstance&&($.popperInstance.destroy(),$.popperInstance=null)}function Ae(){return f(z.querySelectorAll("[data-tippy-root]"))}function Le(e){$.clearDelayTimeouts(),e&&ae("onTrigger",[$,e]),de();var t=oe(!0),n=Q(),r=n[0],o=n[1];x.isTouch&&"hold"===r&&o&&(t=o),t?v=setTimeout((function(){$.show()}),t):$.show()}function De(e){if($.clearDelayTimeouts(),ae("onUntrigger",[$,e]),$.state.isVisible){if(!($.props.trigger.indexOf("mouseenter")>=0&&$.props.trigger.indexOf("click")>=0&&["mouseleave","mousemove"].indexOf(e.type)>=0&&V)){var t=oe(!1);t?g=setTimeout((function(){$.state.isVisible&&$.hide()}),t):h=requestAnimationFrame((function(){$.hide()}))}}else ve()}}function F(e,n){void 0===n&&(n={});var r=R.plugins.concat(n.plugins||[]);document.addEventListener("touchstart",T,t),window.addEventListener("blur",L);var o=Object.assign({},n,{plugins:r}),i=h(e).reduce((function(e,t){var n=t&&_(t,o);return n&&e.push(n),e}),[]);return v(e)?i[0]:i}F.defaultProps=R,F.setDefaultProps=function(e){Object.keys(e).forEach((function(t){R[t]=e[t]}))},F.currentInput=x;var W=Object.assign({},e.applyStyles,{effect:function(e){var t=e.state,n={popper:{position:t.options.strategy,left:"0",top:"0",margin:"0"},arrow:{position:"absolute"},reference:{}};Object.assign(t.elements.popper.style,n.popper),t.styles=n,t.elements.arrow&&Object.assign(t.elements.arrow.style,n.arrow)}}),X={mouseover:"mouseenter",focusin:"focus",click:"click"};var Y={name:"animateFill",defaultValue:!1,fn:function(e){var t;if(null==(t=e.props.render)||!t.$$tippy)return{};var n=S(e.popper),r=n.box,o=n.content,i=e.props.animateFill?function(){var e=d();return e.className="tippy-backdrop",y([e],"hidden"),e}():null;return{onCreate:function(){i&&(r.insertBefore(i,r.firstElementChild),r.setAttribute("data-animatefill",""),r.style.overflow="hidden",e.setProps({arrow:!1,animation:"shift-away"}))},onMount:function(){if(i){var e=r.style.transitionDuration,t=Number(e.replace("ms",""));o.style.transitionDelay=Math.round(t/10)+"ms",i.style.transitionDuration=e,y([i],"visible")}},onShow:function(){i&&(i.style.transitionDuration="0ms")},onHide:function(){i&&y([i],"hidden")}}}};var $={clientX:0,clientY:0},q=[];function z(e){var t=e.clientX,n=e.clientY;$={clientX:t,clientY:n}}var J={name:"followCursor",defaultValue:!1,fn:function(e){var t=e.reference,n=w(e.props.triggerTarget||t),r=!1,o=!1,i=!0,a=e.props;function s(){return"initial"===e.props.followCursor&&e.state.isVisible}function u(){n.addEventListener("mousemove",f)}function c(){n.removeEventListener("mousemove",f)}function p(){r=!0,e.setProps({getReferenceClientRect:null}),r=!1}function f(n){var r=!n.target||t.contains(n.target),o=e.props.followCursor,i=n.clientX,a=n.clientY,s=t.getBoundingClientRect(),u=i-s.left,c=a-s.top;!r&&e.props.interactive||e.setProps({getReferenceClientRect:function(){var e=t.getBoundingClientRect(),n=i,r=a;"initial"===o&&(n=e.left+u,r=e.top+c);var s="horizontal"===o?e.top:r,p="vertical"===o?e.right:n,f="horizontal"===o?e.bottom:r,l="vertical"===o?e.left:n;return{width:p-l,height:f-s,top:s,right:p,bottom:f,left:l}}})}function l(){e.props.followCursor&&(q.push({instance:e,doc:n}),function(e){e.addEventListener("mousemove",z)}(n))}function d(){0===(q=q.filter((function(t){return t.instance!==e}))).filter((function(e){return e.doc===n})).length&&function(e){e.removeEventListener("mousemove",z)}(n)}return{onCreate:l,onDestroy:d,onBeforeUpdate:function(){a=e.props},onAfterUpdate:function(t,n){var i=n.followCursor;r||void 0!==i&&a.followCursor!==i&&(d(),i?(l(),!e.state.isMounted||o||s()||u()):(c(),p()))},onMount:function(){e.props.followCursor&&!o&&(i&&(f($),i=!1),s()||u())},onTrigger:function(e,t){m(t)&&($={clientX:t.clientX,clientY:t.clientY}),o="focus"===t.type},onHidden:function(){e.props.followCursor&&(p(),c(),i=!0)}}}};var G={name:"inlinePositioning",defaultValue:!1,fn:function(e){var t,n=e.reference;var r=-1,o=!1,i=[],a={name:"tippyInlinePositioning",enabled:!0,phase:"afterWrite",fn:function(o){var a=o.state;e.props.inlinePositioning&&(-1!==i.indexOf(a.placement)&&(i=[]),t!==a.placement&&-1===i.indexOf(a.placement)&&(i.push(a.placement),e.setProps({getReferenceClientRect:function(){return function(e){return function(e,t,n,r){if(n.length<2||null===e)return t;if(2===n.length&&r>=0&&n[0].left>n[1].right)return n[r]||t;switch(e){case"top":case"bottom":var o=n[0],i=n[n.length-1],a="top"===e,s=o.top,u=i.bottom,c=a?o.left:i.left,p=a?o.right:i.right;return{top:s,bottom:u,left:c,right:p,width:p-c,height:u-s};case"left":case"right":var f=Math.min.apply(Math,n.map((function(e){return e.left}))),l=Math.max.apply(Math,n.map((function(e){return e.right}))),d=n.filter((function(t){return"left"===e?t.left===f:t.right===l})),v=d[0].top,m=d[d.length-1].bottom;return{top:v,bottom:m,left:f,right:l,width:l-f,height:m-v};default:return t}}(p(e),n.getBoundingClientRect(),f(n.getClientRects()),r)}(a.placement)}})),t=a.placement)}};function s(){var t;o||(t=function(e,t){var n;return{popperOptions:Object.assign({},e.popperOptions,{modifiers:[].concat(((null==(n=e.popperOptions)?void 0:n.modifiers)||[]).filter((function(e){return e.name!==t.name})),[t])})}}(e.props,a),o=!0,e.setProps(t),o=!1)}return{onCreate:s,onAfterUpdate:s,onTrigger:function(t,n){if(m(n)){var o=f(e.reference.getClientRects()),i=o.find((function(e){return e.left-2<=n.clientX&&e.right+2>=n.clientX&&e.top-2<=n.clientY&&e.bottom+2>=n.clientY})),a=o.indexOf(i);r=a>-1?a:r}},onHidden:function(){r=-1}}}};var K={name:"sticky",defaultValue:!1,fn:function(e){var t=e.reference,n=e.popper;function r(t){return!0===e.props.sticky||e.props.sticky===t}var o=null,i=null;function a(){var s=r("reference")?(e.popperInstance?e.popperInstance.state.elements.reference:t).getBoundingClientRect():null,u=r("popper")?n.getBoundingClientRect():null;(s&&Q(o,s)||u&&Q(i,u))&&e.popperInstance&&e.popperInstance.update(),o=s,i=u,e.state.isMounted&&requestAnimationFrame(a)}return{onMount:function(){e.props.sticky&&a()}}}};function Q(e,t){return!e||!t||(e.top!==t.top||e.right!==t.right||e.bottom!==t.bottom||e.left!==t.left)}return F.setDefaultProps({plugins:[Y,J,G,K],render:N}),F.createSingleton=function(e,t){var n;void 0===t&&(t={});var r,o=e,i=[],a=[],c=t.overrides,p=[],f=!1;function l(){a=o.map((function(e){return u(e.props.triggerTarget||e.reference)})).reduce((function(e,t){return e.concat(t)}),[])}function v(){i=o.map((function(e){return e.reference}))}function m(e){o.forEach((function(t){e?t.enable():t.disable()}))}function g(e){return o.map((function(t){var n=t.setProps;return t.setProps=function(o){n(o),t.reference===r&&e.setProps(o)},function(){t.setProps=n}}))}function h(e,t){var n=a.indexOf(t);if(t!==r){r=t;var s=(c||[]).concat("content").reduce((function(e,t){return e[t]=o[n].props[t],e}),{});e.setProps(Object.assign({},s,{getReferenceClientRect:"function"==typeof s.getReferenceClientRect?s.getReferenceClientRect:function(){var e;return null==(e=i[n])?void 0:e.getBoundingClientRect()}}))}}m(!1),v(),l();var b={fn:function(){return{onDestroy:function(){m(!0)},onHidden:function(){r=null},onClickOutside:function(e){e.props.showOnCreate&&!f&&(f=!0,r=null)},onShow:function(e){e.props.showOnCreate&&!f&&(f=!0,h(e,i[0]))},onTrigger:function(e,t){h(e,t.currentTarget)}}}},y=F(d(),Object.assign({},s(t,["overrides"]),{plugins:[b].concat(t.plugins||[]),triggerTarget:a,popperOptions:Object.assign({},t.popperOptions,{modifiers:[].concat((null==(n=t.popperOptions)?void 0:n.modifiers)||[],[W])})})),w=y.show;y.show=function(e){if(w(),!r&&null==e)return h(y,i[0]);if(!r||null!=e){if("number"==typeof e)return i[e]&&h(y,i[e]);if(o.indexOf(e)>=0){var t=e.reference;return h(y,t)}return i.indexOf(e)>=0?h(y,e):void 0}},y.showNext=function(){var e=i[0];if(!r)return y.show(0);var t=i.indexOf(r);y.show(i[t+1]||e)},y.showPrevious=function(){var e=i[i.length-1];if(!r)return y.show(e);var t=i.indexOf(r),n=i[t-1]||e;y.show(n)};var E=y.setProps;return y.setProps=function(e){c=e.overrides||c,E(e)},y.setInstances=function(e){m(!0),p.forEach((function(e){return e()})),o=e,m(!1),v(),l(),p=g(y),y.setProps({triggerTarget:a})},p=g(y),y},F.delegate=function(e,n){var r=[],o=[],i=!1,a=n.target,c=s(n,["target"]),p=Object.assign({},c,{trigger:"manual",touch:!1}),f=Object.assign({touch:R.touch},c,{showOnCreate:!0}),l=F(e,p);function d(e){if(e.target&&!i){var t=e.target.closest(a);if(t){var r=t.getAttribute("data-tippy-trigger")||n.trigger||R.trigger;if(!t._tippy&&!("touchstart"===e.type&&"boolean"==typeof f.touch||"touchstart"!==e.type&&r.indexOf(X[e.type])<0)){var s=F(t,f);s&&(o=o.concat(s))}}}}function v(e,t,n,o){void 0===o&&(o=!1),e.addEventListener(t,n,o),r.push({node:e,eventType:t,handler:n,options:o})}return u(l).forEach((function(e){var n=e.destroy,a=e.enable,s=e.disable;e.destroy=function(e){void 0===e&&(e=!0),e&&o.forEach((function(e){e.destroy()})),o=[],r.forEach((function(e){var t=e.node,n=e.eventType,r=e.handler,o=e.options;t.removeEventListener(n,r,o)})),r=[],n()},e.enable=function(){a(),o.forEach((function(e){return e.enable()})),i=!1},e.disable=function(){s(),o.forEach((function(e){return e.disable()})),i=!0},function(e){var n=e.reference;v(n,"touchstart",d,t),v(n,"mouseover",d),v(n,"focusin",d),v(n,"click",d)}(e)})),l},F.hideAll=function(e){var t=void 0===e?{}:e,n=t.exclude,r=t.duration;U.forEach((function(e){var t=!1;if(n&&(t=g(n)?e.reference===n:e.popper===n.popper),!t){var o=e.props.duration;e.setProps({duration:r}),e.hide(),e.state.isDestroyed||e.setProps({duration:o})}}))},F.roundArrow='',F})); 2 | 3 | -------------------------------------------------------------------------------- /_book/site_libs/quarto-nav/headroom.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * headroom.js v0.12.0 - Give your page some headroom. Hide your header until you need it 3 | * Copyright (c) 2020 Nick Williams - http://wicky.nillia.ms/headroom.js 4 | * License: MIT 5 | */ 6 | 7 | !function(t,n){"object"==typeof exports&&"undefined"!=typeof module?module.exports=n():"function"==typeof define&&define.amd?define(n):(t=t||self).Headroom=n()}(this,function(){"use strict";function t(){return"undefined"!=typeof window}function d(t){return function(t){return t&&t.document&&function(t){return 9===t.nodeType}(t.document)}(t)?function(t){var n=t.document,o=n.body,s=n.documentElement;return{scrollHeight:function(){return Math.max(o.scrollHeight,s.scrollHeight,o.offsetHeight,s.offsetHeight,o.clientHeight,s.clientHeight)},height:function(){return t.innerHeight||s.clientHeight||o.clientHeight},scrollY:function(){return void 0!==t.pageYOffset?t.pageYOffset:(s||o.parentNode||o).scrollTop}}}(t):function(t){return{scrollHeight:function(){return Math.max(t.scrollHeight,t.offsetHeight,t.clientHeight)},height:function(){return Math.max(t.offsetHeight,t.clientHeight)},scrollY:function(){return t.scrollTop}}}(t)}function n(t,s,e){var n,o=function(){var n=!1;try{var t={get passive(){n=!0}};window.addEventListener("test",t,t),window.removeEventListener("test",t,t)}catch(t){n=!1}return n}(),i=!1,r=d(t),l=r.scrollY(),a={};function c(){var t=Math.round(r.scrollY()),n=r.height(),o=r.scrollHeight();a.scrollY=t,a.lastScrollY=l,a.direction=ls.tolerance[a.direction],e(a),l=t,i=!1}function h(){i||(i=!0,n=requestAnimationFrame(c))}var u=!!o&&{passive:!0,capture:!1};return t.addEventListener("scroll",h,u),c(),{destroy:function(){cancelAnimationFrame(n),t.removeEventListener("scroll",h,u)}}}function o(t){return t===Object(t)?t:{down:t,up:t}}function s(t,n){n=n||{},Object.assign(this,s.options,n),this.classes=Object.assign({},s.options.classes,n.classes),this.elem=t,this.tolerance=o(this.tolerance),this.offset=o(this.offset),this.initialised=!1,this.frozen=!1}return s.prototype={constructor:s,init:function(){return s.cutsTheMustard&&!this.initialised&&(this.addClass("initial"),this.initialised=!0,setTimeout(function(t){t.scrollTracker=n(t.scroller,{offset:t.offset,tolerance:t.tolerance},t.update.bind(t))},100,this)),this},destroy:function(){this.initialised=!1,Object.keys(this.classes).forEach(this.removeClass,this),this.scrollTracker.destroy()},unpin:function(){!this.hasClass("pinned")&&this.hasClass("unpinned")||(this.addClass("unpinned"),this.removeClass("pinned"),this.onUnpin&&this.onUnpin.call(this))},pin:function(){this.hasClass("unpinned")&&(this.addClass("pinned"),this.removeClass("unpinned"),this.onPin&&this.onPin.call(this))},freeze:function(){this.frozen=!0,this.addClass("frozen")},unfreeze:function(){this.frozen=!1,this.removeClass("frozen")},top:function(){this.hasClass("top")||(this.addClass("top"),this.removeClass("notTop"),this.onTop&&this.onTop.call(this))},notTop:function(){this.hasClass("notTop")||(this.addClass("notTop"),this.removeClass("top"),this.onNotTop&&this.onNotTop.call(this))},bottom:function(){this.hasClass("bottom")||(this.addClass("bottom"),this.removeClass("notBottom"),this.onBottom&&this.onBottom.call(this))},notBottom:function(){this.hasClass("notBottom")||(this.addClass("notBottom"),this.removeClass("bottom"),this.onNotBottom&&this.onNotBottom.call(this))},shouldUnpin:function(t){return"down"===t.direction&&!t.top&&t.toleranceExceeded},shouldPin:function(t){return"up"===t.direction&&t.toleranceExceeded||t.top},addClass:function(t){this.elem.classList.add.apply(this.elem.classList,this.classes[t].split(" "))},removeClass:function(t){this.elem.classList.remove.apply(this.elem.classList,this.classes[t].split(" "))},hasClass:function(t){return this.classes[t].split(" ").every(function(t){return this.classList.contains(t)},this.elem)},update:function(t){t.isOutOfBounds||!0!==this.frozen&&(t.top?this.top():this.notTop(),t.bottom?this.bottom():this.notBottom(),this.shouldUnpin(t)?this.unpin():this.shouldPin(t)&&this.pin())}},s.options={tolerance:{up:0,down:0},offset:0,scroller:t()?window:null,classes:{frozen:"headroom--frozen",pinned:"headroom--pinned",unpinned:"headroom--unpinned",top:"headroom--top",notTop:"headroom--not-top",bottom:"headroom--bottom",notBottom:"headroom--not-bottom",initial:"headroom"}},s.cutsTheMustard=!!(t()&&function(){}.bind&&"classList"in document.documentElement&&Object.assign&&Object.keys&&requestAnimationFrame),s}); 8 | -------------------------------------------------------------------------------- /_book/site_libs/quarto-nav/quarto-nav.js: -------------------------------------------------------------------------------- 1 | const headroomChanged = new CustomEvent("quarto-hrChanged", { 2 | detail: {}, 3 | bubbles: true, 4 | cancelable: false, 5 | composed: false, 6 | }); 7 | 8 | window.document.addEventListener("DOMContentLoaded", function () { 9 | let init = false; 10 | 11 | function throttle(func, wait) { 12 | var timeout; 13 | return function () { 14 | const context = this; 15 | const args = arguments; 16 | const later = function () { 17 | clearTimeout(timeout); 18 | timeout = null; 19 | func.apply(context, args); 20 | }; 21 | 22 | if (!timeout) { 23 | timeout = setTimeout(later, wait); 24 | } 25 | }; 26 | } 27 | 28 | function headerOffset() { 29 | // Set an offset if there is are fixed top navbar 30 | const headerEl = window.document.querySelector("header.fixed-top"); 31 | if (headerEl) { 32 | return headerEl.clientHeight; 33 | } else { 34 | return 0; 35 | } 36 | } 37 | 38 | function footerOffset() { 39 | const footerEl = window.document.querySelector("footer.footer"); 40 | if (footerEl) { 41 | return footerEl.clientHeight; 42 | } else { 43 | return 0; 44 | } 45 | } 46 | 47 | function updateDocumentOffsetWithoutAnimation() { 48 | updateDocumentOffset(false); 49 | } 50 | 51 | function updateDocumentOffset(animated) { 52 | // set body offset 53 | const topOffset = headerOffset(); 54 | const bodyOffset = topOffset + footerOffset(); 55 | const bodyEl = window.document.body; 56 | bodyEl.setAttribute("data-bs-offset", topOffset); 57 | bodyEl.style.paddingTop = topOffset + "px"; 58 | 59 | // deal with sidebar offsets 60 | const sidebars = window.document.querySelectorAll( 61 | ".sidebar, .headroom-target" 62 | ); 63 | sidebars.forEach((sidebar) => { 64 | if (!animated) { 65 | sidebar.classList.add("notransition"); 66 | // Remove the no transition class after the animation has time to complete 67 | setTimeout(function () { 68 | sidebar.classList.remove("notransition"); 69 | }, 201); 70 | } 71 | 72 | if (window.Headroom && sidebar.classList.contains("sidebar-unpinned")) { 73 | sidebar.style.top = "0"; 74 | sidebar.style.maxHeight = "100vh"; 75 | } else { 76 | sidebar.style.top = topOffset + "px"; 77 | sidebar.style.maxHeight = "calc(100vh - " + topOffset + "px)"; 78 | } 79 | }); 80 | 81 | // allow space for footer 82 | const mainContainer = window.document.querySelector(".quarto-container"); 83 | if (mainContainer) { 84 | mainContainer.style.minHeight = "calc(100vh - " + bodyOffset + "px)"; 85 | } 86 | 87 | // link offset 88 | let linkStyle = window.document.querySelector("#quarto-target-style"); 89 | if (!linkStyle) { 90 | linkStyle = window.document.createElement("style"); 91 | linkStyle.setAttribute("id", "quarto-target-style"); 92 | window.document.head.appendChild(linkStyle); 93 | } 94 | while (linkStyle.firstChild) { 95 | linkStyle.removeChild(linkStyle.firstChild); 96 | } 97 | if (topOffset > 0) { 98 | linkStyle.appendChild( 99 | window.document.createTextNode(` 100 | section:target::before { 101 | content: ""; 102 | display: block; 103 | height: ${topOffset}px; 104 | margin: -${topOffset}px 0 0; 105 | }`) 106 | ); 107 | } 108 | if (init) { 109 | window.dispatchEvent(headroomChanged); 110 | } 111 | init = true; 112 | } 113 | 114 | // initialize headroom 115 | var header = window.document.querySelector("#quarto-header"); 116 | if (header && window.Headroom) { 117 | const headroom = new window.Headroom(header, { 118 | tolerance: 5, 119 | onPin: function () { 120 | const sidebars = window.document.querySelectorAll( 121 | ".sidebar, .headroom-target" 122 | ); 123 | sidebars.forEach((sidebar) => { 124 | sidebar.classList.remove("sidebar-unpinned"); 125 | }); 126 | updateDocumentOffset(); 127 | }, 128 | onUnpin: function () { 129 | const sidebars = window.document.querySelectorAll( 130 | ".sidebar, .headroom-target" 131 | ); 132 | sidebars.forEach((sidebar) => { 133 | sidebar.classList.add("sidebar-unpinned"); 134 | }); 135 | updateDocumentOffset(); 136 | }, 137 | }); 138 | headroom.init(); 139 | 140 | let frozen = false; 141 | window.quartoToggleHeadroom = function () { 142 | if (frozen) { 143 | headroom.unfreeze(); 144 | frozen = false; 145 | } else { 146 | headroom.freeze(); 147 | frozen = true; 148 | } 149 | }; 150 | } 151 | 152 | // Observe size changed for the header 153 | const headerEl = window.document.querySelector("header.fixed-top"); 154 | if (headerEl && window.ResizeObserver) { 155 | const observer = new window.ResizeObserver( 156 | updateDocumentOffsetWithoutAnimation 157 | ); 158 | observer.observe(headerEl, { 159 | attributes: true, 160 | childList: true, 161 | characterData: true, 162 | }); 163 | } else { 164 | window.addEventListener( 165 | "resize", 166 | throttle(updateDocumentOffsetWithoutAnimation, 50) 167 | ); 168 | } 169 | setTimeout(updateDocumentOffsetWithoutAnimation, 250); 170 | 171 | // fixup index.html links if we aren't on the filesystem 172 | if (window.location.protocol !== "file:") { 173 | const links = window.document.querySelectorAll("a"); 174 | for (let i = 0; i < links.length; i++) { 175 | links[i].href = links[i].href.replace(/\/index\.html/, "/"); 176 | } 177 | 178 | // Fixup any sharing links that require urls 179 | // Append url to any sharing urls 180 | const sharingLinks = window.document.querySelectorAll( 181 | "a.sidebar-tools-main-item" 182 | ); 183 | for (let i = 0; i < sharingLinks.length; i++) { 184 | const sharingLink = sharingLinks[i]; 185 | const href = sharingLink.getAttribute("href"); 186 | if (href) { 187 | sharingLink.setAttribute( 188 | "href", 189 | href.replace("|url|", window.location.href) 190 | ); 191 | } 192 | } 193 | 194 | // Scroll the active navigation item into view, if necessary 195 | const navSidebar = window.document.querySelector("nav#quarto-sidebar"); 196 | if (navSidebar) { 197 | // Find the active item 198 | const activeItem = navSidebar.querySelector("li.sidebar-item a.active"); 199 | if (activeItem) { 200 | // Wait for the scroll height and height to resolve by observing size changes on the 201 | // nav element that is scrollable 202 | const resizeObserver = new ResizeObserver((_entries) => { 203 | // The bottom of the element 204 | const elBottom = activeItem.offsetTop; 205 | const viewBottom = navSidebar.scrollTop + navSidebar.clientHeight; 206 | 207 | // The element height and scroll height are the same, then we are still loading 208 | if (viewBottom !== navSidebar.scrollHeight) { 209 | // Determine if the item isn't visible and scroll to it 210 | if (elBottom >= viewBottom) { 211 | navSidebar.scrollTop = elBottom; 212 | } 213 | 214 | // stop observing now since we've completed the scroll 215 | resizeObserver.unobserve(navSidebar); 216 | } 217 | }); 218 | resizeObserver.observe(navSidebar); 219 | } 220 | } 221 | } 222 | }); 223 | -------------------------------------------------------------------------------- /_book/site_libs/quarto-search/fuse.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Fuse.js v6.5.3 - Lightweight fuzzy-search (http://fusejs.io) 3 | * 4 | * Copyright (c) 2021 Kiro Risk (http://kiro.me) 5 | * All Rights Reserved. Apache Software License 2.0 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | */ 9 | var e,t;e=this,t=function(){"use strict";function e(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function t(t){for(var n=1;ne.length)&&(t=e.length);for(var n=0,r=new Array(t);n0&&void 0!==arguments[0]?arguments[0]:1,t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:3,n=new Map,r=Math.pow(10,t);return{get:function(t){var i=t.match(C).length;if(n.has(i))return n.get(i);var o=1/Math.pow(i,.5*e),c=parseFloat(Math.round(o*r)/r);return n.set(i,c),c},clear:function(){n.clear()}}}var $=function(){function e(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},n=t.getFn,i=void 0===n?I.getFn:n,o=t.fieldNormWeight,c=void 0===o?I.fieldNormWeight:o;r(this,e),this.norm=E(c,3),this.getFn=i,this.isCreated=!1,this.setIndexRecords()}return o(e,[{key:"setSources",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[];this.docs=e}},{key:"setIndexRecords",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[];this.records=e}},{key:"setKeys",value:function(){var e=this,t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[];this.keys=t,this._keysMap={},t.forEach((function(t,n){e._keysMap[t.id]=n}))}},{key:"create",value:function(){var e=this;!this.isCreated&&this.docs.length&&(this.isCreated=!0,g(this.docs[0])?this.docs.forEach((function(t,n){e._addString(t,n)})):this.docs.forEach((function(t,n){e._addObject(t,n)})),this.norm.clear())}},{key:"add",value:function(e){var t=this.size();g(e)?this._addString(e,t):this._addObject(e,t)}},{key:"removeAt",value:function(e){this.records.splice(e,1);for(var t=e,n=this.size();t2&&void 0!==arguments[2]?arguments[2]:{},r=n.getFn,i=void 0===r?I.getFn:r,o=n.fieldNormWeight,c=void 0===o?I.fieldNormWeight:o,a=new $({getFn:i,fieldNormWeight:c});return a.setKeys(e.map(_)),a.setSources(t),a.create(),a}function F(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},n=t.errors,r=void 0===n?0:n,i=t.currentLocation,o=void 0===i?0:i,c=t.expectedLocation,a=void 0===c?0:c,s=t.distance,u=void 0===s?I.distance:s,h=t.ignoreLocation,f=void 0===h?I.ignoreLocation:h,l=r/e.length;if(f)return l;var d=Math.abs(a-o);return u?l+d/u:d?1:l}function N(){for(var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[],t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:I.minMatchCharLength,n=[],r=-1,i=-1,o=0,c=e.length;o=t&&n.push([r,i]),r=-1)}return e[o-1]&&o-r>=t&&n.push([r,o-1]),n}var P=32;function W(e){for(var t={},n=0,r=e.length;n1&&void 0!==arguments[1]?arguments[1]:{},o=i.location,c=void 0===o?I.location:o,a=i.threshold,s=void 0===a?I.threshold:a,u=i.distance,h=void 0===u?I.distance:u,f=i.includeMatches,l=void 0===f?I.includeMatches:f,d=i.findAllMatches,v=void 0===d?I.findAllMatches:d,g=i.minMatchCharLength,y=void 0===g?I.minMatchCharLength:g,p=i.isCaseSensitive,m=void 0===p?I.isCaseSensitive:p,k=i.ignoreLocation,M=void 0===k?I.ignoreLocation:k;if(r(this,e),this.options={location:c,threshold:s,distance:h,includeMatches:l,findAllMatches:v,minMatchCharLength:y,isCaseSensitive:m,ignoreLocation:M},this.pattern=m?t:t.toLowerCase(),this.chunks=[],this.pattern.length){var b=function(e,t){n.chunks.push({pattern:e,alphabet:W(e),startIndex:t})},x=this.pattern.length;if(x>P){for(var w=0,L=x%P,S=x-L;w3&&void 0!==arguments[3]?arguments[3]:{},i=r.location,o=void 0===i?I.location:i,c=r.distance,a=void 0===c?I.distance:c,s=r.threshold,u=void 0===s?I.threshold:s,h=r.findAllMatches,f=void 0===h?I.findAllMatches:h,l=r.minMatchCharLength,d=void 0===l?I.minMatchCharLength:l,v=r.includeMatches,g=void 0===v?I.includeMatches:v,y=r.ignoreLocation,p=void 0===y?I.ignoreLocation:y;if(t.length>P)throw new Error(w(P));for(var m,k=t.length,M=e.length,b=Math.max(0,Math.min(o,M)),x=u,L=b,S=d>1||g,_=S?Array(M):[];(m=e.indexOf(t,L))>-1;){var O=F(t,{currentLocation:m,expectedLocation:b,distance:a,ignoreLocation:p});if(x=Math.min(O,x),L=m+k,S)for(var j=0;j=z;q-=1){var B=q-1,J=n[e.charAt(B)];if(S&&(_[B]=+!!J),K[q]=(K[q+1]<<1|1)&J,R&&(K[q]|=(A[q+1]|A[q])<<1|1|A[q+1]),K[q]&$&&(C=F(t,{errors:R,currentLocation:B,expectedLocation:b,distance:a,ignoreLocation:p}))<=x){if(x=C,(L=B)<=b)break;z=Math.max(1,2*b-L)}}if(F(t,{errors:R+1,currentLocation:b,expectedLocation:b,distance:a,ignoreLocation:p})>x)break;A=K}var U={isMatch:L>=0,score:Math.max(.001,C)};if(S){var V=N(_,d);V.length?g&&(U.indices=V):U.isMatch=!1}return U}(e,n,i,{location:c+o,distance:a,threshold:s,findAllMatches:u,minMatchCharLength:h,includeMatches:r,ignoreLocation:f}),p=y.isMatch,m=y.score,k=y.indices;p&&(g=!0),v+=m,p&&k&&(d=[].concat(l(d),l(k)))}));var y={isMatch:g,score:g?v/this.chunks.length:1};return g&&r&&(y.indices=d),y}}]),e}(),z=function(){function e(t){r(this,e),this.pattern=t}return o(e,[{key:"search",value:function(){}}],[{key:"isMultiMatch",value:function(e){return D(e,this.multiRegex)}},{key:"isSingleMatch",value:function(e){return D(e,this.singleRegex)}}]),e}();function D(e,t){var n=e.match(t);return n?n[1]:null}var K=function(e){a(n,e);var t=f(n);function n(e){return r(this,n),t.call(this,e)}return o(n,[{key:"search",value:function(e){var t=e===this.pattern;return{isMatch:t,score:t?0:1,indices:[0,this.pattern.length-1]}}}],[{key:"type",get:function(){return"exact"}},{key:"multiRegex",get:function(){return/^="(.*)"$/}},{key:"singleRegex",get:function(){return/^=(.*)$/}}]),n}(z),q=function(e){a(n,e);var t=f(n);function n(e){return r(this,n),t.call(this,e)}return o(n,[{key:"search",value:function(e){var t=-1===e.indexOf(this.pattern);return{isMatch:t,score:t?0:1,indices:[0,e.length-1]}}}],[{key:"type",get:function(){return"inverse-exact"}},{key:"multiRegex",get:function(){return/^!"(.*)"$/}},{key:"singleRegex",get:function(){return/^!(.*)$/}}]),n}(z),B=function(e){a(n,e);var t=f(n);function n(e){return r(this,n),t.call(this,e)}return o(n,[{key:"search",value:function(e){var t=e.startsWith(this.pattern);return{isMatch:t,score:t?0:1,indices:[0,this.pattern.length-1]}}}],[{key:"type",get:function(){return"prefix-exact"}},{key:"multiRegex",get:function(){return/^\^"(.*)"$/}},{key:"singleRegex",get:function(){return/^\^(.*)$/}}]),n}(z),J=function(e){a(n,e);var t=f(n);function n(e){return r(this,n),t.call(this,e)}return o(n,[{key:"search",value:function(e){var t=!e.startsWith(this.pattern);return{isMatch:t,score:t?0:1,indices:[0,e.length-1]}}}],[{key:"type",get:function(){return"inverse-prefix-exact"}},{key:"multiRegex",get:function(){return/^!\^"(.*)"$/}},{key:"singleRegex",get:function(){return/^!\^(.*)$/}}]),n}(z),U=function(e){a(n,e);var t=f(n);function n(e){return r(this,n),t.call(this,e)}return o(n,[{key:"search",value:function(e){var t=e.endsWith(this.pattern);return{isMatch:t,score:t?0:1,indices:[e.length-this.pattern.length,e.length-1]}}}],[{key:"type",get:function(){return"suffix-exact"}},{key:"multiRegex",get:function(){return/^"(.*)"\$$/}},{key:"singleRegex",get:function(){return/^(.*)\$$/}}]),n}(z),V=function(e){a(n,e);var t=f(n);function n(e){return r(this,n),t.call(this,e)}return o(n,[{key:"search",value:function(e){var t=!e.endsWith(this.pattern);return{isMatch:t,score:t?0:1,indices:[0,e.length-1]}}}],[{key:"type",get:function(){return"inverse-suffix-exact"}},{key:"multiRegex",get:function(){return/^!"(.*)"\$$/}},{key:"singleRegex",get:function(){return/^!(.*)\$$/}}]),n}(z),G=function(e){a(n,e);var t=f(n);function n(e){var i,o=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},c=o.location,a=void 0===c?I.location:c,s=o.threshold,u=void 0===s?I.threshold:s,h=o.distance,f=void 0===h?I.distance:h,l=o.includeMatches,d=void 0===l?I.includeMatches:l,v=o.findAllMatches,g=void 0===v?I.findAllMatches:v,y=o.minMatchCharLength,p=void 0===y?I.minMatchCharLength:y,m=o.isCaseSensitive,k=void 0===m?I.isCaseSensitive:m,M=o.ignoreLocation,b=void 0===M?I.ignoreLocation:M;return r(this,n),(i=t.call(this,e))._bitapSearch=new T(e,{location:a,threshold:u,distance:f,includeMatches:d,findAllMatches:g,minMatchCharLength:p,isCaseSensitive:k,ignoreLocation:b}),i}return o(n,[{key:"search",value:function(e){return this._bitapSearch.searchIn(e)}}],[{key:"type",get:function(){return"fuzzy"}},{key:"multiRegex",get:function(){return/^"(.*)"$/}},{key:"singleRegex",get:function(){return/^(.*)$/}}]),n}(z),H=function(e){a(n,e);var t=f(n);function n(e){return r(this,n),t.call(this,e)}return o(n,[{key:"search",value:function(e){for(var t,n=0,r=[],i=this.pattern.length;(t=e.indexOf(this.pattern,n))>-1;)n=t+i,r.push([t,n-1]);var o=!!r.length;return{isMatch:o,score:o?0:1,indices:r}}}],[{key:"type",get:function(){return"include"}},{key:"multiRegex",get:function(){return/^'"(.*)"$/}},{key:"singleRegex",get:function(){return/^'(.*)$/}}]),n}(z),Q=[K,H,B,J,V,U,q,G],X=Q.length,Y=/ +(?=([^\"]*\"[^\"]*\")*[^\"]*$)/;function Z(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};return e.split("|").map((function(e){for(var n=e.trim().split(Y).filter((function(e){return e&&!!e.trim()})),r=[],i=0,o=n.length;i1&&void 0!==arguments[1]?arguments[1]:{},i=n.isCaseSensitive,o=void 0===i?I.isCaseSensitive:i,c=n.includeMatches,a=void 0===c?I.includeMatches:c,s=n.minMatchCharLength,u=void 0===s?I.minMatchCharLength:s,h=n.ignoreLocation,f=void 0===h?I.ignoreLocation:h,l=n.findAllMatches,d=void 0===l?I.findAllMatches:l,v=n.location,g=void 0===v?I.location:v,y=n.threshold,p=void 0===y?I.threshold:y,m=n.distance,k=void 0===m?I.distance:m;r(this,e),this.query=null,this.options={isCaseSensitive:o,includeMatches:a,minMatchCharLength:u,findAllMatches:d,ignoreLocation:f,location:g,threshold:p,distance:k},this.pattern=o?t:t.toLowerCase(),this.query=Z(this.pattern,this.options)}return o(e,[{key:"searchIn",value:function(e){var t=this.query;if(!t)return{isMatch:!1,score:1};var n=this.options,r=n.includeMatches;e=n.isCaseSensitive?e:e.toLowerCase();for(var i=0,o=[],c=0,a=0,s=t.length;a-1&&(n.refIndex=e.idx),t.matches.push(n)}}))}function ve(e,t){t.score=e.score}function ge(e,t){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{},r=n.includeMatches,i=void 0===r?I.includeMatches:r,o=n.includeScore,c=void 0===o?I.includeScore:o,a=[];return i&&a.push(de),c&&a.push(ve),e.map((function(e){var n=e.idx,r={item:t[n],refIndex:n};return a.length&&a.forEach((function(t){t(e,r)})),r}))}var ye=function(){function e(n){var i=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},o=arguments.length>2?arguments[2]:void 0;r(this,e),this.options=t(t({},I),i),this.options.useExtendedSearch,this._keyStore=new S(this.options.keys),this.setCollection(n,o)}return o(e,[{key:"setCollection",value:function(e,t){if(this._docs=e,t&&!(t instanceof $))throw new Error("Incorrect 'index' type");this._myIndex=t||R(this.options.keys,this._docs,{getFn:this.options.getFn,fieldNormWeight:this.options.fieldNormWeight})}},{key:"add",value:function(e){k(e)&&(this._docs.push(e),this._myIndex.add(e))}},{key:"remove",value:function(){for(var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:function(){return!1},t=[],n=0,r=this._docs.length;n1&&void 0!==arguments[1]?arguments[1]:{},n=t.limit,r=void 0===n?-1:n,i=this.options,o=i.includeMatches,c=i.includeScore,a=i.shouldSort,s=i.sortFn,u=i.ignoreFieldNorm,h=g(e)?g(this._docs[0])?this._searchStringList(e):this._searchObjectList(e):this._searchLogical(e);return le(h,{ignoreFieldNorm:u}),a&&h.sort(s),y(r)&&r>-1&&(h=h.slice(0,r)),ge(h,this._docs,{includeMatches:o,includeScore:c})}},{key:"_searchStringList",value:function(e){var t=re(e,this.options),n=this._myIndex.records,r=[];return n.forEach((function(e){var n=e.v,i=e.i,o=e.n;if(k(n)){var c=t.searchIn(n),a=c.isMatch,s=c.score,u=c.indices;a&&r.push({item:n,idx:i,matches:[{score:s,value:n,norm:o,indices:u}]})}})),r}},{key:"_searchLogical",value:function(e){var t=this,n=function(e,t){var n=(arguments.length>2&&void 0!==arguments[2]?arguments[2]:{}).auto,r=void 0===n||n,i=function e(n){var i=Object.keys(n),o=ue(n);if(!o&&i.length>1&&!se(n))return e(fe(n));if(he(n)){var c=o?n[ce]:i[0],a=o?n[ae]:n[c];if(!g(a))throw new Error(x(c));var s={keyId:j(c),pattern:a};return r&&(s.searcher=re(a,t)),s}var u={children:[],operator:i[0]};return i.forEach((function(t){var r=n[t];v(r)&&r.forEach((function(t){u.children.push(e(t))}))})),u};return se(e)||(e=fe(e)),i(e)}(e,this.options),r=function e(n,r,i){if(!n.children){var o=n.keyId,c=n.searcher,a=t._findMatches({key:t._keyStore.get(o),value:t._myIndex.getValueForItemAtKeyId(r,o),searcher:c});return a&&a.length?[{idx:i,item:r,matches:a}]:[]}for(var s=[],u=0,h=n.children.length;u1&&void 0!==arguments[1]?arguments[1]:{},n=t.getFn,r=void 0===n?I.getFn:n,i=t.fieldNormWeight,o=void 0===i?I.fieldNormWeight:i,c=e.keys,a=e.records,s=new $({getFn:r,fieldNormWeight:o});return s.setKeys(c),s.setIndexRecords(a),s},ye.config=I,function(){ne.push.apply(ne,arguments)}(te),ye},"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e="undefined"!=typeof globalThis?globalThis:e||self).Fuse=t(); -------------------------------------------------------------------------------- /_publish.yml: -------------------------------------------------------------------------------- 1 | - source: project 2 | netlify: 3 | - id: 0496a9d6-dbcf-462a-9c27-97f7f7beb4f9 4 | url: 'https://gt.albert-rapp.de' 5 | -------------------------------------------------------------------------------- /_quarto.yml: -------------------------------------------------------------------------------- 1 | project: 2 | type: book 3 | 4 | book: 5 | title: "Creating beautiful tables in R with {gt}" 6 | author: "Albert Rapp" 7 | date: today 8 | chapters: 9 | - index.qmd 10 | - getting_started.qmd 11 | - fancy_stuff.qmd 12 | - formatting.qmd 13 | - styling.qmd 14 | - case_studies.qmd 15 | - quarto_gt.qmd 16 | repo-url: https://github.com/AlbertRapp/gt_book 17 | cookie-consent: true 18 | google-analytics: G-Y3M69TZVPJ 19 | 20 | format: 21 | html: 22 | tbl-cap-location: margin 23 | fig-cap-location: margin 24 | theme: 25 | - theme.scss 26 | 27 | knitr: 28 | opts_chunk: 29 | collapse: true 30 | 31 | editor: visual 32 | 33 | -------------------------------------------------------------------------------- /brands.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlbertRapp/gt_book/7bec14ad39260f4f6f54d4da582308fbb0f69b50/brands.png -------------------------------------------------------------------------------- /brands_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlbertRapp/gt_book/7bec14ad39260f4f6f54d4da582308fbb0f69b50/brands_background.png -------------------------------------------------------------------------------- /cover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlbertRapp/gt_book/7bec14ad39260f4f6f54d4da582308fbb0f69b50/cover.png -------------------------------------------------------------------------------- /data/ratios.csv: -------------------------------------------------------------------------------- 1 | location;inventory_turnover;store_upper;store_lower 2 | Basin City;12,7;8;4,5 3 | King's Landing Location;4,4;7,6;4,3 4 | Hilwood;10;15;6 5 | Shermer Store;6,8;4,4;2,5 6 | The Citadel;7,4;5,5;3 7 | Sunnydale;8;5,1;1,25 8 | Metropolis;11,3;24;11,3 9 | Dark City;9,8;16;8,25 10 | Venusville;10,4;15,5;12,3 11 | Bedrock;7,8;7,6;5,2 12 | South Park;9,3;12,5;5,7 13 | Hill Valley;11;22;7,5 14 | Wellsville;13,6;7,6;2,5 15 | Vice City;8,4;8,4;4,8 16 | Castle Rock;14,7;24,3;20 17 | Atlantis;12,3;12,8;7,8 18 | Bluffington Store;11,7;4,2;3 19 | Emerald City;10,3;14,9;12 20 | Whoville;9;14,5;11,3 21 | Bikini Bottom;11,6;12,5;7,8 22 | Neverland;12,1;18;13,4 23 | Mordor;7,1;12;4,7 24 | LA;5,3;5,4;4,5 25 | -------------------------------------------------------------------------------- /formatting.qmd: -------------------------------------------------------------------------------- 1 | # Formatting {#sec-formatting} 2 | 3 | ```{r} 4 | #| echo: false 5 | library(knitr) 6 | knit_print.gt <- function(x, ...) { 7 | # Two steps to avoid most Quarto changes of my table styles: 8 | # 1. as_raw_html() to use table styles *inline* 9 | # 2. wrap output in a div that resets all Quarto styles 10 | stringr::str_c( 11 | "
\n", 12 | gt::as_raw_html(x), 13 | "\n
" 14 | ) |> 15 | knitr::asis_output() 16 | } 17 | registerS3method( 18 | "knit_print", 'gt_tbl', knit_print.gt, 19 | envir = asNamespace("gt") 20 | # important to overwrite {gt}s knit_print 21 | ) 22 | ``` 23 | 24 | `{gt}` has two families of functions that handle a lot of the data formatting parts. 25 | And you have already seen members of these families, namely `fmt_number()` and `sub_zero()`. 26 | In this chapter, we're going to discover some of their siblings. 27 | 28 | The functions in these families are structured the same. 29 | So, if you can work with one, then you can work with them all. 30 | That's why we're not going to cover them all with examples here. 31 | For a full list of these functions take a look at the [`{gt}` docs](https://gt.rstudio.com/reference/index.html). 32 | 33 | ## fmt\_\* functions {#sec-fmt-functions} 34 | 35 | First, we need some example data to practice on. 36 | Thankfully, `{gt}` already comes with data sets that use many different data formats. 37 | Let me introduce you to `{gt}`'s example tibble, or `exibble` for short. 38 | 39 | ```{r} 40 | #| message: false 41 | #| warning: false 42 | library(tidyverse) 43 | library(gt) 44 | exibble 45 | ``` 46 | 47 | Let's put this into a `{gt}` table. 48 | We're going to use one of the pre-defined themes that come with `opt_stylize()`. 49 | 50 | ```{r} 51 | exibble |> 52 | select(-(row:group)) |> 53 | gt() |> 54 | opt_stylize(style = 3) 55 | ``` 56 | 57 | Phew! 58 | This is table won't win awards any time soon. 59 | Let's clean it up by working us through the columns one by one. 60 | 61 | ### Numbers 62 | 63 | First, we're getting rid of the [scientific notation](https://en.wikipedia.org/wiki/Scientific_notation) in the `num` column. 64 | While we're at it, we're going to round the numbers to one decimal. 65 | 66 | ```{r} 67 | exibble |> 68 | select(num) |> 69 | gt() |> 70 | opt_stylize(style = 3) |> 71 | fmt_number( 72 | columns = 'num', 73 | decimals = 1 74 | ) 75 | ``` 76 | 77 | Next, we may want to adjust our marks `,` and `.` in the output. 78 | For example, in German we write one million as `1.000.000` and a quarter as `0,25`. 79 | Hence, we could change the `sep_mark` and `dec_mark` argument in `fmt_number()`. 80 | But the easier way is to just change the `locale` to `"de"` (German). 81 | 82 | ```{r} 83 | exibble |> 84 | select(num) |> 85 | gt() |> 86 | opt_stylize(style = 3) |> 87 | fmt_number( 88 | columns = 'num', 89 | decimals = 1, 90 | locale = 'de' 91 | ) 92 | ``` 93 | 94 | Since we also have some very large numbers in the `num` column, we could add suffixes instead of displaying a lot of zeroes. 95 | This means that we transform e.g. 1000 to 1K. 96 | 97 | ```{r} 98 | exibble |> 99 | select(num) |> 100 | gt() |> 101 | opt_stylize(style = 3) |> 102 | fmt_number( 103 | columns = 'num', 104 | decimals = 1, 105 | suffixing = TRUE 106 | ) 107 | ``` 108 | 109 | We could also use our own suffixes. 110 | 111 | ```{r} 112 | # Thousand - Million - Billion - Trillion 113 | custom_suffixes <- c("k", "mil", "bil", "tril") 114 | 115 | exibble |> 116 | select(num) |> 117 | gt() |> 118 | opt_stylize(style = 3) |> 119 | fmt_number( 120 | columns = 'num', 121 | decimals = 1, 122 | suffixing = custom_suffixes 123 | ) 124 | ``` 125 | 126 | ### Currency 127 | 128 | Now, let's format the `currency` column. 129 | The default `currency` is `USD`. 130 | That will give you \$ signs. 131 | 132 | ```{r} 133 | exibble |> 134 | select(num, currency) |> 135 | gt() |> 136 | opt_stylize(style = 3) |> 137 | fmt_number(columns = 'num', decimals = 1) |> 138 | fmt_currency(columns = 'currency') 139 | ``` 140 | 141 | Since I mostly use Euros in my real life, let me change the `currency` argument here. 142 | Also, we're going to set `locale` to German again. 143 | 144 | ```{r} 145 | exibble |> 146 | select(num, currency) |> 147 | gt() |> 148 | opt_stylize(style = 3) |> 149 | fmt_number(columns = 'num', decimals = 1) |> 150 | fmt_currency( 151 | columns = 'currency', currency = 'EUR', locale = 'de' 152 | ) 153 | ``` 154 | 155 | You'd think that this is the correct way to state a price in Germany. 156 | But it's not. 157 | Unfortunately, the locale did not catch that we use the Euro symbol at the end of a number. 158 | But no worries, we can fix that manually. 159 | 160 | Instead of `fmt_currency()`, we're going to use `fmt_number()` and apply the Euro symbol manually via `pattern`. 161 | The `fmt_*()` functions use `{x}` as placeholder for the function's regular output. 162 | That way, we can modify outputs as we see fit. 163 | Here are two examples. 164 | 165 | ::: panel-tabset 166 | #### Euro symbol (trailing) 167 | 168 | ```{r} 169 | exibble |> 170 | select(num, currency) |> 171 | gt() |> 172 | opt_stylize(style = 3) |> 173 | fmt_number(columns = 'num', decimals = 1) |> 174 | fmt_number( 175 | columns = 'currency', 176 | decimals = 2, 177 | locale = 'de', 178 | pattern = '{x}€' 179 | ) 180 | ``` 181 | 182 | #### Euro text (leading) 183 | 184 | ```{r} 185 | exibble |> 186 | select(num, currency) |> 187 | gt() |> 188 | opt_stylize(style = 3) |> 189 | fmt_number(columns = 'num', decimals = 1) |> 190 | fmt_number( 191 | columns = 'currency', 192 | decimals = 2, 193 | locale = 'de', 194 | pattern = 'EUR {x}' 195 | ) 196 | ``` 197 | ::: 198 | 199 | We have rounded the `num` column to one decimal with the first `fmt_number()` layer. 200 | It's interesting to find out what happens if we had targeted the `currency` column in that layer too. 201 | Would the next `fmt_number()` layer round the previously rounded number or the original number? 202 | Let's check. 203 | 204 | ::: panel-tabset 205 | #### Round both columns in first layer 206 | 207 | ```{r} 208 | #| code-fold: true 209 | exibble |> 210 | select(num, currency) |> 211 | gt() |> 212 | opt_stylize(style = 3) |> 213 | fmt_number(columns = c('num', 'currency'), decimals = 1) |> 214 | fmt_number( 215 | columns = 'currency', 216 | decimals = 2, 217 | locale = 'de', 218 | pattern = '{x}€' 219 | ) 220 | ``` 221 | 222 | #### Round only `num` in first layer 223 | 224 | ```{r} 225 | #| code-fold: true 226 | exibble |> 227 | select(num, currency) |> 228 | gt() |> 229 | opt_stylize(style = 3) |> 230 | fmt_number(columns = 'num', decimals = 1) |> 231 | fmt_number( 232 | columns = 'currency', 233 | decimals = 2, 234 | locale = 'de', 235 | pattern = '{x}€' 236 | ) 237 | ``` 238 | ::: 239 | 240 | As you can see, the output is the same. 241 | This means that the `fmt_*()` functions always use the original data. 242 | That's good to know. 243 | 244 | Fun fact: That's also what's happening when you rename a column with `cols_label()`. 245 | Internally, the column names always remain the same. 246 | 247 | ### Dates, times and datetimes 248 | 249 | We can format any date using `fmt_date()`. 250 | And there are quite a few `date_style`s we can choose from. 251 | Here, are a few examples. 252 | 253 | ::: panel-tabset 254 | #### wday_month_day_year 255 | 256 | ```{r} 257 | exibble |> 258 | select(num, currency, date) |> 259 | gt() |> 260 | opt_stylize(style = 3) |> 261 | fmt_number(columns = 'num', decimals = 1) |> 262 | fmt_number( 263 | columns = 'currency', 264 | decimals = 2, 265 | locale = 'de', 266 | pattern = '{x}€' 267 | ) |> 268 | fmt_date(columns = 'date', date_style = "wday_month_day_year") 269 | ``` 270 | 271 | #### day_m\_year 272 | 273 | ```{r} 274 | exibble |> 275 | select(num, currency, date) |> 276 | gt() |> 277 | opt_stylize(style = 3) |> 278 | fmt_number(columns = 'num', decimals = 1) |> 279 | fmt_number( 280 | columns = 'currency', 281 | decimals = 2, 282 | locale = 'de', 283 | pattern = '{x}€' 284 | ) |> 285 | fmt_date(columns = 'date', date_style = "day_m_year") 286 | ``` 287 | 288 | #### yMMMd 289 | 290 | ```{r} 291 | exibble |> 292 | select(num, currency, date) |> 293 | gt() |> 294 | opt_stylize(style = 3) |> 295 | fmt_number(columns = 'num', decimals = 1) |> 296 | fmt_number( 297 | columns = 'currency', 298 | decimals = 2, 299 | locale = 'de', 300 | pattern = '{x}€' 301 | ) |> 302 | fmt_date(columns = 'date', date_style = "yMMMd") 303 | ``` 304 | 305 | #### yMMMEd 306 | 307 | ```{r} 308 | exibble |> 309 | select(num, currency, date) |> 310 | gt() |> 311 | opt_stylize(style = 3) |> 312 | fmt_number(columns = 'num', decimals = 1) |> 313 | fmt_number( 314 | columns = 'currency', 315 | decimals = 2, 316 | locale = 'de', 317 | pattern = '{x}€' 318 | ) |> 319 | fmt_date(columns = 'date', date_style = "yMMMEd") 320 | ``` 321 | ::: 322 | 323 | To see the full list of available styles, you can run `info_date_style()`. 324 | 325 | ```{r} 326 | info_date_style() 327 | ``` 328 | 329 | Notice that some of these styles are labeled as flexible. 330 | This means that they will adjust to locales. 331 | Beware that month names may adapt to the locale but not the formatting. 332 | 333 | Here's an example of that with `day_m_year` (not flexible) and `yMMMd` (flexible) using the German locale. 334 | Notice how `day_m_year` does not set a `.` after the day but `yMMMd` does. 335 | The latter is the correct German formatting. 336 | 337 | ::: panel-tabset 338 | #### day_m\_year 339 | 340 | ```{r} 341 | #| code-fold: true 342 | exibble |> 343 | select(num, currency, date) |> 344 | gt() |> 345 | opt_stylize(style = 3) |> 346 | fmt_number(columns = 'num', decimals = 1) |> 347 | fmt_number( 348 | columns = 'currency', 349 | decimals = 2, 350 | locale = 'de', 351 | pattern = '{x}€' 352 | ) |> 353 | fmt_date( 354 | columns = 'date', 355 | locale = 'de', 356 | date_style = "day_m_year" 357 | ) 358 | ``` 359 | 360 | #### yMMMd 361 | 362 | ```{r} 363 | #| code-fold: true 364 | exibble |> 365 | select(num, currency, date) |> 366 | gt() |> 367 | opt_stylize(style = 3) |> 368 | fmt_number(columns = 'num', decimals = 1) |> 369 | fmt_number( 370 | columns = 'currency', 371 | decimals = 2, 372 | locale = 'de', 373 | pattern = '{x}€' 374 | ) |> 375 | fmt_date( 376 | columns = 'date', 377 | locale = 'de', 378 | date_style = "yMMMd" 379 | ) 380 | ``` 381 | ::: 382 | 383 | Formatting time works basically the same, so I'm just going to show one example.[^formatting-1] 384 | 385 | [^formatting-1]: You should probably know that there seems to be an issue with some of the time formats when you're also using `{renv}`. 386 | At least I've run into some troubles with that (see [Issue](https://github.com/rstudio/gt/issues/1124)). 387 | But if your desired format does not work, you can always format it manually. 388 | Either before sending the data to `gt()` or with `fmt()` which we'll cover in a sec. 389 | 390 | ```{r} 391 | exibble |> 392 | select(num, currency, date, time) |> 393 | gt() |> 394 | opt_stylize(style = 3) |> 395 | fmt_number(columns = 'num', decimals = 1) |> 396 | fmt_number( 397 | columns = 'currency', 398 | decimals = 2, 399 | locale = 'de', 400 | pattern = '{x}€' 401 | ) |> 402 | fmt_date(columns = 'date', locale = 'de', date_style = "yMMMd") |> 403 | fmt_time(columns = 'time', time_style = "Hms") 404 | ``` 405 | 406 | I have a date. 407 | I have a time. 408 | Uh! 409 | Datetime, cf. 410 | [PPAP](https://www.youtube.com/watch?v=Ct6BUPvE2sM)[^formatting-2]. 411 | 412 | [^formatting-2]: My brain randomly reminded me of some dumb internet stuff from 6 years ago. 413 | So naturally I had to incorporate it into my text somehow. 414 | And of course the YouTube algorithm had to remind me of more [fun stuff](https://www.youtube.com/watch?v=jofNR_WkoCE). 415 | 416 | Working with these magical columns is exactly what you'd expect. 417 | You use `fmt_datetime()` which has a `date_style` and a `time_style` argument. 418 | 419 | ```{r} 420 | exibble |> 421 | select(num, currency, date, time, datetime) |> 422 | gt() |> 423 | opt_stylize(style = 3) |> 424 | fmt_number(columns = 'num', decimals = 1) |> 425 | fmt_number( 426 | columns = 'currency', 427 | decimals = 2, 428 | locale = 'de', 429 | pattern = '{x}€' 430 | ) |> 431 | fmt_date(columns = 'date', locale = 'de', date_style = "yMMMd") |> 432 | fmt_time(columns = 'time', time_style = "Hms") |> 433 | fmt_datetime( 434 | columns = 'datetime', 435 | date_style = "yMMMd", 436 | time_style = "Hms" 437 | ) 438 | ``` 439 | 440 | ### Markdown 441 | 442 | We can also use Markdown and therefore HTML + CSS in our tables. 443 | Let's use that to make our table a bit colorful. 444 | For example, we could wrap elements from the `currency` column into ``-tags to colorize them. 445 | 446 | ```{r} 447 | exibble |> 448 | select(num, currency, date, time, datetime) |> 449 | mutate( 450 | currency = str_c( 451 | '', 452 | currency, 453 | '€' 454 | ) 455 | ) |> 456 | gt() |> 457 | opt_stylize(style = 3) |> 458 | fmt_number(columns = 'num', decimals = 1) |> 459 | fmt_date(columns = 'date', locale = 'de', date_style = "yMMMd") |> 460 | fmt_time(columns = 'time', time_style = "Hms") |> 461 | fmt_datetime( 462 | columns = 'datetime', 463 | date_style = "yMMMd", 464 | time_style = "Hms" 465 | ) |> 466 | fmt_markdown(columns = 'currency') 467 | ``` 468 | 469 | This is one way you could style your table. 470 | But I've used this way only for demo purposes. 471 | We'll learn more about styling in [@sec-styling]. 472 | 473 | The real power of the `fmt_markdown()` layer is that you can put any html into the table and it will be formatted correctly afterwards. 474 | For example, I've copied the [svg](https://en.wikipedia.org/wiki/Scalable_Vector_Graphics)-code (which can be used in HTML) for the R logo from [Wikipedia](https://commons.wikimedia.org/wiki/File:R_logo.svg). 475 | Putting this code a `{gt}` table and using `fmt_markdown()`, let's me use the R logo. 476 | 477 | ```{r} 478 | ## factor to apply to original width and height of svg from Wikipedia 479 | scale_size <- 0.5 480 | 481 | r_logo_svg <- glue::glue(' 482 | 483 | 484 | 485 | 486 | 487 | 488 | 489 | 490 | 491 | 492 | 493 | 494 | 495 | 496 | ') 497 | 498 | tibble(logo = r_logo_svg) |> 499 | gt() |> 500 | fmt_markdown(columns = 'logo') 501 | ``` 502 | 503 | This `fmt_markdown()` technique is super powerful. 504 | We could even use it to nest `{gt}`-tables (which are HTML) inside of each other. 505 | That's what we'll do in [@sec-case-studies] to create elaborate tables. 506 | 507 | ### Any data format 508 | 509 | There are some more `fmt_*()` functions for specific formats. 510 | Once again, you can look at them in the [docs](s://gt.rstudio.com/reference/index.html). 511 | Instead of showing them all, let me finish off this section with the most powerful function of them all. 512 | That's `fmt()`. 513 | 514 | You can just apply any function that you like for formatting. 515 | For example, you could convert text entries to all-caps with `str_to_upper()`. 516 | 517 | ```{r} 518 | exibble |> 519 | select(num, currency, date, time, datetime, char) |> 520 | gt() |> 521 | opt_stylize(style = 3) |> 522 | fmt_number(columns = 'num', decimals = 1) |> 523 | fmt_number( 524 | columns = 'currency', 525 | decimals = 2, 526 | locale = 'de', 527 | pattern = '{x}€' 528 | ) |> 529 | fmt_date(columns = 'date', locale = 'de', date_style = "yMMMd") |> 530 | fmt_time(columns = 'time', time_style = "Hms") |> 531 | fmt_datetime( 532 | columns = 'datetime', 533 | date_style = "yMMMd", 534 | time_style = "Hms" 535 | ) |> 536 | fmt(columns = 'char', fn = str_to_upper) 537 | ``` 538 | 539 | Or you could write your own time-formatting function. 540 | 541 | ```{r} 542 | on_time_format <- function(time, target) { 543 | if_else(parse_time(time) <= target, 'on time', 'too late') 544 | } 545 | 546 | exibble |> 547 | select(time) |> 548 | mutate(rep_time = time) |> 549 | gt() |> 550 | opt_stylize(style = 3) |> 551 | fmt( 552 | columns = 'rep_time', 553 | fns = function(x) { 554 | on_time_format(x, hms::hms(hours = 16, minutes = 30)) 555 | } 556 | ) 557 | ``` 558 | 559 | ## sub\_ functions {#sec-sub-functions} 560 | 561 | The `sub_*()` functions are straightforward to use. 562 | There are five functions that you can use. 563 | 564 | - `sub_missing()` replaces `NA` values 565 | - `sub_zero()` replaces zeroes 566 | - `sub_large_values()` replaces large values (according to some threshold) 567 | - `sub_small_values()` does... I think you can guess it 568 | - `sub_values()` can replace large numbers or texts that match a [regex](https://r4ds.hadley.nz/regexps.html) 569 | 570 | The first two are straight-forward to use. 571 | By default, they apply to the whole data. 572 | But you can also target only specific columns and rows by changing the `columns` and `rows` argument. 573 | 574 | ::: panel-tabset 575 | ### Replace `NA` 576 | 577 | ```{r} 578 | exibble |> 579 | select(num, currency, date, time, datetime) |> 580 | gt() |> 581 | opt_stylize(style = 3) |> 582 | fmt_number(columns = 'num', decimals = 1) |> 583 | fmt_number( 584 | columns = 'currency', 585 | decimals = 2, 586 | locale = 'de', 587 | pattern = '{x}€' 588 | ) |> 589 | fmt_date(columns = 'date', locale = 'de', date_style = "yMMMd") |> 590 | fmt_time(columns = 'time', time_style = "Hms") |> 591 | fmt_datetime( 592 | columns = 'datetime', 593 | date_style = "yMMMd", 594 | time_style = "Hms" 595 | ) |> 596 | sub_missing(missing_text = '----------') 597 | ``` 598 | 599 | ### Replace zeros 600 | 601 | ```{r} 602 | tibble(demo_column = -3:3) |> 603 | gt() |> 604 | opt_stylize(style = 3) |> 605 | sub_zero(zero_text = 'ZERO, WATCH OUT WHOOP WHOOP') 606 | ``` 607 | ::: 608 | 609 | With `sub_small_vals()` and `sub_large_vals()` you have to be a bit careful about the sign of the number you're replacing. 610 | Both functions will replace only positive or negative numbers. 611 | So, if you want to replace positive **and** negative numbers, you have to use the layers multiple times. 612 | 613 | ::: panel-tabset 614 | ### Replace positives 615 | 616 | ```{r} 617 | tibble(x = c(-100, 100, 0.01, -0.01), demo_col = x) |> 618 | gt() |> 619 | opt_stylize(style = 3) |> 620 | fmt_number(columns = where(is.numeric)) |> 621 | sub_small_vals( 622 | columns = 'demo_col', threshold = 1, sign = '+' 623 | ) |> 624 | sub_large_vals( 625 | columns = 'demo_col', threshold = 50, sign = '+' 626 | ) 627 | ``` 628 | 629 | ### Replace negatives 630 | 631 | ```{r} 632 | tibble(x = c(-100, 100, 0.01, -0.01), demo_col = x) |> 633 | gt() |> 634 | opt_stylize(style = 3) |> 635 | fmt_number(columns = where(is.numeric)) |> 636 | sub_small_vals( 637 | columns = 'demo_col', threshold = 1, sign = '-' 638 | ) |> 639 | sub_large_vals( 640 | columns = 'demo_col', threshold = 50, sign = '-' 641 | ) 642 | ``` 643 | 644 | ### Replace both 645 | 646 | ```{r} 647 | tibble(x = c(-100, 100, 0.01, -0.01), demo_col = x) |> 648 | gt() |> 649 | opt_stylize(style = 3) |> 650 | fmt_number(columns = where(is.numeric)) |> 651 | sub_small_vals( 652 | columns = 'demo_col', threshold = 1, sign = '+' 653 | ) |> 654 | sub_large_vals( 655 | columns = 'demo_col', threshold = 50, sign = '+' 656 | ) |> 657 | sub_small_vals( 658 | columns = 'demo_col', threshold = 1, sign = '-' 659 | ) |> 660 | sub_large_vals( 661 | columns = 'demo_col', threshold = 50, sign = '-' 662 | ) 663 | ``` 664 | ::: 665 | 666 | The last `sub_*()` function is `sub_values()`. 667 | It is the most powerful function of the `sub_*()` family because it can replace numbers and texts. 668 | To do that it has a `values` and `pattern` argument. 669 | In case you're wondering, you can only use one of them at a time. 670 | If you specify both, `pattern` will always take precedence. 671 | 672 | But there's more. 673 | It also has an `fn` argument. 674 | You could use it to let an arbitrary function decide which values get replaced. 675 | In order for this to work, this function must take a column and return a `TRUE`/`FALSE` vector of the same length. 676 | 677 | Let's take a look at a couple of examples. 678 | 679 | ::: panel-tabset 680 | ### Replace by values 681 | 682 | ```{r} 683 | exibble |> 684 | select(num) |> 685 | mutate(demo_col = num) |> 686 | gt() |> 687 | opt_stylize(style = 3) |> 688 | fmt_number(columns = everything()) |> 689 | sub_values( 690 | columns = 'demo_col', 691 | values = c(0.111, 777000), 692 | replacement = 'REPLACED' 693 | ) 694 | ``` 695 | 696 | ### Replace by pattern 697 | 698 | ```{r} 699 | exibble |> 700 | select(char) |> 701 | mutate(demo_col = char) |> 702 | gt() |> 703 | opt_stylize(style = 3) |> 704 | sub_values( 705 | columns = 'demo_col', 706 | pattern = '(a|e)', 707 | replacement = 'fruit contains an a or e' 708 | ) 709 | ``` 710 | 711 | ### Replace by function 712 | 713 | ```{r} 714 | exibble |> 715 | select(num) |> 716 | mutate(demo_col = num) |> 717 | gt() |> 718 | opt_stylize(style = 3) |> 719 | fmt_number(columns = everything()) |> 720 | sub_values( 721 | columns = 'demo_col', 722 | fn = function(x) between(x, 10, 10000), 723 | replacement = 'Between 10 and 10000' 724 | ) 725 | ``` 726 | ::: 727 | 728 | ## Summary 729 | 730 | That's a wrap on [@sec-formatting]. 731 | We've got the formatting options covered. 732 | Time to get to the most complicated part of our tables: Their theme. 733 | 734 | Just like in a ggplot we can style more or less every part of our table. 735 | And if you're familiar with HTML/CSS you can even apply custom styles that have not been implemented in `{gt}` yet. 736 | -------------------------------------------------------------------------------- /formula.svg: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /formula.txt: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /formula2.svg: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /getting_started.qmd: -------------------------------------------------------------------------------- 1 | # Getting started {#sec-getting-started} 2 | 3 | ```{r} 4 | #| echo: false 5 | library(knitr) 6 | knit_print.gt <- function(x, ...) { 7 | # Two steps to avoid most Quarto changes of my table styles: 8 | # 1. as_raw_html() to use table styles *inline* 9 | # 2. wrap output in a div that resets all Quarto styles 10 | stringr::str_c( 11 | "
\n", 12 | gt::as_raw_html(x), 13 | "\n
" 14 | ) |> 15 | knitr::asis_output() 16 | } 17 | registerS3method( 18 | "knit_print", 'gt_tbl', knit_print.gt, 19 | envir = asNamespace("gt") 20 | # important to overwrite {gt}s knit_print 21 | ) 22 | ``` 23 | 24 | In this chapter, we're going to do two things: 25 | 26 | 1. Learn simple guidelines for better tables 27 | 2. Implement them with `{gt}` 28 | 29 | And of course we will need data for that. 30 | I like penguins, so we're going to use the fabulous `penguins` data set from `{palmerpenguins}`. 31 | 32 | ```{r} 33 | #| message: false 34 | #| warning: false 35 | library(tidyverse) 36 | penguins <- palmerpenguins::penguins |> 37 | filter(!is.na(sex)) 38 | penguins 39 | ``` 40 | 41 | Using this data, let us count the penguins. 42 | These counts will serve as a simple data set to practice table building. 43 | 44 | ```{r} 45 | penguin_counts <- penguins |> 46 | mutate(year = as.character(year)) |> 47 | group_by(species, island, sex, year) |> 48 | summarise(n = n(), .groups = 'drop') 49 | penguin_counts 50 | ``` 51 | 52 | In an actual table, the data would probably be rearranged a bit. 53 | There's nothing wrong with this long (i.e. many rows) data format. 54 | In fact, this format is great for data analysis. 55 | But in a table that is meant to be read by humans, not machines, you'll probably go with a wider format. 56 | 57 | ```{r} 58 | penguin_counts_wider <- penguin_counts |> 59 | pivot_wider( 60 | names_from = c(species, sex), 61 | values_from = n 62 | ) |> 63 | # Make missing numbers (NAs) into zero 64 | mutate(across(.cols = -(1:2), .fns = ~replace_na(., replace = 0))) |> 65 | arrange(island, year) 66 | penguin_counts_wider 67 | ``` 68 | 69 | Now, let's put this into a table. 70 | Not too long ago I would have probably visualized the data with a table like this: 71 | 72 | ![Ugh. A not so sexy table of our `penguins_counts_wider` data set created by yours truly with LibreOffice Calc (spreadsheet software - double ugh. Though, compared to Excel it's open-source. So maybe 1.5 ugh?)](img/stupid_table_screenshot.png){#fig-terrible-tbl fig-align="center" width="90%"} 73 | 74 | Ugh. 75 | This is not a sexy table. 76 | I get bored just looking at that. 77 | So let's improve this table. 78 | To do so, here are the 6 guidelines that will, well, guide us. 79 | 80 | 1. Avoid vertical lines 81 | 2. Use better column names 82 | 3. Align columns 83 | 4. Use groups instead of repetitive columns 84 | 5. Remove missing numbers 85 | 6. Add summaries 86 | 87 | ## Avoid vertical lines 88 | 89 | This is the guideline that gives you the biggest bang for your buck. 90 | The above table uses waaaay to many grid lines. 91 | Without vertical lines, the table will look less cramped. 92 | 93 | Thankfully, `{gt}` seems to live by this rule as it is implemented by default. 94 | Thus, we only need to pass our data set `penguin_counts_wider` to `gt()`. 95 | You can think of this function as the `ggplot()` analogue: 96 | It's the starting point of any table in the `{gt}` universe. 97 | 98 | ```{r} 99 | library(gt) 100 | penguin_counts_wider |> 101 | gt() 102 | ``` 103 | 104 | This isn't a great table yet but it's a start. 105 | In any case, it feels more open due to less grid lines. 106 | Of course, the column labels could be better which brings us to our next point. 107 | 108 | ## Use better column names 109 | 110 | To change the column names use the "layer" called `cols_layer()`. 111 | Much like `{ggplot2}`, `{gt}` works with layers. 112 | To change anything about the table, we just pass the table from layer to the next. 113 | This works with piping. 114 | Armed with that knowledge, we could label the columns like we did in [@fig-terrible-tbl]. 115 | 116 | ```{r} 117 | penguin_counts_wider |> 118 | gt() |> 119 | cols_label( 120 | island = 'Island', 121 | year = 'Year', 122 | Adelie_female = 'Adelie (female)', 123 | Adelie_male = 'Adelie (male)', 124 | Chinstrap_female = 'Chinstrap (female)', 125 | Chinstrap_male = 'Chinstrap (male)', 126 | Gentoo_female = 'Gentoo (female)', 127 | Gentoo_male = 'Gentoo (male)', 128 | ) 129 | ``` 130 | 131 | But this isn't a great way to label the columns. 132 | So let's do something else instead. 133 | First, let us create so-called **spanners**. 134 | These are joined columns and can be created with `tab_spanner()` layers. 135 | You'll need one layer for each spanner. 136 | 137 | ```{r} 138 | penguin_counts_wider |> 139 | gt() |> 140 | cols_label( 141 | island = 'Island', 142 | year = 'Year', 143 | Adelie_female = 'Adelie (female)', 144 | Adelie_male = 'Adelie (male)', 145 | Chinstrap_female = 'Chinstrap (female)', 146 | Chinstrap_male = 'Chinstrap (male)', 147 | Gentoo_female = 'Gentoo (female)', 148 | Gentoo_male = 'Gentoo (male)', 149 | ) |> 150 | tab_spanner( 151 | label = md('**Adelie**'), 152 | columns = 3:4 153 | ) |> 154 | tab_spanner( 155 | label = md('**Chinstrap**'), 156 | columns = c('Chinstrap_female', 'Chinstrap_male') 157 | ) |> 158 | tab_spanner( 159 | label = md('**Gentoo**'), 160 | columns = contains('Gentoo') 161 | ) 162 | ``` 163 | 164 | As you can see, `tab_spanner()` always requires two arguments `label` and `columns`. 165 | For the `columns` argument I have shown you three ways to get the job done: 166 | 167 | 1. Vector of column numbers 168 | 2. Vector of column names 169 | 3. [tidyselect helpers](https://tidyselect.r-lib.org/reference/language.html) 170 | 171 | For the `label` argument you can either just state a `character` vector or you can wrap one in `md()` to enable Markdown syntax (like `**bold text**`). 172 | 173 | Okay, now we don't really need the Species labels in the actual column names anymore. 174 | The spanners already state that for us. 175 | So, let us modify our previous code to rename the columns. 176 | To do so, let me show you a cool trick that may save you some tedious typing. 177 | 178 | First, we create a **named** vector that contains the actual and the desired column names. 179 | 180 | ```{r} 181 | actual_colnames <- colnames(penguin_counts_wider) 182 | actual_colnames 183 | desired_colnames <- actual_colnames |> 184 | str_remove('(Adelie|Gentoo|Chinstrap)_') |> 185 | str_to_title() 186 | 187 | names(desired_colnames) <- actual_colnames 188 | desired_colnames 189 | ``` 190 | 191 | Then, we can use this named vector as the `.list` argument in `cols_label()`. 192 | 193 | ```{r} 194 | penguin_counts_wider |> 195 | gt() |> 196 | cols_label(.list = desired_colnames) |> 197 | tab_spanner( 198 | label = md('**Adelie**'), 199 | columns = 3:4 200 | ) |> 201 | tab_spanner( 202 | label = md('**Chinstrap**'), 203 | columns = c('Chinstrap_female', 'Chinstrap_male') 204 | ) |> 205 | tab_spanner( 206 | label = md('**Gentoo**'), 207 | columns = contains('Gentoo') 208 | ) 209 | ``` 210 | 211 | Finally, while we're currently changing labels, let us add one important label - the title. 212 | The `tab_header()` layer does the trick. 213 | 214 | ```{r} 215 | penguin_counts_wider |> 216 | gt() |> 217 | cols_label(.list = desired_colnames) |> 218 | tab_spanner( 219 | label = md('**Adelie**'), 220 | columns = 3:4 221 | ) |> 222 | tab_spanner( 223 | label = md('**Chinstrap**'), 224 | columns = c('Chinstrap_female', 'Chinstrap_male') 225 | ) |> 226 | tab_spanner( 227 | label = md('**Gentoo**'), 228 | columns = contains('Gentoo') 229 | ) |> 230 | tab_header( 231 | title = 'Penguins in the Palmer Archipelago', 232 | subtitle = 'Data is courtesy of the {palmerpenguins} R package' 233 | ) 234 | ``` 235 | 236 | By the same trick we could also add a caption for a Quarto document (`tab_caption()`), a footnote (`tab_footnote()`) or another source note (`tab_sourcenote()`), 237 | In this case it's a bit much, though. 238 | So I won't add them. 239 | Just know that these functions exist in case you need them. 240 | For now, let us talk about our next guideline. 241 | 242 | Before we can do that, let me mention one small thing: Our spanners and headers will not change as we move along this tutorial. 243 | To avoid repeating them all the time, let me wrap them in a function. 244 | 245 | ```{r} 246 | spanners_and_header <- function(gt_tbl) { 247 | gt_tbl |> 248 | tab_spanner( 249 | label = md('**Adelie**'), 250 | columns = 3:4 251 | ) |> 252 | tab_spanner( 253 | label = md('**Chinstrap**'), 254 | columns = c('Chinstrap_female', 'Chinstrap_male') 255 | ) |> 256 | tab_spanner( 257 | label = md('**Gentoo**'), 258 | columns = contains('Gentoo') 259 | ) |> 260 | tab_header( 261 | title = 'Penguins in the Palmer Archipelago', 262 | subtitle = 'Data is courtesy of the {palmerpenguins} R package' 263 | ) 264 | } 265 | 266 | # This produces the same output 267 | penguin_counts_wider |> 268 | gt() |> 269 | cols_label(.list = desired_colnames) |> 270 | spanners_and_header() 271 | ``` 272 | 273 | ## Align columns 274 | 275 | Did you notice that `gt()` aligned the columns differently? 276 | That's because the columns of the corresponding `data.frame`/`tibble` contained different data types. 277 | Specifically: 278 | 279 | - the counts are `integers` and aligned to the right 280 | 281 | - the `year` column is a `character` vector and uses alignment to the left (though it's not totally visible because the column is narrow) 282 | 283 | - the `island` column is a `factor` and uses center alignment (even though its entries are `character`s) 284 | 285 | It's a good default to align numbers to the right and texts to the left. 286 | Why? 287 | Because it's more readable. 288 | Need an example? 289 | Here's one. 290 | Most (western) people will probably say that the left column is the easiest to read because we read from left to right. 291 | 292 | ```{r} 293 | #| echo: false 294 | #| message: false 295 | 296 | size <- 5 297 | read_csv2('data/ratios.csv') |> 298 | mutate(location = str_remove(location, ' Location')) |> 299 | ggplot() + 300 | geom_text( 301 | aes(x = 0, y = seq_along(location), label = location), 302 | hjust = 0, 303 | size = size, 304 | color = 'grey20' 305 | ) + 306 | geom_text( 307 | aes(x = 3, y = seq_along(location), label = location), 308 | hjust = 0.5, 309 | size = size, 310 | color = 'grey20' 311 | ) + 312 | geom_text( 313 | aes(x = 6, y = seq_along(location), label = location), 314 | hjust = 1, 315 | size = size, 316 | color = 'grey20' 317 | ) + 318 | coord_cartesian(xlim = c(0, 6)) + 319 | theme_void() 320 | ``` 321 | 322 | For numbers it's the other way around. 323 | That's because right-aligned numbers make it easy to see how many digits a number has compared to other numbers. 324 | This assumes that your numbers use a font that assigns equal width to all digits (monospace fonts). 325 | 326 | So, let us align the `island` and `year` column. 327 | We can either do this by transforming the data types before even calling `gt()`. 328 | Or we use the `cols_align()` layer. 329 | Once again, this layer understands text locations and tidyselection helpers. 330 | 331 | ::: panel-tabset 332 | ## Conversion 333 | 334 | ```{r} 335 | penguin_counts_wider |> 336 | mutate( 337 | island = as.character(island), 338 | year = as.numeric(year) 339 | ) |> 340 | gt() |> 341 | cols_label(.list = desired_colnames) |> 342 | spanners_and_header() 343 | ``` 344 | 345 | ## Align 346 | 347 | ```{r} 348 | penguin_counts_wider |> 349 | gt() |> 350 | cols_label(.list = desired_colnames) |> 351 | spanners_and_header() |> 352 | cols_align(align = 'right', columns = 'year') |> 353 | cols_align( 354 | align = 'left', 355 | columns = where(is.factor) 356 | ) 357 | ``` 358 | ::: 359 | 360 | ## Use groups instead of repetitive columns 361 | 362 | The `island` column is somewhat repetitive. 363 | In cases like these, I'd rather remove the column. 364 | Instead, I would group the table using additional rows. 365 | I like to think that this comes with better readability. 366 | 367 | With `{gt}`, this grouping is easy. 368 | We only need to specify the `groupname_col` argument in `gt()`. 369 | If we want, we can also set the `rowname_col` argument to `year`. 370 | This will format the "Year" column a bit differently. 371 | 372 | ::: panel-tabset 373 | ## Groups 374 | 375 | ```{r} 376 | penguin_counts_wider |> 377 | mutate( 378 | island = as.character(island), 379 | year = as.numeric(year) 380 | ) |> 381 | gt(groupname_col = 'island') |> 382 | cols_label(.list = desired_colnames) |> 383 | spanners_and_header() 384 | ``` 385 | 386 | ## Groups + row names 387 | 388 | ```{r} 389 | penguin_counts_wider |> 390 | mutate( 391 | island = as.character(island), 392 | year = as.numeric(year) 393 | ) |> 394 | gt(groupname_col = 'island', rowname_col = 'year') |> 395 | cols_label(.list = desired_colnames) |> 396 | spanners_and_header() 397 | ``` 398 | ::: 399 | 400 | In this case, I prefer the latter style because we don't really need a "Year" label to identify 2007, 2008 and 2009 as years. 401 | But an island label could be nice (I'm really bad with geography). 402 | The easiest way to add that to the group names is via string manipulation before `gt()` is called. 403 | 404 | ```{r} 405 | penguin_counts_wider |> 406 | mutate( 407 | island = as.character(island), 408 | year = as.numeric(year), 409 | island = paste0('Island: ', island) 410 | ) |> 411 | gt(groupname_col = 'island', rowname_col = 'year') |> 412 | cols_label(.list = desired_colnames) |> 413 | spanners_and_header() 414 | ``` 415 | 416 | ## Remove missing numbers 417 | 418 | Notice that our table has a lot of zeroes in it. 419 | For better readability, let us replace the zeroes with something more lightweight. 420 | We accomplish this with the `sub_zero()` layer. 421 | 422 | ```{r} 423 | penguin_counts_wider |> 424 | mutate( 425 | island = as.character(island), 426 | year = as.numeric(year), 427 | island = paste0('Island: ', island) 428 | ) |> 429 | gt(groupname_col = 'island', rowname_col = 'year') |> 430 | cols_label(.list = desired_colnames) |> 431 | spanners_and_header() |> 432 | sub_zero(zero_text = '-') 433 | ``` 434 | 435 | There are more `sub_*()` functions in `{gt}`. 436 | We will learn about them in [@sec-sub-functions]. 437 | 438 | ## Add summaries 439 | 440 | Now, this table looks already cleaner than what we started with. 441 | In this format, we could even add **more information** at little cost. 442 | 443 | For example, we could add a summary for each group. 444 | In this case, a summary could be as simple as a total or maximum over all years (we'll just assume that this makes sense for our penguin data). 445 | 446 | Here, the key layer is `summary_rows()`. 447 | Let's have a look at what it can produce and then I'll explain. 448 | 449 | ```{r} 450 | penguin_counts_wider |> 451 | mutate( 452 | island = as.character(island), 453 | year = as.numeric(year), 454 | island = paste0('Island: ', island) 455 | ) |> 456 | gt(groupname_col = 'island', rowname_col = 'year') |> 457 | cols_label(.list = desired_colnames) |> 458 | spanners_and_header() |> 459 | sub_zero(zero_text = '-') |> 460 | summary_rows( 461 | groups = TRUE, 462 | fns = list( 463 | 'Maximum' = ~max(.), 464 | 'Total' = ~sum(.) 465 | ), 466 | formatter = fmt_number, 467 | decimals = 0 468 | ) 469 | ``` 470 | 471 | The `summary_rows()` function works with a named list of functions (one function for each summary). 472 | As you've seen, you can create one using `list('Name' = ~fct(.))`. 473 | In this case, `.` represents the column data. 474 | All other arguments can be named as usual. 475 | For example, you could do something like `~mean(., na.rm = TRUE)`.[^getting_started-1] 476 | 477 | [^getting_started-1]: Currently, this is the only possible way to define functions in `summary_rows()`. 478 | The documentation of this function says something different but this is a [known issue](https://github.com/rstudio/gt/issues/921). 479 | 480 | Notice that I had to set `groups = TRUE`. 481 | Otherwise, we would get summary rows at the end of the table (using all data). 482 | This is also known as a "grand summary". 483 | 484 | Further, the output of the summary function had to be formatted. 485 | By default, the output would contain two decimals. 486 | So, we'd get numbers like `9.00`. 487 | Here, `fmt_number()` is the formatter that corrected that. 488 | But we had to tell it to use `decimals = 0`. 489 | We'll learn more about the `fmt_*()` family in [@sec-fmt-functions]. 490 | 491 | Now that we've added more information to the table, it became quite long. 492 | We can amend that by reducing the row heights. 493 | Frankly, they have been too large for my taste for some time now. 494 | 495 | To do so, we could set the so-called `data_row.padding` to 2 pixels. 496 | This is done with `tab_options()`, the premier layer to style the table[^getting_started-2]. 497 | Similarly, there are padding options for `summary_row` and `row_group`[^getting_started-3]. And while we're at it, why not apply a pre-defined theme to our table with `opt_stylize()`? 498 | 499 | [^getting_started-2]: Basically, this is the analogue of `theme()` in `{ggplot2}`. 500 | 501 | [^getting_started-3]: I'm not sure why it's not `group_row` but we'll just go with it. 502 | ![](img/be-the-leaf-dance.gif) 503 | 504 | ```{r} 505 | penguin_counts_wider |> 506 | mutate( 507 | island = as.character(island), 508 | year = as.numeric(year), 509 | island = paste0('Island: ', island) 510 | ) |> 511 | gt(groupname_col = 'island', rowname_col = 'year') |> 512 | cols_label(.list = desired_colnames) |> 513 | spanners_and_header() |> 514 | sub_zero(zero_text = '-') |> 515 | summary_rows( 516 | groups = TRUE, 517 | fns = list( 518 | 'Maximum' = ~max(.), 519 | 'Total' = ~sum(.) 520 | ), 521 | formatter = fmt_number, 522 | decimals = 0 523 | ) |> 524 | tab_options( 525 | data_row.padding = px(2), 526 | summary_row.padding = px(3), # A bit more padding for summaries 527 | row_group.padding = px(4) # And even more for our groups 528 | ) |> 529 | opt_stylize(style = 6, color = 'gray') 530 | ``` 531 | 532 | This has been a little foretaste of styling a table. 533 | We'll learn more about changing a table's theme in [@sec-theming]. 534 | 535 | Finally, let me address the big inconsistency in the room. 536 | We have replaced the zeroes by `-` earlier. 537 | However, the summary rows still display `0`. 538 | Unfortunately, there is no `sub_zero()` function that targets the summary rows. 539 | So, we'll do something else instead. 540 | 541 | In our data set we have replaced all `NA`s with zero. 542 | But we didn't have to do that. 543 | We could just let them be `NA`s and use `sub_missing()` to replace them. 544 | In `summary_rows()`, we could then use `missing_text = "-"`. 545 | I think you get the idea, so I'm just going to fold the code (so you can focus on the result). 546 | 547 | ```{r} 548 | #| code-fold: true 549 | penguin_counts_wider |> 550 | mutate(across(.cols = -(1:2), ~if_else(. == 0, NA_integer_, .))) |> 551 | mutate( 552 | island = as.character(island), 553 | year = as.numeric(year), 554 | island = paste0('Island: ', island) 555 | ) |> 556 | gt(groupname_col = 'island', rowname_col = 'year') |> 557 | cols_label(.list = desired_colnames) |> 558 | spanners_and_header() |> 559 | sub_missing(missing_text = '-') |> 560 | summary_rows( 561 | groups = TRUE, 562 | fns = list( 563 | 'Maximum' = ~max(.), 564 | 'Total' = ~sum(.) 565 | ), 566 | formatter = fmt_number, 567 | decimals = 0, 568 | missing_text = '-' 569 | ) |> 570 | tab_options( 571 | data_row.padding = px(2), 572 | summary_row.padding = px(3), # A bit more padding for summaries 573 | row_group.padding = px(4) # And even more for our groups 574 | ) |> 575 | opt_stylize(style = 6, color = 'gray') 576 | ``` 577 | 578 | ## Summary 579 | 580 | We've started this chapter with a terrible table that needed improvement. 581 | Over the course of this chapter, we learned and applied six guidelines with `{gt}`. 582 | These guidelines were 583 | 584 | 1. Avoid vertical lines 585 | 2. Use better column names 586 | 3. Align columns 587 | 4. Use groups instead of repetitive columns 588 | 5. Remove missing numbers 589 | 6. Add summaries 590 | 591 | In the table business, these guidelines are pretty basic. 592 | I don't mean basic in a bad or boring way. 593 | It's just that these are solid recommendations that improve tables without any fancy stuff. 594 | No icons, no pictures, no other eye-catching elements. 595 | Just plain data formatted carefully. 596 | 597 | So now we've learned the basics. 598 | No need to stop there. 599 | Let's learn the fancy stuff too. 600 | That's what we'll do in the next chapter. 601 | -------------------------------------------------------------------------------- /gt_book.Rproj: -------------------------------------------------------------------------------- 1 | Version: 1.0 2 | 3 | RestoreWorkspace: Default 4 | SaveWorkspace: Default 5 | AlwaysSaveHistory: Default 6 | 7 | EnableCodeIndexing: Yes 8 | UseSpacesForTab: Yes 9 | NumSpacesForTab: 2 10 | Encoding: UTF-8 11 | 12 | RnwWeave: Sweave 13 | LaTeX: pdfLaTeX 14 | -------------------------------------------------------------------------------- /img/1960_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlbertRapp/gt_book/7bec14ad39260f4f6f54d4da582308fbb0f69b50/img/1960_1.png -------------------------------------------------------------------------------- /img/1960_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlbertRapp/gt_book/7bec14ad39260f4f6f54d4da582308fbb0f69b50/img/1960_2.png -------------------------------------------------------------------------------- /img/1960_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlbertRapp/gt_book/7bec14ad39260f4f6f54d4da582308fbb0f69b50/img/1960_3.png -------------------------------------------------------------------------------- /img/1960_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlbertRapp/gt_book/7bec14ad39260f4f6f54d4da582308fbb0f69b50/img/1960_4.png -------------------------------------------------------------------------------- /img/1960_5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlbertRapp/gt_book/7bec14ad39260f4f6f54d4da582308fbb0f69b50/img/1960_5.png -------------------------------------------------------------------------------- /img/1970_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlbertRapp/gt_book/7bec14ad39260f4f6f54d4da582308fbb0f69b50/img/1970_1.png -------------------------------------------------------------------------------- /img/1970_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlbertRapp/gt_book/7bec14ad39260f4f6f54d4da582308fbb0f69b50/img/1970_2.png -------------------------------------------------------------------------------- /img/1970_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlbertRapp/gt_book/7bec14ad39260f4f6f54d4da582308fbb0f69b50/img/1970_3.png -------------------------------------------------------------------------------- /img/1970_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlbertRapp/gt_book/7bec14ad39260f4f6f54d4da582308fbb0f69b50/img/1970_4.png -------------------------------------------------------------------------------- /img/1970_5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlbertRapp/gt_book/7bec14ad39260f4f6f54d4da582308fbb0f69b50/img/1970_5.png -------------------------------------------------------------------------------- /img/1980_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlbertRapp/gt_book/7bec14ad39260f4f6f54d4da582308fbb0f69b50/img/1980_1.png -------------------------------------------------------------------------------- /img/1980_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlbertRapp/gt_book/7bec14ad39260f4f6f54d4da582308fbb0f69b50/img/1980_2.png -------------------------------------------------------------------------------- /img/1980_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlbertRapp/gt_book/7bec14ad39260f4f6f54d4da582308fbb0f69b50/img/1980_3.png -------------------------------------------------------------------------------- /img/1980_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlbertRapp/gt_book/7bec14ad39260f4f6f54d4da582308fbb0f69b50/img/1980_4.png -------------------------------------------------------------------------------- /img/1980_5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlbertRapp/gt_book/7bec14ad39260f4f6f54d4da582308fbb0f69b50/img/1980_5.png -------------------------------------------------------------------------------- /img/1990_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlbertRapp/gt_book/7bec14ad39260f4f6f54d4da582308fbb0f69b50/img/1990_1.png -------------------------------------------------------------------------------- /img/1990_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlbertRapp/gt_book/7bec14ad39260f4f6f54d4da582308fbb0f69b50/img/1990_2.png -------------------------------------------------------------------------------- /img/1990_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlbertRapp/gt_book/7bec14ad39260f4f6f54d4da582308fbb0f69b50/img/1990_3.png -------------------------------------------------------------------------------- /img/1990_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlbertRapp/gt_book/7bec14ad39260f4f6f54d4da582308fbb0f69b50/img/1990_4.png -------------------------------------------------------------------------------- /img/1990_5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlbertRapp/gt_book/7bec14ad39260f4f6f54d4da582308fbb0f69b50/img/1990_5.png -------------------------------------------------------------------------------- /img/2000_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlbertRapp/gt_book/7bec14ad39260f4f6f54d4da582308fbb0f69b50/img/2000_1.png -------------------------------------------------------------------------------- /img/2000_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlbertRapp/gt_book/7bec14ad39260f4f6f54d4da582308fbb0f69b50/img/2000_2.png -------------------------------------------------------------------------------- /img/2000_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlbertRapp/gt_book/7bec14ad39260f4f6f54d4da582308fbb0f69b50/img/2000_3.png -------------------------------------------------------------------------------- /img/2000_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlbertRapp/gt_book/7bec14ad39260f4f6f54d4da582308fbb0f69b50/img/2000_4.png -------------------------------------------------------------------------------- /img/2000_5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlbertRapp/gt_book/7bec14ad39260f4f6f54d4da582308fbb0f69b50/img/2000_5.png -------------------------------------------------------------------------------- /img/2010_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlbertRapp/gt_book/7bec14ad39260f4f6f54d4da582308fbb0f69b50/img/2010_1.png -------------------------------------------------------------------------------- /img/2010_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlbertRapp/gt_book/7bec14ad39260f4f6f54d4da582308fbb0f69b50/img/2010_2.png -------------------------------------------------------------------------------- /img/2010_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlbertRapp/gt_book/7bec14ad39260f4f6f54d4da582308fbb0f69b50/img/2010_3.png -------------------------------------------------------------------------------- /img/2010_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlbertRapp/gt_book/7bec14ad39260f4f6f54d4da582308fbb0f69b50/img/2010_4.png -------------------------------------------------------------------------------- /img/2010_5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlbertRapp/gt_book/7bec14ad39260f4f6f54d4da582308fbb0f69b50/img/2010_5.png -------------------------------------------------------------------------------- /img/be-the-leaf-dance.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlbertRapp/gt_book/7bec14ad39260f4f6f54d4da582308fbb0f69b50/img/be-the-leaf-dance.gif -------------------------------------------------------------------------------- /img/screenshot_table.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlbertRapp/gt_book/7bec14ad39260f4f6f54d4da582308fbb0f69b50/img/screenshot_table.png -------------------------------------------------------------------------------- /img/stupid_table_screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlbertRapp/gt_book/7bec14ad39260f4f6f54d4da582308fbb0f69b50/img/stupid_table_screenshot.png -------------------------------------------------------------------------------- /index.qmd: -------------------------------------------------------------------------------- 1 | # Introduction {.unnumbered} 2 | 3 | Tables don't have to be dull lists of numbers. If done correctly, they can, in fact, be beautiful & engaging. And in this book, we're going to learn how to create such tables with R. More precisely, we will learn how to use the [`{gt}` package](https://gt.rstudio.com/index.html). 4 | 5 | [@sec-getting-started] is the starting point of our `{gt}` journey. It will cover useful guidelines for any table and how to implement them with `{gt}`. You can think of this chapter as a quickstart guide. 6 | 7 | Afterwards, we're learning how to add eye-catching elements like plots, icons and images to our table in [@sec-fancy-stuff]. Many of these elements are powered through the amazing [`{gtExtras}` package](https://jthomasmock.github.io/gtExtras/index.html). But you will also learn how to add any plot to your table (regardless of whether it is implemented in `{gtExtras}` or not.) 8 | 9 | Next, we'll talk about formatting the entries of your table in [@sec-formatting]. `{gt}` has two powerful sets of functions to get this job done, namely `fmt_*()` and `sub_*()`. These functions all work the same and are pretty easy to pick up. Hence, it's easy to format the data in your table the way you want or need. 10 | 11 | In [@sec-styling], we're learning how to change the theme of our table. This will be a long chapter. After all, we can basically change anything about our table's appearance. 12 | 13 | All of the knowledge that we've acquired in the previous chapters culminate in [@sec-case-studies]. That's where we will build elaborate tables with `{gt}`. 14 | 15 | Finally, [@sec-quarto] is a special chapter dedicated to the interactions between [Quarto](https://quarto.org/) and `{gt}`. As of right now (Quarto `v1.2.267` & `{gt}` `v0.8.0`), Quarto's default theme can interact with your `{gt}` table and change its theme. But there are ways to isolate Quarto from `{gt}`. This is what you'll learn in this chapter. 16 | 17 | ## Prerequisites 18 | 19 | This book is meant for people with an intermediate knowledge of R, specifically the Tidyverse. I do not expect people to know HTML/CSS for the most parts. 20 | 21 | Throughout most of the book, we'll only to do small changes with HTML/CSS and these bits will be explained. But there will be parts in [@sec-styling] and the subsequent chapters that will talk about changing CSS styles. Feel free to skip these parts if you're uncomfortable with CSS. But I'll do my best to help you with CSS. 22 | 23 | 24 | ## About this book 25 | 26 | This book is completely free and the code is open-source. If you find the things you learn here useful, I'd appreciate it if you sponsor my work via [Buy Me Coffee](https://www.buymeacoffee.com/rappa753). 27 | 28 | ::: {style="padding-bottom:20px;"} 29 | 30 | ```{=html} 31 | 32 | ``` 33 | 34 | ::: 35 | 36 | Also, if you're interested in working with me, I'm open to freelance work. You can book an appointment with me on [Calendly](https://calendly.com/r-a/30min). 37 | 38 | ## Session info 39 | 40 | Just so you're sure what versions I'm using. Here's my session info and my Quarto version. 41 | 42 | ```{r} 43 | sessioninfo::session_info("installed") 44 | ``` 45 | 46 | ```{bash} 47 | quarto check 48 | ``` 49 | -------------------------------------------------------------------------------- /index_SO_problems.qmd: -------------------------------------------------------------------------------- 1 | --- 2 | execute: 3 | echo: false 4 | message: false 5 | warning: false 6 | knitr: 7 | opts_chunk: 8 | column: body 9 | --- 10 | 11 | 12 | 13 | # Tables (lots of them) 14 | 15 | Before `as_raw_html()` was able to include images or custom styles like `gtExtras::gt_theme_538()` with inline CSS, it was a bit tricky to include tables in Quarto docs. 16 | I've spent some time trying to manually change all table classes in the html so that Quarto cannot overwrite it. 17 | Still, some stuff like line heights are still inherited from the surrounding Quarto doc (regardless of whether you use `as_raw_html()` or my function). 18 | I solved this issue by wrapping the code chunk into a div using `style="all:initial;"` (maybe there's a more elegant solution). 19 | 20 | In some cases, `as_raw_html()` does not seem to adjust line styles. 21 | Below, I've tested a few (non-minimal) table examples to see how the formatting may or may not change. 22 | In my examples, there were mainly two issues. 23 | 24 | - `as_raw_html()` still needs a surrounding div with `style="all:initial;"` 25 | - Small line heights lead to scroll bars that are not there when creating the table outside of Quarto 26 | - Line styles are not adjusted with `as_raw_html()`. 27 | 28 | ## My main function 29 | 30 | The new `as_raw_html()` is probably preferable to my own function (as it likely won't work with nested tables). 31 | But since my function does not have the issue with the line formatting I've kept it here for comparisons. 32 | 33 | 34 | ```{r} 35 | #| echo: true 36 | #| code-fold: true 37 | make_tbl_quarto_robust <- function(tbl) { 38 | # Get tbl html code (without the inline stuff) 39 | tbl_html <- tbl |> 40 | as_raw_html(inline_css = FALSE) 41 | 42 | # Find table id 43 | tbl_id <- str_match(tbl_html, 'id="(.*)"\\s')[,2] 44 | 45 | # Split html so that we only replace strings in the css part at first 46 | # That's important for performance 47 | split_html <- tbl_html |> 48 | str_split_1('') 49 | css_part <- split_html[1] |> 50 | str_split_1('