├── .Rbuildignore ├── .Rprofile ├── .gitattributes ├── .github └── workflows │ └── Render-Book.yaml ├── .gitignore ├── DESCRIPTION ├── MSG-packages.bib ├── Makefile ├── Modern-Stat-Graphics.bib ├── README.md ├── _bookdown.yml ├── _build.sh ├── _common.R ├── _deploy-book.R ├── _deploy.sh ├── _output.yml ├── _render.R ├── algorithm.Rmd ├── book.bib ├── data.Rmd ├── elements.Rmd ├── foreword.Rmd ├── gallery.Rmd ├── gui.Rmd ├── history.Rmd ├── images ├── China-Heart-3D.png ├── China-v-US-graphic.jpg ├── GGobi-2d-tour.png ├── GGobi-hp-mpg.png ├── GGobi-main-window.png ├── GGobi-narrow-brush.png ├── GGobi-parcoords.png ├── GGobi-qsec-wt.png ├── GGobi-scatterplot.png ├── Minard.png ├── Nightingale-mortality.jpg ├── Playfair-TimeSeries.png ├── Playfair-pie1.png ├── Playfair-pie2.jpg ├── RgoogleMaps-1.png ├── RgoogleMaps-2.png ├── Snow-cholera-map.jpg ├── canabalt-screenshot.png ├── cc-by-nc-sa.pdf ├── cc-by-nc-sa.svg ├── disk-usage.png ├── gwidgets-color-selector1.png ├── gwidgets-color-selector2.png ├── html-interface.png ├── pork-price-orig.pdf ├── pork-price-orig.png ├── randomForest-music.png ├── rgl-animation19.png ├── rgl-animation4.png ├── rgl-beta.pdf ├── rgl-beta.png ├── rgl-ch4.png ├── rgl-china-map.png ├── smoothScatter-ggplot.pdf ├── smoothScatter-ggplot.png ├── titlepic.pdf ├── worst-graph.pdf ├── worst-graph.png └── yihui-name-wordle.png ├── includes ├── data-url.html ├── ga_script.html └── header.html ├── index.Rmd ├── interactives ├── faithful.html ├── faithful.pdf ├── faithful.svg ├── heatmaply.html ├── heatmaply.pdf ├── heatmaply.svg ├── lib │ ├── CanvasMatrix4-2016 │ │ └── CanvasMatrix.src.js │ ├── crosstalk-1.0.0 │ │ ├── css │ │ │ └── crosstalk.css │ │ └── js │ │ │ ├── crosstalk.js │ │ │ ├── crosstalk.js.map │ │ │ ├── crosstalk.min.js │ │ │ └── crosstalk.min.js.map │ ├── htmlwidgets-1.3 │ │ └── htmlwidgets.js │ ├── jquery-1.11.3 │ │ ├── jquery-AUTHORS.txt │ │ ├── jquery.js │ │ ├── jquery.min.js │ │ └── jquery.min.map │ ├── plotly-binding-4.9.0 │ │ └── plotly.js │ ├── plotly-htmlwidgets-css-1.46.1 │ │ └── plotly-htmlwidgets.css │ ├── plotly-main-1.46.1 │ │ └── plotly-latest.min.js │ ├── rglWebGL-binding-0.100.30 │ │ └── rglWebGL.js │ ├── rglwidgetClass-2 │ │ ├── rgl.css │ │ └── rglClass.src.js │ └── typedarray-0.1 │ │ └── typedarray.min.js ├── plotly-china-map.html ├── plotly-china-map.pdf ├── plotly-china-map.svg ├── plotly-heatmap-contour.html ├── plotly-heatmap-contour.pdf ├── plotly-heatmap-contour.svg ├── plotly-histogram.html ├── plotly-histogram.pdf ├── plotly-histogram.svg ├── rgl-beta.html ├── rgl-ch4.html ├── rgl-china-heart.html └── rgl-china-map.html ├── model.Rmd ├── msg-pkgs.Rmd ├── msg.Rproj ├── postscript.Rmd ├── preamble.tex ├── preface.Rmd ├── principle.Rmd ├── programming.Rmd ├── references.Rmd ├── sidebar.lua ├── style.css ├── system.Rmd ├── texlive.txt ├── tools.Rmd └── tricks.Rmd /.Rbuildignore: -------------------------------------------------------------------------------- 1 | ^renv$ 2 | ^renv\.lock$ 3 | ^\.travis\.yml$ 4 | -------------------------------------------------------------------------------- /.Rprofile: -------------------------------------------------------------------------------- 1 | if (file.exists('~/.Rprofile')) sys.source('~/.Rprofile', envir = environment()) 2 | 3 | options( 4 | citation.bibtex.max = 999, 5 | bitmapType = "cairo", 6 | stringsAsFactors = FALSE, 7 | knitr.graphics.auto_pdf = TRUE, 8 | rgl.printRglwidget = TRUE, 9 | rgl.useNULL = TRUE, 10 | width = 79, 11 | demo.ask = FALSE, 12 | formatR.indent = 2, 13 | gganimate.nframes = 50, 14 | gganimate.fps = 20, 15 | str = utils::strOptions(strict.width = "cut"), 16 | tinytex.engine = 'xelatex', 17 | tikzDefaultEngine = "xetex", 18 | tikzDocumentDeclaration = "\\documentclass[tikz]{standalone}\n", 19 | tikzXelatexPackages = c( 20 | "\\usepackage[fontset=fandol]{ctex}", 21 | "\\usepackage[default,semibold]{sourcesanspro}", 22 | "\\usepackage{amsfonts,mathrsfs,amssymb}\n" 23 | ) 24 | ) 25 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.tex linguist-vendored 2 | *.cls linguist-vendored 3 | *.Rmd linguist-detectable 4 | -------------------------------------------------------------------------------- /.github/workflows/Render-Book.yaml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | branches: 4 | - master 5 | paths-ignore: 6 | - 'examples/**' 7 | - 'Dockerfile' 8 | pull_request: 9 | branches: 10 | - master 11 | 12 | name: Render-Book 13 | 14 | env: 15 | isExtPR: ${{ github.event.pull_request.head.repo.fork == true }} 16 | 17 | jobs: 18 | build: 19 | if: "!contains(github.event.head_commit.message, '[skip ci]')" 20 | runs-on: ${{ matrix.config.os }} 21 | name: ${{ matrix.config.os }} (r-${{ matrix.config.r }}) 22 | strategy: 23 | fail-fast: false 24 | matrix: 25 | config: 26 | - {os: macos-14, r: '4.4.0'} 27 | 28 | env: 29 | LANG: "en_US.UTF-8" 30 | TZ: "Asia/Shanghai" 31 | 32 | steps: 33 | - name: Checkout repo 34 | uses: actions/checkout@v4 35 | 36 | - name: Setup R 37 | uses: r-lib/actions/setup-r@v2 38 | with: 39 | use-public-rspm: true 40 | r-version: ${{ matrix.config.r }} 41 | 42 | - uses: r-lib/actions/setup-r-dependencies@v2 43 | 44 | - name: Install Pandoc 45 | uses: r-lib/actions/setup-pandoc@v2 46 | with: 47 | pandoc-version: '3.1.11' 48 | 49 | - name: Install ghostscript and Others 50 | run: | 51 | export HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK=1 52 | brew update 53 | brew install poppler optipng graphviz 54 | brew install --cask xquartz 55 | brew install --cask font-inconsolata font-dejavu 56 | 57 | - name: Install TinyTeX 58 | uses: r-lib/actions/setup-tinytex@v2 59 | env: 60 | # install full prebuilt version 61 | TINYTEX_INSTALLER: TinyTeX 62 | 63 | - name: Install LaTeX Packages 64 | run: | 65 | tinytex::tlmgr_install(readLines("texlive.txt")) 66 | shell: Rscript {0} 67 | 68 | - name: Install Fonts 69 | run: | 70 | library(showtext) 71 | font_install(source_han_serif(), quiet = TRUE) 72 | font_install(source_han_sans(), quiet = TRUE) 73 | shell: Rscript {0} 74 | 75 | - name: Add Fonts 76 | run: | 77 | curl -fLo Adobe-Fonts.zip https://github.com/XiangyunHuang/fonts/releases/download/v0.1/Adobe-Fonts.zip 78 | unzip Adobe-Fonts.zip -d ~/Library/Fonts/adobe 79 | fc-cache -fsv 80 | 81 | - name: Test Pkgs 82 | run: | 83 | sessionInfo(.packages(T)) 84 | capabilities() 85 | library(tikzDevice) 86 | tikzTest() 87 | tikzTest("$\\sin(x^2/2 - y^2/4 + 3) \\cos(2 x + 1 - \\exp(y))$") 88 | shell: Rscript {0} 89 | 90 | - name: Render Book 91 | id: render-book 92 | run: | 93 | bookdown::render_book("index.Rmd", "all") 94 | shell: Rscript {0} 95 | 96 | - name: Cp Graphics 97 | run: | 98 | mkdir -p _book/interactives && cp -r interactives/* _book/interactives 99 | 100 | - name: Upload book folder for debug 101 | if: failure() 102 | uses: actions/upload-artifact@v4 103 | with: 104 | name: Upload Book 105 | path: _book 106 | 107 | - name: Deploy book to bookdown.org 108 | if: github.event_name == 'push' 109 | env: 110 | CONNECT_API_KEY: ${{ secrets.RSC_BOOKDOWN_ORG_TOKEN }} 111 | CONTENT_ID: 2868 112 | run: | 113 | Rscript '_deploy-book.R' 114 | 115 | # 先本地创建一个 Github Pages 分支推送上来才行 116 | # https://bookdown.org/yihui/bookdown/github.html 117 | - name: Checkout the gh-pages branch 118 | uses: actions/checkout@v4 119 | with: 120 | ref: gh-pages 121 | path: book-output 122 | 123 | - name: Deploy to Github Pages 124 | if: ${{ github.ref == 'refs/heads/master' }} 125 | run: ./_deploy.sh 126 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .Rproj.user 2 | .Rhistory 3 | .RData 4 | .Ruserdata 5 | _main.rds 6 | *.log 7 | rsconnect 8 | packages.bib 9 | 10 | .project 11 | _book/ 12 | _bookdown_files/ 13 | tmp/ 14 | *-tikzDictionary 15 | temp.Rmd 16 | .DS_Store 17 | msg-2nd.rds 18 | msg.rds 19 | tikzMetricsCache 20 | docker-update/ 21 | -------------------------------------------------------------------------------- /DESCRIPTION: -------------------------------------------------------------------------------- 1 | Package: placeholder 2 | Type: Book 3 | Title: placeholder. 4 | Version: 1.0.0 5 | Depends: R (>= 4.1.0) 6 | Imports: 7 | alphahull, 8 | animation, 9 | aplpack, 10 | av, 11 | bookdown, 12 | corrplot, 13 | cowplot, 14 | car, 15 | formatR, 16 | fun, 17 | GGally, 18 | gganimate, 19 | ggplot2, 20 | ggpointdensity, 21 | gifski, 22 | heatmaply, 23 | hexbin, 24 | igraph, 25 | leaflet, 26 | magick, 27 | maps, 28 | MSG, 29 | mvtnorm, 30 | pdftools, 31 | plot3D, 32 | plotly, 33 | plotrix, 34 | quantreg, 35 | randomForest, 36 | raster, 37 | rgl, 38 | rsconnect, 39 | RgoogleMaps, 40 | scatterplot3d, 41 | showtext, 42 | sna, 43 | sf, 44 | sp, 45 | survminer, 46 | svglite, 47 | TeachingDemos, 48 | tikzDevice, 49 | transformr, 50 | tuneR, 51 | vcd, 52 | vioplot 53 | BugReports: https://github.com/XiangyunHuang/msg/issues 54 | -------------------------------------------------------------------------------- /MSG-packages.bib: -------------------------------------------------------------------------------- 1 | @Manual{alphahull, 2 | title = {alphahull: Generalization of the Convex Hull of a Sample of Points in the Plane}, 3 | author = {Beatriz Pateiro-Lopez and Alberto Rodriguez-Casal and {.}}, 4 | year = {2022}, 5 | note = {R package version 2.5}, 6 | url = {https://CRAN.R-project.org/package=alphahull}, 7 | } 8 | 9 | @Manual{aplpack, 10 | title = {aplpack: Another Plot Package: Bagplots, Iconplots, Summaryplots, Slider Functions and Others}, 11 | author = {Hans Peter Wolf}, 12 | year = {2021}, 13 | note = {R package version 1.3.5}, 14 | url = {https://www.uni-bielefeld.de/fakultaeten/wirtschaftswissenschaften/fakultaet/lehrende-ehemalige/pwolf/wolf_aplpack/index.xml}, 15 | } 16 | 17 | @Manual{base, 18 | title = {R: A Language and Environment for Statistical Computing}, 19 | author = {{R Core Team}}, 20 | organization = {R Foundation for Statistical Computing}, 21 | address = {Vienna, Austria}, 22 | year = {2024}, 23 | url = {https://www.R-project.org/}, 24 | } 25 | 26 | @Manual{corrplot, 27 | title = {corrplot: Visualization of a Correlation Matrix}, 28 | author = {Taiyun Wei and Viliam Simko}, 29 | year = {2021}, 30 | note = {R package version 0.92}, 31 | url = {https://github.com/taiyun/corrplot}, 32 | } 33 | 34 | @Manual{formatR, 35 | title = {formatR: Format R Code Automatically}, 36 | author = {Yihui Xie}, 37 | year = {2023}, 38 | note = {R package version 1.14}, 39 | url = {https://github.com/yihui/formatR}, 40 | } 41 | 42 | @Manual{fun, 43 | title = {fun: Use R for Fun}, 44 | author = {Yihui Xie and Yixuan Qiu and Taiyun Wei}, 45 | year = {2020}, 46 | note = {R package version 0.3}, 47 | url = {https://github.com/yihui/fun}, 48 | } 49 | 50 | @Manual{GGally, 51 | title = {GGally: Extension to ggplot2}, 52 | author = {Barret Schloerke and Di Cook and Joseph Larmarange and Francois Briatte and Moritz Marbach and Edwin Thoen and Amos Elberg and Jason Crowley}, 53 | year = {2024}, 54 | note = {R package version 2.2.1}, 55 | url = {https://ggobi.github.io/ggally/}, 56 | } 57 | 58 | @Manual{ggpointdensity, 59 | title = {ggpointdensity: A Cross Between a 2D Density Plot and a Scatter Plot}, 60 | author = {Lukas P. M. Kremer}, 61 | year = {2019}, 62 | note = {R package version 0.1.0}, 63 | url = {https://github.com/LKremer/ggpointdensity}, 64 | } 65 | 66 | @Manual{heatmaply, 67 | title = {heatmaply: Interactive Cluster Heat Maps Using plotly and ggplot2}, 68 | author = {Tal Galili and Alan O'Callaghan}, 69 | year = {2023}, 70 | note = {R package version 1.5.0, https://cran.r-project.org/package=heatmaply, https://github.com/talgalili/heatmaply/}, 71 | url = {https://talgalili.github.io/heatmaply/}, 72 | } 73 | 74 | @Manual{hexbin, 75 | title = {hexbin: Hexagonal Binning Routines}, 76 | author = {Dan Carr and Nicholas Lewin-Koh and Martin Maechler}, 77 | year = {2023}, 78 | note = {R package version 1.28.3}, 79 | url = {https://github.com/edzer/hexbin}, 80 | } 81 | 82 | @Manual{htmlwidgets, 83 | title = {htmlwidgets: HTML Widgets for R}, 84 | author = {Ramnath Vaidyanathan and Yihui Xie and JJ Allaire and Joe Cheng and Carson Sievert and Kenton Russell}, 85 | year = {2023}, 86 | note = {R package version 1.6.4}, 87 | url = {https://github.com/ramnathv/htmlwidgets}, 88 | } 89 | 90 | @Manual{leaflet, 91 | title = {leaflet: Create Interactive Web Maps with the JavaScript Leaflet Library}, 92 | author = {Joe Cheng and Barret Schloerke and Bhaskar Karambelkar and Yihui Xie}, 93 | year = {2024}, 94 | note = {R package version 2.2.2, https://github.com/rstudio/leaflet}, 95 | url = {https://rstudio.github.io/leaflet/}, 96 | } 97 | 98 | @Manual{maps, 99 | title = {maps: Draw Geographical Maps}, 100 | author = {Ray Brownrigg}, 101 | year = {2023}, 102 | note = {R package version 3.4.2}, 103 | url = {https://CRAN.R-project.org/package=maps}, 104 | } 105 | 106 | @Manual{MSG, 107 | title = {MSG: Data and Functions for the Book Modern Statistical Graphics}, 108 | author = {Yihui Xie and Peng Zhao}, 109 | year = {2021}, 110 | note = {R package version 0.8}, 111 | url = {https://github.com/yihui/MSG}, 112 | } 113 | 114 | @Manual{quantreg, 115 | title = {quantreg: Quantile Regression}, 116 | author = {Roger Koenker}, 117 | year = {2023}, 118 | note = {R package version 5.97}, 119 | url = {https://www.r-project.org}, 120 | } 121 | 122 | @Manual{RColorBrewer, 123 | title = {RColorBrewer: ColorBrewer Palettes}, 124 | author = {Erich Neuwirth}, 125 | year = {2022}, 126 | note = {R package version 1.1-3}, 127 | url = {https://CRAN.R-project.org/package=RColorBrewer}, 128 | } 129 | 130 | @Manual{rgl, 131 | title = {rgl: 3D Visualization Using OpenGL}, 132 | author = {Daniel Adler and Duncan Murdoch}, 133 | year = {2024}, 134 | note = {R package version 1.3.1}, 135 | url = {https://github.com/dmurdoch/rgl}, 136 | } 137 | 138 | @Manual{rpart, 139 | title = {rpart: Recursive Partitioning and Regression Trees}, 140 | author = {Terry Therneau and Beth Atkinson}, 141 | year = {2023}, 142 | note = {R package version 4.1.23, https://cran.r-project.org/package=rpart}, 143 | url = {https://github.com/bethatkinson/rpart}, 144 | } 145 | 146 | @Manual{sm, 147 | title = {sm: Smoothing Methods for Nonparametric Regression and Density Estimation}, 148 | author = {Adrian Bowman and Adelchi Azzalini}, 149 | year = {2024}, 150 | note = {R package version 2.2-6.0}, 151 | url = {https://CRAN.R-project.org/package=sm}, 152 | } 153 | 154 | @Manual{TeachingDemos, 155 | title = {TeachingDemos: Demonstrations for Teaching and Learning}, 156 | author = {Greg Snow}, 157 | year = {2024}, 158 | note = {R package version 2.13}, 159 | url = {https://CRAN.R-project.org/package=TeachingDemos}, 160 | } 161 | 162 | @Manual{tikzDevice, 163 | title = {tikzDevice: R Graphics Output in LaTeX Format}, 164 | author = {Charlie Sharpsteen and Cameron Bracken}, 165 | year = {2023}, 166 | note = {R package version 0.12.6}, 167 | url = {https://github.com/daqana/tikzDevice}, 168 | } 169 | @Manual{survival, 170 | title = {A Package for Survival Analysis in R}, 171 | author = {Terry M Therneau}, 172 | year = {2024}, 173 | note = {R package version 3.6-4}, 174 | url = {https://CRAN.R-project.org/package=survival}, 175 | } 176 | @Manual{Matrix, 177 | title = {Matrix: Sparse and Dense Matrix Classes and Methods}, 178 | author = {Douglas Bates and Martin Maechler and Mikael Jagan}, 179 | year = {2024}, 180 | note = {R package version 1.7-0}, 181 | url = {https://CRAN.R-project.org/package=Matrix}, 182 | } 183 | @Manual{KernSmooth, 184 | title = {KernSmooth: Functions for Kernel Smoothing Supporting Wand \& Jones (1995)}, 185 | author = {Matt Wand}, 186 | year = {2024}, 187 | note = {R package version 2.23-24}, 188 | url = {https://CRAN.R-project.org/package=KernSmooth}, 189 | } 190 | @Manual{tuneR, 191 | title = {tuneR: Analysis of Music and Speech}, 192 | author = {Uwe Ligges}, 193 | year = {2024}, 194 | note = {R package version 1.4.7}, 195 | url = {https://CRAN.R-project.org/package=tuneR}, 196 | } 197 | 198 | @Manual{vcd, 199 | title = {vcd: Visualizing Categorical Data}, 200 | author = {David Meyer and Achim Zeileis and Kurt Hornik and Michael Friendly}, 201 | year = {2023}, 202 | note = {R package version 1.4-12}, 203 | url = {https://CRAN.R-project.org/package=vcd}, 204 | } 205 | 206 | @Manual{vioplot, 207 | title = {vioplot: Violin Plot}, 208 | author = {Daniel Adler and S. Thomas Kelly and Tom M. Elliott and Jordan Adamson}, 209 | year = {2022}, 210 | note = {R package version 0.4.0}, 211 | url = {https://github.com/TomKellyGenetics/vioplot}, 212 | } 213 | 214 | @Manual{aplpack2019, 215 | title = {{aplpack}: Another Plot Package (version 190512)}, 216 | author = {Hans Peter Wolf}, 217 | year = {2019}, 218 | url = {https://cran.r-project.org/package=aplpack}, 219 | } 220 | 221 | @Manual{corrplot2021, 222 | title = {R package 'corrplot': Visualization of a Correlation Matrix}, 223 | author = {Taiyun Wei and Viliam Simko}, 224 | year = {2021}, 225 | note = {(Version 0.92)}, 226 | url = {https://github.com/taiyun/corrplot}, 227 | } 228 | 229 | @Article{heatmaply2017, 230 | author = {{Galili} and {Tal} and {O'Callaghan} and {Alan} and {Sidi} and {Jonathan} and {Sievert} and {Carson}}, 231 | title = {heatmaply: an R package for creating interactive cluster heatmaps for online publishing}, 232 | journal = {Bioinformatics}, 233 | year = {2017}, 234 | doi = {10.1093/bioinformatics/btx657}, 235 | url = {https://academic.oup.com/bioinformatics/article-pdf/doi/10.1093/bioinformatics/btx657/21358327/btx657.pdf}, 236 | } 237 | 238 | @Manual{sm2024, 239 | title = {{R} package \texttt{sm}: nonparametric smoothing methods (version 2.2-6.0)}, 240 | author = {A. W. Bowman and A. Azzalini}, 241 | address = {University of Glasgow, UK and Universit\`a di Padova, Italia}, 242 | year = {2024}, 243 | url = {http://www.stats.gla.ac.uk/~adrian/sm/}, 244 | } 245 | 246 | @Manual{tuneR2023, 247 | title = {{tuneR}: Analysis of Music and Speech}, 248 | author = {Uwe Ligges and Sebastian Krey and Olaf Mersmann and Sarah Schnackenberg}, 249 | year = {2023}, 250 | url = {https://CRAN.R-project.org/package=tuneR}, 251 | } 252 | 253 | @Article{vcd2006, 254 | title = {The Strucplot Framework: Visualizing Multi-Way Contingency Tables with vcd}, 255 | author = {David Meyer and Achim Zeileis and Kurt Hornik}, 256 | journal = {Journal of Statistical Software}, 257 | year = {2006}, 258 | volume = {17}, 259 | number = {3}, 260 | pages = {1--48}, 261 | doi = {10.18637/jss.v017.i03}, 262 | } 263 | 264 | @Article{vcd2007, 265 | title = {Residual-based Shadings for Visualizing (Conditional) Independence}, 266 | author = {Achim Zeileis and David Meyer and Kurt Hornik}, 267 | journal = {Journal of Computational and Graphical Statistics}, 268 | year = {2007}, 269 | volume = {16}, 270 | number = {3}, 271 | pages = {507--525}, 272 | doi = {10.1198/106186007X237856}, 273 | } 274 | 275 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | pdf: 2 | Rscript --quiet _render.R "bookdown::pdf_book" 3 | 4 | gitbook: 5 | Rscript --quiet _render.R "bookdown::gitbook" 6 | 7 | all: 8 | Rscript --quiet _render.R 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## [![Book build status](https://github.com/XiangyunHuang/msg/workflows/Render-Book/badge.svg)](https://github.com/XiangyunHuang/msg/actions?workflow=Render-Book) 2 | 3 | ## 现代统计图形 4 | 5 | 本仓库作为 [《现代统计图形》](https://bookdown.org/xiangyun/msg/) 书稿源码的托管地址,《现代统计图形》印刷版的主页见 。 6 | 7 | ## Modern Statistical Graphics 8 | 9 | This is code and text behind the [Modern Statistical Graphics](https://bookdown.org/xiangyun/msg/). 10 | 11 | ## 授权 LICENSE 12 | 13 | 知识共享许可协议 14 | 15 | 本作品采用知识共享署名-非商业性使用-相同方式共享 2.5 中国大陆许可协议进行许可。 16 | 17 | ------------------------------------------------------------------------ 18 | 19 | ## 益辉答读者问 20 | 21 | ### 1. 统计图形在对信息高度浓缩的同时,怎么做到既美观,又简约?或者作图的美观性和传递信息的严谨性是怎样相辅相成的?是否在博弈? 22 | 23 | 美观通常是主观评价,所以这个问题不太容易回答。比如我个人崇尚极简主义,我认为简约本身就很美观,但也一定有很多人喜欢华丽或其它风格。统计图形要做到简约的话,说到底就是突出你想展现的主要信息,尽量淡化、隐去甚至去掉次要信息。下图是我若干年前看到的一幅动图,一步步展示了如何简化一幅繁杂的条形图,可供参考: 24 | 25 | ![data-ink](https://user-images.githubusercontent.com/12031874/131237513-474122a3-2ffd-4004-a4b2-f1105b2baf53.gif) 26 | 27 | 美观和严谨应该是互不冲突的,或者说二者并没有一定的关联。图形是一种信息编码方式,而且通常是对数值进行编码,只要读者在用眼睛解码时大概能正确解读和感知出真实的数据,那我觉得就算严谨了。严谨的图看起来可能好看,也可能不好看。比如对偏度很大的数据,无论画什么分布图(直方图、箱线图等),我觉得应该都不会“美观”,直方图会是一两根很高的柱子加上一长条扁平的尾巴、箱线图会是一个瘪瘪的箱子加一些离群点,但这些图形都是严谨的。再比如三维透视图通常看起来好看,尤其是点缀上光源效果之后,看上去很逼真,但三维图形在人眼中的解码比二维平面图形低效,三维空间中每个元素都需要向三个平面投影才能解读它的数值,而且三维透视图也很容易造成视觉欺骗,所以美观的图也可能不严谨。 28 | 29 | ### 2. R和各大包更新了那么多代,内容会不会过时? 30 | 31 | 这个问题我在序言中回答过。本书尽量兼顾图形“道”与“术”,其中“道”的部分应该没那么容易过时。“术”的部分依赖于具体的工具,比如 R 和 R 包。R 的基础图形系统可以说在二十年间变化都比较少,但 R 包(比如 ggplot2)的变化确实会相对快一些。如果你注重稳定、希望十年前的作图代码还可以继续用,不妨可以试试基础图形系统。基础图形系统与 ggplot2 的区别我在序言中也有提到。 32 | 33 | ### 3. 现在一般用Python了,R的语法生疏了,怎么更好地读这本书呢? 34 | 35 | 这取决于你是否还有意愿把 R 捡起来。如果有,那么可以找个快速入门教程先复习一下基本概念和语法;如果没有,那么可以忽略书中具体的代码实现,其中相当一部分图形应该都是可以在别的语言中重现的。 36 | 37 | ### 4. 学习R语言需要数学基础吗? 38 | 39 | 如果不涉及到建模分析的话,那么几乎不需要数学基础,会数数的数学水平应该就够。 40 | 41 | ### 5. ggplot2 vs R基础绘图系统(哪个更胜一筹?) 42 | 43 | 各有所长。书的序言中有提到。归根结底,是抽象快捷和细节控制二者之间的平衡和取舍问题。 44 | 45 | ### 6. 统计图形的艺术性和功能性如何辩证看待? 46 | 47 | 艺术?艺术就是爆炸(迪达拉),还是永恒(赤砂蝎)?……我个人倾向于功能性优先(即永恒)。如果有人信奉艺术就是爆炸的美学,那我觉得可以关注一下纽约时报的可视化项目,它们有时候很有艺术性。另外有个做可视化的网站叫布丁,我个人觉得它的艺术性非常惊艳,供参考: 48 | 49 | ### 7. 真的一图胜千言吗?图形承载多大的信息量才合适,或者,清楚明白才是王道? 50 | 51 | 一图胜千言这话当然不绝对,有些“言”简练清晰有力,恐怕图也胜不了。比如左转中的一句“晋侯复假道于虞以伐虢”,和画一幅地图相比,恐怕还是文字会赢(地图很难体现文字中的“复”字)。有些数据要是用文字描述起来会很啰嗦,而画图出来则一目了然,这时候才容易一图胜千言。书中第一章提到的拿破仑行军图就是这样的例子。 52 | 53 | 难说图形承载多大的信息量合适,因为这有点涉及到主观判断。有读者喜欢信息简洁,有读者喜欢看尽量多的细节。我觉得也不一定非得一幅图里塞下所有信息,可以同一批数据用几幅图来分别展现不同的重点,比如有的图展现分布形状,有的图展现相关关系,有的图展现建模结果。 54 | 55 | 另外,也可以考虑使用交互式图形。在初始图形上显示相对简单的信息,留给读者交互探索的余地。要是读者想看更多细节,他可以自己去拖去点,哪里不会点哪里。 56 | 57 | ### 8. 对于高维数据的可视化,有没有很漂亮的表达方式?或者可否举一些精彩的例子呢? 58 | 59 | 高维数据最终还是要落到平面上来表达,所以通常都得降维。有些降维方式很巧妙,比如书中提到的调和曲线图(图 6.14)将高维数据表达成一条曲线,让曲线之间的距离正好能代表样本之间的欧氏距离。类似的还有多维标度法(Multidimensional scaling),它把高维数据表达在平面上的散点里,使得每两点之间的距离尽量跟它们真实的欧氏距离成正比。 60 | 61 | 如果不想降维,那也有办法,比如书中提到的平行坐标图(图 6.13)。我的博士导师们和合作者早年间曾开发了一个跨时代的软件工具叫 GGobi,只可惜后来后继无人、快要失传了,这个软件专注于高维数据可视化和交互式图形,其中它的高维可视化就是将高维数据不断用新的投影矩阵投到平面上,出来的动画效果就像是在持续扭转一个高维“数据球”,让你看到数据的不同侧面。背后涉及到的数学技术有投影寻踪(多诗意的名字,Projection pursuit)等等。 62 | 63 | ### 9. 撇开各种图形类型和工具,您认为画好一张统计图形,最重要的素质和要求是什么? 64 | 65 | 这个问题听起来像一道考试题,我不知道我是否有资格回答。论素质,我觉得还是要用心吧(参见我在序言中提到的“生命的别名就是心”):用心处理数据,首先数据别搞错了;用心选用合适的图形元素表达关键的信息。论要求,我觉得让读者很快能解读出数据的特征,就算是很好的图形了;如果一幅图读起来有探案的效果、充满着惊奇,那就更好了。 66 | 67 | ### 10. 请问使用shiny显示实时图形应用会越来越多吗,技术难度会降低吗? 68 | 69 | 我个人感觉那种实时、交互式的图形会越来越多,但它们应该很难在可视化领域占据主导地位。在可预见的未来里,我觉得静态图形还会是主导。一方面,我觉得出版业应该不至于太快消亡,应该也有很多人倾向于读静态报告;另一方面,实时的动态图形真正读起来也比较耗费脑力,我觉得人脑承受不了这种信息量,就算动态图形成了主流,结果很可能会是多数人都审美疲劳或信息疲劳,变成“视而不见”。当然,这些只是我的猜测。 70 | 71 | 技术难度一定会降低的。 72 | 73 | ### 11. 您好,我买了这本新书。然后尝试了一下代码,发现: 输出的图表中,不显示汉字,请问这种情况该如何解决? 74 | 75 | 这种技术性问题不妨到论坛上问: 但请按论坛的规矩发帖,提供相应的系统信息。 76 | 77 | ### 12. 想问问大佬这本书的定位是怎样的,以及阅读这本书的方法? 78 | 79 | 本书的前言回答了这个问题。 80 | 81 | ### 13. 这本书14年前就在网上开源啦,图灵出的书和网上内容相比,主要有哪些变化? 82 | 83 | 本书的后记里部分回答了这个问题。更详细的对比参见: 84 | 85 | ### 14. 有哪些值得推荐的数据可视化工具? 86 | 87 | 这个问题听起来像个知乎问题,有点笼统。我从没在知乎上回答过问题,这个问题我也不知从何答起。什么“值得推荐”取决于“推荐了之后想拿它去做什么”。本书介绍的主要是 R,有点偏统计。现在江湖上的 JavaScript 酷炫可视化库也铺天盖地,我觉得也可以稍微关注一下,具体哪家强我就不点名了(我不是这方面的专家,而且我掌握的信息可能已经落后时代几年了)。 88 | 89 | ### 15. 本书对R语言的要求是什么样的? 90 | 91 | 可以初学者,也可以是熟悉 R 语言的程度。前言中有提到,依据对 R 掌握的不同等级,这本书可以有不同的读法。 92 | 93 | ### 16. Science/Nature的编辑偏好什么样的统计图形? 94 | 95 | 这个我不了解。最多只能提供一个数据点,就是魏太云开发的 corrplot 包在《科学》《自然》杂志上露面多次了;如果它有代表性的话,那我觉得图形还是清晰明了就可以了,估计那些编辑应该不至于会偏爱某一种或几种特定的统计图形,否则这种杂志就太封闭了。 96 | 97 | ### 17. 如何锻炼或者如何学习将数据图形化的能力;数据可视化的学习路径是什么有哪些途径获得实操项目? 98 | 99 | 可以从第一章中的名作开始学习,其实那些图并没有什么复杂之处:两条线、一群点、几块饼、一条带而已,但都清楚展现出了问题(尤其霍乱传染图和南丁格尔玫瑰图)。 100 | 101 | 关于实操项目,本书第 3 章介绍了十个实例,都是从新闻时事或者作者自己关心的问题里发展出来的。回答你自己关心的问题,往往是最好的研究和学习动力。 102 | 103 | ### 18. 我听说有一些统计的假设检验是针对异常值和离群点的。请问在实务中,是对数据先做异常值的假设检验,抛去统计上显著的异常值之后,用剩余的数据做统计图形更好,还是先把所有数据用统计图形展示,通过观察再去除异常值更好呢?谢谢 104 | 105 | 我觉得异常值在统计分析里可能都是被冤枉了。有句话叫:我不是异常值,我只是还没找到我的分布而已!要不要剔除所谓的异常值,得首先看异常值是正确还是错误的数据。要是本来就是错误数据,通过分析或画图发现了,那剔除掉没什么可说的。如果它是真实的正确数据,我觉得还是谨慎处理。 106 | 107 | 有异常值的图形往往不那么“好看”,但你得自问,是选择好看,还是选择真实?当然,我也并不觉得这二者互斥。只要空间和版面允许,完全可以画两幅图,一幅包含异常值,一幅不包含。不包含异常值的图形有助于看清剩下的数据的特征。 108 | 109 | ### 19. 统计绘图当中,不少GUI软件貌似在不少情况下效率更高:代码 vs GUI 110 | 111 | 本书的 2.2 小节中谈到了这个问题。如果是一次性的作图、作后即焚,而你感觉图形界面软件好用,那用一次也无妨。如果是注重图形的可重复性,那么用代码作图更方便;比如要是将来发现数据变了,那代码只需要重跑一遍即可,而哪里不会点哪里的图形界面软件就需要你再全程点一遍。 112 | -------------------------------------------------------------------------------- /_bookdown.yml: -------------------------------------------------------------------------------- 1 | book_filename: "msg" 2 | delete_merged_file: true 3 | language: 4 | label: 5 | fig: "图 " 6 | tab: "表 " 7 | ui: 8 | edit: "编辑" 9 | chapter_name: ["第 ", " 章"] 10 | 11 | before_chapter_script: ["_common.R"] 12 | new_session: yes 13 | rmd_files: 14 | - "index.Rmd" 15 | - "preface.Rmd" 16 | - "foreword.Rmd" 17 | 18 | - "history.Rmd" 19 | - "tools.Rmd" 20 | 21 | - "elements.Rmd" 22 | - "gallery.Rmd" 23 | 24 | - "system.Rmd" 25 | - "data.Rmd" 26 | - "principle.Rmd" 27 | 28 | - "programming.Rmd" 29 | - "tricks.Rmd" 30 | - "gui.Rmd" 31 | - "msg-pkgs.Rmd" 32 | - "postscript.Rmd" 33 | 34 | - "references.Rmd" 35 | -------------------------------------------------------------------------------- /_build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | Rscript --no-save -e "bookdown::render_book(input = 'index.Rmd', output_format = 'all', output_dir = '_book', config_file = '_bookdown.yml')" 4 | -------------------------------------------------------------------------------- /_common.R: -------------------------------------------------------------------------------- 1 | # convert pdf to png 2 | to_png <- function(fig_path) { 3 | png_path <- sub("\\.pdf$", ".png", fig_path) 4 | magick::image_write(magick::image_read_pdf(fig_path), format = "png", path = png_path, 5 | density = 300, quality = 100) 6 | return(png_path) 7 | } 8 | 9 | library(formatR) 10 | 11 | is_latex <- identical(knitr::opts_knit$get("rmarkdown.pandoc.to"), "latex") 12 | is_html <- identical(knitr::opts_knit$get("rmarkdown.pandoc.to"), "html") 13 | 14 | knitr::knit_hooks$set( 15 | optipng = knitr::hook_optipng, 16 | pdfcrop = knitr::hook_pdfcrop, 17 | small.mar = function(before, options, envir) { 18 | if (before) par(mar = c(4, 4, .1, .1), cex.lab = .95, cex.axis = .9, mgp = c(2, .7, 0), tcl = -.3) # smaller margin on top and right 19 | }, 20 | font.cn = function(before, options, envir) { 21 | if (before) pdf.options(family = "GB1") 22 | }, 23 | font.en = function(before, options, envir) { 24 | if (before) pdf.options(family = "Times") 25 | } 26 | ) 27 | 28 | knitr::opts_chunk$set( 29 | fig.align = "center", 30 | cache = TRUE, 31 | small.mar = TRUE, 32 | fig.showtext = TRUE, 33 | dpi = 300 34 | ) 35 | 36 | if (is_latex) { 37 | knitr::opts_chunk$set( 38 | out.width = "70%" 39 | ) 40 | } 41 | 42 | append_ext <- function(filename) { 43 | paste(filename, ifelse(is_latex, "pdf", "svg"), sep = ".") 44 | } 45 | -------------------------------------------------------------------------------- /_deploy-book.R: -------------------------------------------------------------------------------- 1 | # On CI connect to server, using API KEY and deploy using appId 2 | rsconnect::addServer(url = "https://bookdown.org", name = "bookdown.org") 3 | rsconnect::connectApiUser( 4 | account = "xiangyun", server = "bookdown.org", 5 | apiKey = Sys.getenv("CONNECT_API_KEY") 6 | ) 7 | rsconnect::deploySite( 8 | siteName = "msg", 9 | siteTitle = "现代统计图形", 10 | server = "bookdown.org", 11 | render = "none", account = "xiangyun", 12 | forceUpdate = TRUE 13 | ) 14 | -------------------------------------------------------------------------------- /_deploy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | cd book-output 6 | 7 | git config --local user.email "actions@github.com" 8 | git config --local user.name "GitHub Actions" 9 | 10 | ls | xargs rm -rf 11 | git ls-files --deleted -z | xargs -0 git rm 12 | 13 | cp -r ../_book/* ./ 14 | git add --all * 15 | git commit -m "Update the book" || true 16 | git reset $(git commit-tree HEAD^{tree} -m "Update the book") 17 | git push -f -q origin gh-pages 18 | -------------------------------------------------------------------------------- /_output.yml: -------------------------------------------------------------------------------- 1 | bookdown::gitbook: 2 | css: style.css 3 | split_by: rmd 4 | dev: "svglite" 5 | config: 6 | toc: 7 | before: | 8 |
  • 现代统计图形
  • 9 | after: | 10 |
  • Published with bookdown
  • 11 | edit: 12 | link: https://github.com/XiangyunHuang/MSG-Book/edit/master/%s 13 | text: "Edit" 14 | sharing: 15 | github: yes 16 | facebook: no 17 | twitter: no 18 | all: [] 19 | md_extensions: "+ascii_identifiers" 20 | includes: 21 | in_header: 22 | - includes/header.html 23 | - includes/ga_script.html 24 | after_body: 25 | - includes/data-url.html 26 | bookdown::pdf_book: 27 | includes: 28 | in_header: preamble.tex 29 | latex_engine: xelatex 30 | citation_package: natbib 31 | template: null 32 | dev: "cairo_pdf" 33 | keep_tex: yes 34 | keep_md: yes 35 | extra_dependencies: 36 | ctex: 37 | - heading 38 | - fontset=adobe 39 | pandoc_args: ["--top-level-division=chapter", "--lua-filter=sidebar.lua"] 40 | quote_footer: ["\\hspace*{\\fill} ", ""] 41 | -------------------------------------------------------------------------------- /_render.R: -------------------------------------------------------------------------------- 1 | quiet = "--quiet" %in% commandArgs(FALSE) 2 | formats = commandArgs(TRUE) 3 | 4 | # provide default formats if necessary 5 | if (length(formats) == 0) formats = c('bookdown::pdf_book', 'bookdown::gitbook') 6 | # render the book to all formats unless they are specified via command-line args 7 | for (fmt in formats) { 8 | cmd = sprintf("bookdown::render_book('index.Rmd', '%s', quiet = %s)", fmt, quiet) 9 | res = bookdown:::Rscript(c('-e', shQuote(cmd))) 10 | if (res != 0) stop('Failed to compile the book to ', fmt) 11 | } 12 | 13 | if (length(formats) > 1 && Sys.getenv("USER") == "xiangyun") { 14 | bookdown::publish_book(name = "msg", account = "xiangyun", render = "none", server = "bookdown.org") 15 | } 16 | -------------------------------------------------------------------------------- /algorithm.Rmd: -------------------------------------------------------------------------------- 1 | # 算法 {#cha:algorithm} 2 | 3 | 4 | ## 随机森林 {#sec:rf} 5 | 6 | 7 | 8 | ## 支持向量机 {#sec:svm} 9 | 10 | 11 | -------------------------------------------------------------------------------- /book.bib: -------------------------------------------------------------------------------- 1 | @Book{xie2015, 2 | title = {Dynamic Documents with {R} and knitr}, 3 | author = {Yihui Xie}, 4 | publisher = {Chapman and Hall/CRC}, 5 | address = {Boca Raton, Florida}, 6 | year = {2015}, 7 | edition = {2nd}, 8 | note = {ISBN 978-1498716963}, 9 | url = {http://yihui.org/knitr/}, 10 | } 11 | @Book{xie2016, 12 | title = {bookdown: Authoring Books and Technical Documents with {R} Markdown}, 13 | author = {Yihui Xie}, 14 | publisher = {Chapman and Hall/CRC}, 15 | address = {Boca Raton, Florida}, 16 | year = {2016}, 17 | note = {ISBN 978-1138700109}, 18 | url = {https://github.com/rstudio/bookdown}, 19 | } 20 | @Book{xie2018, 21 | title = {R Markdown: The Definitive Guide}, 22 | author = {Yihui Xie and J.J. Allaire and Garrett Grolemund}, 23 | publisher = {Chapman and Hall/CRC}, 24 | address = {Boca Raton, Florida}, 25 | year = {2018}, 26 | note = {ISBN 9781138359338}, 27 | url = {https://bookdown.org/yihui/rmarkdown}, 28 | } 29 | -------------------------------------------------------------------------------- /foreword.Rmd: -------------------------------------------------------------------------------- 1 | \mainmatter 2 | 3 | # 前言 {#forword .unnumbered} 4 | 5 | 我们常说一图胜千言,然而现实情况是我们了解的图形种类太少、使用的作图工具缺乏灵活性,这在很大程度上制约了统计图形的发展,使得统计图形在数据分析中应有的潜力没有被充分挖掘出来,正是这样的背景催生了本书的写作。 6 | 7 | 本书根据统计图形制作的需要,将所有内容分为七章:第一章先选择性回顾历史上几幅著名统计图形,在欣赏前人智慧的基础上说明统计图形在社会生活的各个方面所能体现的价值;第二章介绍图形工具,本书主要以 R 软件为制图工具,因此本章也会介绍关于 R 语言的一些基础知识;第三章讲解基础图形元素的使用,包括点、线、多边形、颜色和文本等,本章会给那些期望能自定义统计图形的读者提供方便的解决方案;第四章是本书的一大核心,集中介绍讲解现有的统计图形种类如常见的直方图、条形图、茎叶图、饼图、箱线图等,此外还会引入若干较特殊和不太常见的图形种类和数据图示方法,并且配以相应的统计数据分析实例深入说明统计图形的用法和含义;第五章介绍基础图形系统(base graphics)之外的其它图形系统如 grid、lattice 和 ggplot2 ;第六章从数据的角度对各种统计图形给出一些应用实例并作出归纳总结,以便让读者清楚区分统计图形运用的条件和场合;第七章总结分析了绘制统计图形的一些指导原则;附录 \@ref(chap:programming) 是为 R 新手提供的编程入门资料;附录 \@ref(cha:tricks) 详细介绍了基础图形系统的图形参数和一些作图技巧,用以对图形进行细节调整;附录 \@ref(cha:GUI) 介绍了如何用 R 编写图形界面。 8 | 9 | 本书可以从任意章节开始读,各章节之间没有严格的逻辑依赖关系。对于熟悉或喜爱编程的读者,本书可按顺序从前往后阅读;对于不熟悉编程并且对编程不感兴趣的读者,本书阅读顺序可以是:第一章、第八章、第七章、第五章,其余章节可选择性阅读或放弃阅读。对于有统计学理论基础的专业人士,应该更加关注第五章中的统计方法和图形的对应关系;对于非统计专业人士,第七章的数据案例可能会带来一些启示。书中有若干例彩蛋,它们与统计学几乎没什么关系,仅供娱乐消遣,但也可作为学习函数使用方法的参考。对于根本无暇阅读本书的读者,仅仅了解本书要传达的部分观点也无妨: 10 | 11 | 1. 我们对统计图形可以有完全的控制,这种自由在某些情况下很重要,但我们的大脑永远比我们的工具重要; 12 | 13 | 1. 我们最熟悉的饼图有它自身设计的巧妙 - 切分一个 $360\,^{\circ}$ 的圆圈给人以形象的“比例”展示,然而它表达数据的实际效果却不如条形图或 Cleveland 点图,因为人眼对角度大小的敏感性不如长度。我们应该尽量避免用饼图,无论我们多熟悉它。不过这种巧妙的设计却可以被用在其它场合,如提灯女士的玫瑰图(\@ref(sec:nightingale) 小节); 14 | 15 | 1. 尽量避免 3D 图形,除非它是可以交互操作的。三维立体图形看起来很炫很时髦,但它的缺点也是很明显的 — 对于静态的三维图形,我们在二维媒介上只能看到它的一个侧面,这样可能会隐藏一些数据信息。大多数情况下,三维图形可以被等高线代替(\@ref(sec:contour) 小节),后者可以让我们从平面上看到所有信息;如果需要使用三维图形,可以考虑 **rgl** 包等动态图形系统(\@ref(sec:rgl) 小节),这样我们的图形可以任意旋转角度、缩放; 16 | 17 | 1. 大多数情况下,将连续数据分组是很糟糕的数据处理方式,例如将年龄数据分为 0-10 岁、 10-20 岁等,这一点在作图的数据预处理中尤其常见,我们应该尽量避免这种损失数据信息的处理方式,不能为了作图而作图:即让图形去配合数据,而不能让数据去配合图形; 18 | 19 | 1. 设计统计图形需要综合考虑数据处理、统计模型和用户心理等,任何一个环节上的失误或偏颇,都可能使图形带来相反的作用 — 不仅不能展示数据中的信息,反而带来误导。“一图胜千言”也有其前提。有些读者可能还记得 1986 年美国挑战者号航天飞机的那场灾难,灾难的原因只是因为飞机上一种小零件“O 型环”出了故障,这个故障本来可以用更合适的图形揭示出来,但一幅错误的图形误导了科学家们,这个误导间接促成了发射的决策,最终的代价是航天飞机在发射后 73 秒解体、机上 7 名宇航员全部罹难; 20 | 21 | 1. 数学只是研究统计学的一种角度,它奠定了统计学的理论基础,但这不是统计学的终点或全部; 22 | 23 | 24 | 本书的所有图形文件和程序代码可以从 Github 仓库下载(),另外本书也配有相应的 R 包 **MSG** [@MSG], 使用说明参见附录 \@ref(chap:MSG)。阅读本书过程中若有任何疑问请 Email 联系作者:。 25 | 26 | 关于本书中 R 程序代码,记号说明如下: 27 | 28 | `>` 29 | : 表示一段 R 程序的开始;在 R 中可以用 `options(prompt = '> ')` 设置程序行的起始符号,默认为“> ”;后文中只要遇到这个标记,则说明该标记之后的程序语句可以直接在 R 中运行(读者在运行书中的示例时不要把这个符号也敲进命令行中) 30 | 31 | `+` 32 | : 续行符;当一段程序在某一行中没有完整显示出来时,就会折到下一行,此时 R 会以“+”表示程序语句上不完整,正在继续 33 | 34 | `[n]` 35 | : 其中“n”表示一个整数,中括号括上一个整数表示 R 程序输出的行号,比如 [1] 表示这是第 1 行输出 36 | 37 | `#` 38 | : 表示 R 程序注释,即不会被执行的语句(为了增强程序的可读性而写的文字) 39 | 40 | 电子版读者可以用按住 Alt 键再用鼠标选中除去 > 和 + 的代码。另外,本书 R 代码以等宽正体排版,代码中带有程序起始符以及续行符,并且用不同颜色分别标记函数、参数、数字、字符和注释等部分;代码输出则不带高亮,例: 41 | 42 | ```{r} 43 | library(MSG) # 加载本书配套的 R 包 MSG 44 | data(PlantCounts) # 加载数据供下面的回归模型使用 45 | summary(fit <- lm(counts ~ altitude, data = PlantCounts)) 46 | ``` 47 | 48 | 正文中的代码以等宽正体表示,如 `inline R code`,函数名称以斜体表示,如 `function()`,对象类名称和参数名称用无衬线字体表示,如 `class using sans serif`,R 程序包用粗体表示,如 **package**。 49 | 50 | 本书以 LaTeX 结合 R (Sweave,参见 @Leisch02 )写成,所有图形均为 R 代码动态生成,因此整本书稿具有可重复性(reproducible),读者可从网上下载书稿源代码并配合适当的工具编译生成本书稿。本书的大部分图形都直接附带有源代码,少数图形的源代码被放在 **MSG** 包的演示中,当代码为 `demo('topic', package = 'MSG')` 形式时,读者在 R 中运行这句代码就可以看到源代码和相应的图形输出。当我们介绍一个函数的用法时,通常会使用一个 `usage()` 函数,它在 R 包 **formatR** 中。 51 | 52 | 顾炎武在《日知录》中引用过《易经》中的一句话:“形而上者谓之道,形而下者谓之器。”对本书来讲,统计作图的(计算机)技术本身即为“器”,而数据处理以及统计图形的灵活应用则为“道”。本书的写作目的正是希望能够基于“器”的练习和启发,让读者在统计数据处理和分析中真正得“道”,使统计图形在数据的探索分析中发挥福尔摩斯探案般的功效。 53 | 54 | ```{block2, type = 'flushright', html.tag = 'p'} 55 | 谢益辉 56 | 于美国奥马哈 57 | ``` 58 | -------------------------------------------------------------------------------- /gui.Rmd: -------------------------------------------------------------------------------- 1 | # 图形界面 {#cha:GUI} 2 | 3 | 虽然 R 自身几乎没有图形用户界面(GUI),但 CRAN 中存在若干用来生成 GUI 的附加包,例如 **RGtk2** 和 **gWidgets**,R 基础包中也有 **tcltk** 可以制作简单的图形界面。根据作者自身的经验,**gWidgets** 是最易学的一个附加包。尽管 **tcltk** 包由 R 自带、用户不必额外安装,但它的图形界面相对简陋,帮助文档也不够健全,而且个人认为 tcl/tk 界面不够美观,尽管如此,还是有很多包都基于 **tcltk** 包来创建界面,比如 John Fox 的 **Rcmdr** [@Rcmdr] 包就是一个比较成熟和著名的 R 用户界面;**RGtk2** 包的功能非常完善,但用它构建界面也经常让人觉得过于繁琐。相比之下,**gWidgets** 则是在抽象程度和功能之间有着很好的平衡的一个附加包,因此这里我们着重介绍它。 4 | 5 | **gWidgets** 包本身只是一个抽象框架,它需要附着在一个具体的界面包上才能生成具体界面元素,这也是它的灵活性所在:使用 **gWidgets** 包创建图形界面时,我们可以不必关心最终用哪种界面实现,这些界面与创建界面的代码是分离的。对 **gWidgets** 包而言,它至少有两种界面实现:GTK+ 界面和 tcl/tk 界面,分别对应着 **gWidgetsRGtk2** 包和 **gWidgetstcltk** 包(历史上曾经有过基于 **gWidgetsrJava** 包的 Java 界面但后来作者不再维护这个包了)。由于 GTK+ 界面看起来美观一些,这里我们只以 GTK+ 界面为例,但创建 GUI 的代码几乎可以不作任何修改转化为 tcl/tk 界面(有时候会有细微差别)。首先我们需要安装并加载 **gWidgetsRGtk2** 包: 6 | 7 | ```{r gwidgets-install,eval=FALSE} 8 | install.packages("gWidgetsRGtk2") 9 | library(gWidgetsRGtk2) 10 | options(guiToolkit = "RGtk2") # 这项设置可省略 11 | # 如果要创建 tcl/tk 界面则需要安装 gWidgetstcltk 并设置 options(guiToolkit='tcltk') 12 | ``` 13 | 14 | 创建图形界面有三大关键要素:一是界面控件,如按钮、下拉框、文本框等,二是这些控件的位置安排,或者嵌套关系,三是事件,因为控件通常是用来响应一些用户事件的,例如鼠标点击或键盘动作等。这些要素在 gWidgets 的框架下非常简易明了,下面我们以一个最简单的例子说明这些要素: 15 | 16 | ```{r gwidgets-simple-demo,eval=FALSE} 17 | gw <- gwindow("Hi Window!") # 创建一个窗口,它是所有控件的载体 18 | gb <- gbutton("Open", container = gw, handler = function(h, ...) { 19 | gfile("Open a file", type = "open") 20 | }) 21 | ``` 22 | 23 | 首先我们用 `gwindow()` 函数创建了一个窗口,几乎所有的界面都需要有一个窗口来装载图形控件,这个函数的第一个参数是窗口的标题;接下来我们用 `gbutton()` 创建一个按钮,注意此时我们指定这个按钮被装在刚才创建的窗口对象中,这通过 `container` 参数实现,`gbutton()` 的第一个参数为按钮上的文本;在创建按钮时我们也绑定了一个事件在它上面,即 `handler` 参数,这个参数的取值为一个函数,上例中的函数格式为这个参数的固定取值格式(第一个参数为 `h`,剩下的参数为 `...`),函数用来执行事件,比如这里如果我们点击按钮,那么触发的事件将是弹出一个打开文件的对话框,它通过 `gfile()` 函数实现。在真实的应用中,我们一般需要对打开的文件进行进一步操作,这个文件的地址可从 `gfile()` 的返回值中得到。 24 | 25 | **gWidgets** 包中的控件包括: 26 | 27 | `gwindow()` 28 | : 窗口 29 | 30 | `gbutton()` 31 | : 按钮 32 | 33 | `gedit(), gtext(), glabel()` 34 | : 单行文本框和多行文本框,标签(它只能显示文本,不可编辑) 35 | 36 | `gcheckbox(), gcheckboxgroup(), gradio()` 37 | : 复选框、复选框组和单选框 38 | 39 | `gcombobox(), gdroplist()` 40 | : 下拉框(前者允许用户自行输入选项,后者只能从给定列表中选择) 41 | 42 | `gfile(), gfilebrowse(), gcalendar()` 43 | : 文件对话框和日历控件 44 | 45 | `gmessage(), ginput(), gconfirm(), galert()` 46 | : 消息对话框、输入对话框、确认对话框和警告对话框 47 | 48 | `gdf(), gdfnotebook()` 49 | : 可编辑的数据表控件(后者的界面更丰富) 50 | 51 | `gtable()` 52 | : 不可编辑的数据表控件 53 | 54 | `ggroup()` 55 | : 组合控件,它没有具体外观,但可以作为其它控件的载体,将其它控件组合在一起 56 | 57 | `gframe()` 58 | : 框架,可选择性地带文本标签 59 | 60 | `gmenu(), gtoolbar(), gstatusbar()` 61 | : 菜单、工具栏和状态栏 62 | 63 | `ggraphics()` 64 | : 图形控件,可将 R 图形嵌入界面中(依赖于 **cairoDevice** 包) 65 | 66 | `gimage()` 67 | : 图片控件,可显示任意本地图片文件 68 | 69 | `gnotebook()` 70 | : 标签页,或选项卡,包含若干子页面,顶部为页面名称,页面之间可以来回切换显示 71 | 72 | `gslider(), gspinbutton()` 73 | : 滑动条和计数按钮,前者可由鼠标拖动滑杆改变控件的值,后者通过鼠标点击按钮上的上下或左右箭头来逐步改变值 74 | 75 | `gtree()` 76 | : 树型控件(展示树型嵌套结构) 77 | 78 | 大部分控件都有其取值,可以用函数 `svalue()` 提取或改变。例如文本框控件的取值就是其中的文本,我们也可以通过 `svalue()` 改变文本框中的文本: 79 | 80 | ```{r gwidgets-svalue-demo,eval=FALSE} 81 | gtxt <- gedit("initial text", container = TRUE) 82 | svalue(gtxt) # 返回 'initial text' 83 | svalue(gtxt) <- "changed text" # 文本框中的文本被更新 84 | ``` 85 | 86 | 类似地,我们也可以用 `enabled()` 和 `visible()` 分别控制控件的可用性(不可用时控件通常变为灰色)和可见性。我们还可以用一系列 `addHandlerXXX()` 函数来事后往控件上添加事件,例如 `addHandlerClicked()` 可以添加鼠标点击事件,等等。 87 | 88 | 特别值得一提的是,gWidgets 和 R 的数据结构融合得非常好,对于熟悉 R 数据结构的用户来说,这些控件甚至可以直接当作 R 对象使用。典型的例子如数据表控件,我们可以用方括号从控件中取值: 89 | 90 | 91 | ```{r gwidgets-gtable-demo,eval=FALSE} 92 | gtb <- gtable(iris, container = TRUE) 93 | gtb[1, 1] # 表中第 1 行第 1 列的值 94 | gtb[1:10, 1:2] # 前 10 行前 2 列的值 95 | ``` 96 | 97 | 最后我们展示一个简单的应用实例:在 \@ref(sec:color) 小节中我们介绍了 R 中的颜色生成机制,其中有一种就是通过红绿蓝三原色混合生成颜色,我们可以利用 gWidgets 创建一个简易图形界面来动态实现这种混合,最终效果如图 \@ref(fig:color-selector)。 98 | 99 | ```{r gwidgets-color-selector,eval=FALSE} 100 | library(gWidgetsRGtk2) 101 | g <- ggroup(horizontal = FALSE, container = gwindow("Color Selector")) 102 | x <- c(0, 0, 0) # 红绿蓝颜色成分向量 103 | for (i in 1:3) { 104 | gslider(from = 0, to = 1, by = 0.05, action = i, handler = function(h, ...) { 105 | x[h$action] <<- svalue(h$obj) 106 | par(bg = rgb(x[1], x[2], x[3]), mar = rep(0, 4)) 107 | plot.new() 108 | }, container = g) 109 | } 110 | ggraphics(container = g, width = 200, height = 100) 111 | ``` 112 | ```{r color-selector,echo=FALSE,fig.cap="(ref:color-selector)",fig.scap="(ref:color-selector-s)",fig.subcap=c("",""),out.width="45%"} 113 | knitr::include_graphics(path = c( 114 | "images/gwidgets-color-selector1.png", 115 | "images/gwidgets-color-selector2.png" 116 | )) 117 | ``` 118 | 119 | (ref:color-selector-s) 用 **gWidgets** 制作的颜色混合器 120 | 121 | (ref:color-selector) 用 **gWidgets** 制作的颜色混合器:调节红绿蓝三原色的成分可以获得新的混合色。 122 | 123 | 124 | 上面的代码中,首先我们用 `ggroup()` 来排版布局,主要是让控件以纵向方向排列,因为默认情况下新控件会横向排列;接着我们用循环创建三个滑动条分别代表红绿蓝,它们的取值范围为 0 到 1,滑动一步时值的变动为 0.05,其中绑定的事件为画空白图,图的背景按照向量 x 的三个成分由 `rgb()` 函数混合而成,这里我们使用了一点新概念,即 `action` 参数,它的意义稍微有点曲折:`action` 参数将来会传给 `handler` 参数中的函数,具体传递过程是,它的取值会被放在 `h` 参数的 `action` 子元素中,也就是我们可以用 `h$action` 来提取传入的值;循环中三个滑动条的 `action` 值分别为 1、2、3,而 `handler` 的函数中 `h$obj` 可以用来调用控件本身,所以用 `svalue()` 就可以提取该控件当前的值,并赋给 x 中的第 i 个元素,这里的赋值符号用的是双箭头 `<<-`,原因是在普通的赋值符下(等号 `=` 或者单箭头 `<-`),R 的变量作用域会使得函数内部对变量的修改只是局部的修改,外部的变量值不会改变,但双箭头可以从内部改变外部变量的值。这样,每次我们拖动第 i 个滑动条时,触发的事件是 x 中的第 i 个值被修改为滑动条的值,然后带背景色的空白图形会根据新的颜色被重画。最后我们使用了图形控件 `ggraphics()` 将 R 图形嵌入当前的图形界面,一个完整的颜色混合器界面就完成了。图 \@ref(fig:color-selector) 中左图显示的是 50\% 的红色配 50\% 的蓝色,结果是紫色;右图是 70\% 红、100\% 绿和 10\% 蓝混合的结果。 125 | 126 | 通过以上介绍,我们相信在 R 中创建 GUI 不再是难事。在日常工作中,偶尔写一个简单界面也能为我们的工作增添一些乐趣和便利。**formatR** 包 [@formatR] 就是作者编写的一个用来自动整理 R 代码的图形界面,点一下按钮,文本框中的 R 代码就会被整理整齐(自动添加空格和缩进),更多介绍参见 。另外,图形界面也是让图形发挥更大功效的有力工具,如 GGobi 软件若离开了它的图形界面,可能会失色不少。 127 | 128 | 129 | -------------------------------------------------------------------------------- /history.Rmd: -------------------------------------------------------------------------------- 1 | # 历史 {#cha:history} 2 | 3 | > “这易如反掌,”他说,“我看到你左脚穿的那只鞋的内侧,也就是炉火刚好照到的地方,皮面上有六道几乎平行的划痕。显然,这些划痕是有人为了去掉沾在鞋跟上的泥疙瘩,极其粗心大意地顺着鞋跟刮泥而造成的。因此,现在你就明白了我得出的这两个推断:其一,你曾经在恶劣的天气外出过;其二,你穿的皮靴上面的特别难看的划痕是伦敦的女佣所为。至于你开业行医,这么说吧,如果一位先生走进我的房间,身上带有碘的气味,右手食指上有硝酸银腐蚀的黑斑,高顶黑色大礼帽的右侧鼓起一块,那里面藏着听诊器,而我不断言他是医务界的一位活跃分子,那我不是太迟钝了吗? 4 | > 5 | > --- 柯南·道尔《波希米亚丑闻》 6 | 7 | 统计图形的意义在于引导我们观察到统计数据中的信息。用著名统计学家 John Tukey 的话来讲,就是“图形的最大价值就是使我们注意到我们从来没有料到过的信息”(The greatest value of a picture is when it forces us to notice what we never expected to see)。从这个意义上讲,统计图形的重要性自然不言而喻。 8 | 9 | 在统计图形历史上,能够达到“揭示人们不曾料到的信息”这种高度的图形并不多,那么这里我们首先欣赏几幅前人创造出的名垂青史的统计图形。 10 | 11 | ## 饼图和线图的起源 {#sec:pie-and-line-of-history} 12 | 13 | 饼图和线图都是当今社会中常用的统计图形,它们是由有着“统计图形奠基人”之称的苏格兰工程师兼政治经济学家 William Playfair 发明的。在 “The Commercial and Political Atlas” [@Playfair86] 一书中,他用线图展示了英格兰自 1700 年至 1780 年间的进出口数据(如图 \@ref(fig:playfair86)),从图中可以很清楚看出对英格兰有利和不利(即顺差、逆差)的年份;而在 “The Statistical Breviary” [@Playfair01] 一书中,他第一次使用了饼图来展示一些欧洲国家的领土比例,图 \@ref(fig:playfair01) 即为史上第一例饼图。从下方的饼图中我们可以清楚看出当时的土耳其帝国分别在亚洲、欧洲和非洲的领土面积比例。这两幅图在今天看来似乎没有什么惊世骇俗之处,但在当时统计图形种类极为稀少的年代,能以这种方式清晰展示数据结构,也实属难能可贵。除了这两种图形之外,他还发明了条形图和圆环图。 14 | 15 | ```{r playfair86, fig.scap = "William Playfair 的时序线图 ",fig.cap="(ref:playfair86)",echo=FALSE} 16 | knitr::include_graphics(path = "images/Playfair-TimeSeries.png", dpi = NA) 17 | ``` 18 | 19 | (ref:playfair86) @Playfair86 绘制的线图。 这幅图主要展示了 1700 年至 1780 年间英格兰的进出口时序数据,左边表明了对外贸易对英格兰不利,而随着时间发展,大约 1752 年后,对外贸易逐渐变得有利。图片来源: 20 | 21 | ```{r playfair01, fig.scap = "William Playfair 的饼图 ",fig.subcap=c("",""),fig.cap="(ref:playfair01)",echo=FALSE,fig.ncol=1} 22 | knitr::include_graphics(path = c("images/Playfair-pie1.png", "images/Playfair-pie2.jpg"), dpi = NA) 23 | ``` 24 | 25 | (ref:playfair01) @Playfair01 绘制的饼图。这是历史上最早出现的饼图,描述了法国大革命前后一些欧洲国家的统计数据。上方的大图展示了各个国家的领土面积(和圆圈成比例)以及人口(左垂线)、税收(右垂线)、国土在各大洲分布比例等数据,两条垂线连线的斜率可表示税负的轻重(这一点颇有争议,因为斜率与圆的半径有关)。下方的饼图展示了土耳其帝国在三大洲的国土面积分布。图片来源: 26 | 27 | 28 | 29 | 30 | ## 霍乱传染之谜 {#sec:the-mystery-of-cholera-infection} 31 | 32 | 袭击欧洲大城市最严重的天灾要数 19 世纪的霍乱。由于垃圾没有得到及时清理,清洁水源的缺少,以及下水管道系统的不足,伦敦成为无药可医的流行病滋生的最佳地点。公众一致认为霍乱是由空气传播的,如果呼吸到了“瘴气”或者接触到霍乱患者,就会染上这种病。医生兼自学成才的科学家 John Snow 对这个观点颇为怀疑,他决心通过彻底调查这种致命疾病的根源来证实他的怀疑。 33 | 34 | 通过和当地居民交谈,他确定了霍乱爆发的源头是位于 Broad 大街的公共水泵。他对这种疾病类型的研究看起来很可信,因此他成功说服了当地政府废弃那个水泵。他所利用的主要证据就是图 \@ref(fig:snow-cholera):死亡发生的地点有明显的地理规律,在这种规律的指引和相关调查证据的支持下,他最终确定了霍乱的源头。后来证实离这口井仅三英尺远的地方有一处污水坑,坑内释放出来的细菌正是霍乱发生的罪魁祸首。 35 | 36 | ```{r snow-cholera,fig.scap="John Snow 的霍乱传染原因探索图",fig.cap="(ref:snow-cholera)", fig.link="https://en.wikipedia.org/wiki/John_Snow_(physician)",echo=FALSE} 37 | knitr::include_graphics(path = "images/Snow-cholera-map.jpg", dpi = NA) 38 | ``` 39 | 40 | (ref:snow-cholera) 1854 年英国 Broad 大街大规模爆发霍乱,当时了解微生物理论的人很少,人们不清楚霍乱传播途径,而“瘴气传播理论”是当时的主导理论;John Snow 对这种理论表示了怀疑,于 1849 年发表了关于霍乱传播理论的论文,本图即其主要依据。图中心东西方向的街道即为 Broad 大街,黑点表示死亡的地点,黑点叠加的高度相应表示了该处死亡人数。这幅图形揭示了一个重要现象,就是死亡发生地都在街道中部一处水源(水井)周围,市内其它水源周围极少发现死者。进一步调查他发现这些死者都饮用过这里的井水 41 | 42 | ## 提灯女士的玫瑰图 {#sec:nightingale} 43 | 44 | 南丁格尔(Florence Nightingale)是我们耳熟能详的“提灯女士”,她不仅是现代护理的鼻祖及现代护理专业的创始人,而且是历史上使用极坐标面积图的先驱。这种图形外形如玫瑰,因此后来也称之为玫瑰图,其主要构思是用“花瓣”的面积表示统计数值的大小。图 \@ref(fig:nightingale-mortality) 反映了克里米亚战争(英国等与俄国争夺巴尔干半岛的战争)中英国军队自 1854 年 4 月至 1856 年 3 月的逐月死亡人数 [@Nightingale58];其中,右图为 1854 年 4 月至 1855 年 3 月的死亡人数,左图为 1855 年 4 月至 1856 年 3 月的死亡人数。玫瑰图不仅清楚展示了这两年军队死亡人数的变化,而且更重要的是,她将每个月中三种死亡情况也分别用不同颜色标记出来:蓝色表示死于可预防的疾病、红色表示死于战争伤害、黑色表示死于其它原因。这样我们可以清楚知道军队伤亡原因的结构,尤其是“绝大多数士兵死于可预防的疾病”(图中最高的花瓣)。凭借这一条重要信息,她让英国政府意识到,真正影响战争伤亡的并非战争本身,而是由于军队缺乏有效的医疗护理! 45 | 46 | ```{r nightingale-mortality,fig.scap="(ref:nightingale-mortality-s)",fig.cap="(ref:nightingale-mortality)",fig.link="",echo=FALSE} 47 | knitr::include_graphics(path = "images/Nightingale-mortality.jpg", dpi = NA) 48 | ``` 49 | 50 | (ref:nightingale-mortality-s) Florence Nightingale 的极坐标面积图 51 | 52 | (ref:nightingale-mortality) 南丁格尔的极坐标面积图:两幅图分别是 1854 年和 1855 年的军队伤亡人数,一年 12 个月恰好可以将极坐标分为 12 等分,每一瓣代表一个月。图中用颜色标记出了三种死亡原因。南丁格尔的重大贡献在于使得英国政府意识到真正影响战争伤亡的并非战争本身,而是由于军队缺乏有效的医疗护理,导致大量的士兵死于可预防的疾病。1857 年,在她的努力下,英国皇家陆军卫生委员会成立。同年,军医学校成立。 53 | 54 | ## 拿破仑的俄罗斯远征 {#sec:charles-joseph-minard} 55 | 56 | (ref:minard) Minard 绘制的地图,展现了 1812 年拿破仑的大军团进军俄国的路线(上半部分)和撤退时的气温变化(下半部分)。这一历史事件中,法军数量的急剧减少以及恶劣的气候条件一览无遗,法国科学家 Étienne-Jules Marey 称“该图所展现出的雄辩对历史学家的笔是一种极大的挑战” 57 | 58 | ```{r minard, fig.scap="Charles Joseph Minard 的拿破仑远征图 ",fig.cap="(ref:minard)",fig.link="https://en.wikipedia.org/wiki/Charles_Joseph_Minard",echo=FALSE} 59 | knitr::include_graphics(path = "images/Minard.png", dpi = NA) 60 | ``` 61 | 62 | 1812 年 6 月 24 日,拿破仑率领的 691,501 人的大兵团---同时也是欧洲历史上集结的最大规模的部队---开赴莫斯科。但等他们到达那里,看到的只是一座空城。城里的人都被遣散,所有的供给也被中断。由于没有正式的投降,拿破仑觉得俄国人从他那儿剥夺了一场传统意义上的胜利。 63 | 64 | 军队不得不撤退。在归程中,因为天气过于恶劣,给军队提供补给几乎是不可能的。马匹因为缺少粮草而变得虚弱,所有的马要么饿死,要么被饥饿的士兵拿去果腹。没有了坐骑,法国骑兵们成了步兵,大炮和马车被迫丢弃,部队没了装甲。饥饿与疾病带来惨重的伤亡,而逃兵数目也直线上升。大军团的小分队在 Vyazma,Krasnoi 和 Polotsk 也被俄国人击溃。法国军队在渡贝尔齐纳河时遭到俄军两面夹击,伤亡惨重,这也是法军在俄国遭遇的最后一场灾难。1812 年 12 月 14 日,大军团被驱逐出俄国领土。在这场远征俄罗斯的战役中,拿破仑的士兵只有大约 22,000 人得以幸存。 65 | 66 | 这一历史事件被 Charles Joseph Minard 用一张二维平面图形记录了下来,Minard 是一位法国工程师,他以在工程和统计中应用图形而闻名。图 \@ref(fig:minard) 就是他的著名作品:在一张二维图形中,他成功地展示了如下信息: 67 | 68 | - 军队的位置和前进方向,以及一路上军队的分支和汇合情况 69 | - 士兵数目的减少(图形顶端最粗的线条表示最初渡河的 422,000 人,他们一路深入到俄国领土,在莫斯科停下来的时候还有 100,000 人左右。从右到左,他们朝西走回头路,渡过 Niemen 河的时候,仅仅剩下 10,000。随着大部队和余部会师(比如在渡贝尔齐纳河之前),图中显示的数字降中也有升) 70 | - 撤退时的气温变化(参见图的下半部分,可知当时气候条件极其恶劣) 71 | 72 | 这幅图形在统计图形界内享有至高无上的地位,被 Edward Tufte 称为“有史以来最好的统计图形”(Tufte 是统计图形和信息可视化领域的领军人物,人称“数据达芬奇”)。 73 | 74 | ## 小结与开始 {#sec:begin} 75 | 76 | 在前面四节中,我们看到了具有历史意义的几幅统计图形,它们融入了前人的智慧与艺术,有些甚至具有重大社会价值。当然我们不能苛求每一幅统计图形都能达到那样的效果,但至少我们了解到了统计图形在揭示特殊现象或规律上的功能,这种功能是数据本身不能替代的。试想,若只是将每一个霍乱死者的数据列在纸上,那么要观察出霍乱发生的规律是何其艰难。 77 | 78 | 统计图形领域还有大批卓有成就的研究者,为统计图形的发展做出了不少贡献。在上个世纪八九十年代甚至更早,国外已经有比较全面的图示书籍文献资料,如前文提到的“数据达芬奇” Tufte,他的著作如 @Tufte92 和 @Tufte01 在可视化领域有非常深远的影响,他本人于 2010 年被奥巴马政府聘请加入“经济复苏独立咨询小组”(Recovery Independent Advisory Panel),从这一点可见他的成就和威望;但如果说统计图形有一位启蒙思想家的话,那么恐怕非 Tukey 莫属了,@Tukey1977 提出来的探索性数据分析可以说在当时引领了一个统计学的新方向,在数理统计为主导的统计界注入了一股新活力,探索性数据分析的主要工具就是统计图形,注意 Tukey 本人的数学功底极好,这一点从若干著名统计学家的回忆录中都可以找到证据(统计学刊物 Statistical Science 每一期的最后都有一篇采访,本人在这些采访中时常见到 Tukey 被提及),但从他的一些论文著作中我们可以看到他非常重视数据分析,@Tukey62 就是一篇很好的例证(他认为数学不是一门科学,而数据分析则是);Tukey 常常体现在一些细节之处展现他的观察力,令人不得不感到佩服,例如 @Wainer81 中提到了一件事:过去人们常用斜线计数,$\text{/}$ 表示 1,$\text{//}$ 表示 2,……,达到 4 条线($\text{////}$)之后紧接着用 $\bcancel{\text{////}}$ 表示 5,但 Tukey 认为这样很容易出错,比如要是在 3 条线之后不小心早划了表示 5 的斜线 $\bcancel{\text{///}}$,或者在 5 条线之后才画那条反方向的斜线 $\bcancel{\text{/////}}$,都将造成难以修复的错误,因此他提出了一种新的计数方法:先用正方形的四个顶点分别表示 1、2、3、4,到 4 点的时候就开始连边线,每连一条线就表示数字增加 1,这样 4 条边都连好之后就可以表示到 8 了,最后分别连对角线表示 9 和 10,即 $\boxtimes$ 表示 10,用这种计数法则不必担心画错线,因为不管连哪条线都是表示 1,其实细心的中文读者马上能联想到我们的“正”字计数法,这个计数法比 Tukey 的方法更具有稳健性;统计图形一直以来都因为缺乏像数学那样的理论而受人诟病,关于这一点,@Wilkinson05 则给出了一个很好的框架,它也是 R 包 **ggplot2** 的理论基础;贝尔实验室的的 Cleveland 在图形认知方面做了不少工作,告诉我们应该怎样合理构建图形以及解读图形,如 @Cleveland85 和 @Cleveland93 等,其中值得一提的是他可能是最早研究统计图形对读者心理感知的影响的统计学家之一,但不幸的是,这项工作似乎并没有引起人们的广泛重视(饼图直至今日仍然泛滥便是一个最好的例证),另外他提出了 S 语言中的 Trellis 图形,这对统计图形软件的发展来说也是具有划时代意义的贡献,后来 R 语言中的 **lattice** 包正是继承了 Trellis 图形的概念,近些年来也非常有影响力。关于统计图形的历史总结,@Friendly01 是一份非常详尽的资料,该文档整理、记载了自 17 世纪以前至今数百年历史中较有影响力的统计图形。 79 | 80 | 近代统计图形以 @Tukey1977 的探索性数据分析为里程碑式的起点,诞生了大批具有数理统计意义和计算机应用的图形著作和图形种类,如我们熟知的箱线图 [@McGill78],LOWESS 曲线 [@Cleveland79],直方图和密度曲线 [@Scott92],基于 S 语言的著作 [@Chambers83] 以及注重表达信息的著作(如前文介绍的 Tufte)等;现代统计图形的发展则更偏重计算机工具的开发以及高维图形和动态图形的展示,其中 S 语言 [@Becker88] 为现代统计图形的发展奠定了重要的基础,随后 R 语言 [@Ihaka96;@base] 的兴起,更是带来了数不胜数的统计图形方法,比较有代表性的如 R 语言的基础包 **graphics** 包和 **grid** 包 [@Murrell05]、基于 Trellis 图形 [@Cleveland93] 思想的 lattice 图形 [@Sarkar08]、基于统计图形理论著作 @Wilkinson05 的 ggplot2 图形 [@ggplot2]、基于动态图形 GGobi 软件 [@Cook07] 的高维数据交互图形实现 **rggobi** 包 [@rggobi]、基于 OpenGL 的三维动态图形系统 **rgl** 包 [@rgl] 和分类数据图示的 **vcd** 包 [@vcd] 等,此外,还有一批新的高维图形思想被提出,如打破笛卡尔坐标系常规的平行坐标图 [@Inselberg07],并出现了一些 R 语言之外的独立交互图形软件如用于分析缺失值的 MANET 软件 [@Unwin96] 和交互式图形分析软件 Mondrian [@Theus02] 等,这些动态图形和交互图形的综述可参考 @Jurgen04。 81 | 82 | 如今统计图形的使用看似已经比较普遍,饼图、条形图都已不是什么新鲜内容,人人都能做,但是,一方面统计图形的价值并没有被很好地体现出来,另一方面人们对统计图形的了解也被统计软件所限,而不能随心创造图形。我们来看这样一组事实 [@Xie08]: 83 | 84 | > 以期刊《统计研究》在 2006 年 12 月 \~ 2007 年 11 月期间共 12 个月的所有论文作为统计对象,剔除部分非学术研究型论文之后,挑选论文总数为 168 篇,其中使用表格的论文篇数为 136 篇(81.43\%),表格总数为 528 个,而使用图形的论文仅有 63 篇(37.72\%),若将仅仅使用示意图(非统计图形)、条形图和折线图的论文排除在外,使用其它图形的论文仅剩下 9 篇。 85 | 86 | 这可算国内统计图形应用现状的一个缩影。为了改变这种局面、发掘出统计图形在数据分析中应有的潜力,我们特别撰写这本书,供广大统计研究者参考。我们的目的并非仅限于如何作出漂亮的统计图形,而是在作图的同时,强调图背后更重要的工作,那就是“数据分析与统计图形的有机结合”。传统的统计分析大约可以分为三类: 87 | 88 | - 描述性统计分析:Descriptive Statistical Analysis 89 | - 推断性统计分析:Inferential Statistical Analysis 90 | - 探索性统计分析:Exploratory Statistical Analysis 91 | 92 | 前两类统计分析往往都是从既定的统计模型、方法的角度入手,而探索统计分析则主要借助图形对数据进行探索性分析,这对于数据分析的手段是一种重要的拓展(姑且称之为“图形统计分析”);然而要使用这种手段,则必须清楚了解如何制图以及现有图形有哪些种类,这样才能真正开发出统计图形的价值。 93 | 94 | 其实,“图形统计分析”也不是一个新概念,平常的统计图示已经或多或少用到了这样的思想,只是我们往往更倾向于数理意义上的统计模型分析,而不会把图形统计分析作为主要分析手段。当然,由于图形的表达限制以及统计图形的普及程度,也使得它不可能替代模型分析,但无论如何,我们对统计图形在统计分析中的地位应该加深认识,不仅是因为这是一个信息爆炸的时代、大量的信息让我们无法在短时间内获取核心信息,更重要的是,目前在国内仍有大量的统计图形未被开发介绍出来,图形种类过于单一,表达信息的效果大打折扣。 95 | 96 | 本书介绍统计图形的方式主要是从两方面入手,第一,阐明各种统计图形所用到的统计量;第二,与实例结合,解释图形中表现的统计量的实际含义。在本书的附录 \@ref(cha:tricks) 中,我们也会介绍一些有用的细节和作图技巧,用以辅助完善统计图形。 97 | 98 | 总的说来,要把图形提到“统计分析”的高度,就一定要搞清楚统计图形的来龙去脉,包括原始数据的来源和类型、统计量的计算、图形的构造与组合机制等,这与统计模型实际上没有本质的区别:若不清楚模型的假设前提、计算原理以及相应的结果解释,同样也不能随便使用模型分析。除了图形本身之外,用好图形分析还需要一定的洞察力,最简单的莫过于观察数据的分布状况、离群点、线性/非线性关系等表面观察,而更重要也是最本质的莫过于洞察到种种规律或异常现象背后的深刻原因,至此,我们才达到了分析的目的。 99 | 100 | ## 思考与练习 101 | 102 | 1. 图 \@ref(fig:playfair86) 的主旨是用来刻画贸易顺差或逆差,因此把出口额和进口额画在同一幅图上似乎是自然而然的选择,但是我们真正关心的是两条曲线之差。你认为 Playfair 的这幅图是否存在不足、以及应该怎样改进? 103 | 104 | 1. 某种程度上,Tukey 引导了“用数据说话”的潮流,这样的做法有什么潜在危险?换句话说,当我们浏览一幅图形的时候,我们首先要考虑的是数据的来源,如果我们忽略数据的来源而直接去考虑基于图形的发现,所谓的“数据说的话”是什么话? 105 | 106 | 1. 统计图形和统计模型的最大区别在哪里?提示:并非高下之分,而是二者各自的假设是什么。更具体地,我们知道数理统计中的假设检验通常有零假设和备择假设,统计图形更像在研究哪一个假设,而统计模型又通常研究哪一个? 107 | -------------------------------------------------------------------------------- /images/China-Heart-3D.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XiangyunHuang/msg/1c7109c91ddc76b7b4c37e1a934da15f6a512692/images/China-Heart-3D.png -------------------------------------------------------------------------------- /images/China-v-US-graphic.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XiangyunHuang/msg/1c7109c91ddc76b7b4c37e1a934da15f6a512692/images/China-v-US-graphic.jpg -------------------------------------------------------------------------------- /images/GGobi-2d-tour.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XiangyunHuang/msg/1c7109c91ddc76b7b4c37e1a934da15f6a512692/images/GGobi-2d-tour.png -------------------------------------------------------------------------------- /images/GGobi-hp-mpg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XiangyunHuang/msg/1c7109c91ddc76b7b4c37e1a934da15f6a512692/images/GGobi-hp-mpg.png -------------------------------------------------------------------------------- /images/GGobi-main-window.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XiangyunHuang/msg/1c7109c91ddc76b7b4c37e1a934da15f6a512692/images/GGobi-main-window.png -------------------------------------------------------------------------------- /images/GGobi-narrow-brush.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XiangyunHuang/msg/1c7109c91ddc76b7b4c37e1a934da15f6a512692/images/GGobi-narrow-brush.png -------------------------------------------------------------------------------- /images/GGobi-parcoords.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XiangyunHuang/msg/1c7109c91ddc76b7b4c37e1a934da15f6a512692/images/GGobi-parcoords.png -------------------------------------------------------------------------------- /images/GGobi-qsec-wt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XiangyunHuang/msg/1c7109c91ddc76b7b4c37e1a934da15f6a512692/images/GGobi-qsec-wt.png -------------------------------------------------------------------------------- /images/GGobi-scatterplot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XiangyunHuang/msg/1c7109c91ddc76b7b4c37e1a934da15f6a512692/images/GGobi-scatterplot.png -------------------------------------------------------------------------------- /images/Minard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XiangyunHuang/msg/1c7109c91ddc76b7b4c37e1a934da15f6a512692/images/Minard.png -------------------------------------------------------------------------------- /images/Nightingale-mortality.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XiangyunHuang/msg/1c7109c91ddc76b7b4c37e1a934da15f6a512692/images/Nightingale-mortality.jpg -------------------------------------------------------------------------------- /images/Playfair-TimeSeries.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XiangyunHuang/msg/1c7109c91ddc76b7b4c37e1a934da15f6a512692/images/Playfair-TimeSeries.png -------------------------------------------------------------------------------- /images/Playfair-pie1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XiangyunHuang/msg/1c7109c91ddc76b7b4c37e1a934da15f6a512692/images/Playfair-pie1.png -------------------------------------------------------------------------------- /images/Playfair-pie2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XiangyunHuang/msg/1c7109c91ddc76b7b4c37e1a934da15f6a512692/images/Playfair-pie2.jpg -------------------------------------------------------------------------------- /images/RgoogleMaps-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XiangyunHuang/msg/1c7109c91ddc76b7b4c37e1a934da15f6a512692/images/RgoogleMaps-1.png -------------------------------------------------------------------------------- /images/RgoogleMaps-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XiangyunHuang/msg/1c7109c91ddc76b7b4c37e1a934da15f6a512692/images/RgoogleMaps-2.png -------------------------------------------------------------------------------- /images/Snow-cholera-map.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XiangyunHuang/msg/1c7109c91ddc76b7b4c37e1a934da15f6a512692/images/Snow-cholera-map.jpg -------------------------------------------------------------------------------- /images/canabalt-screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XiangyunHuang/msg/1c7109c91ddc76b7b4c37e1a934da15f6a512692/images/canabalt-screenshot.png -------------------------------------------------------------------------------- /images/cc-by-nc-sa.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XiangyunHuang/msg/1c7109c91ddc76b7b4c37e1a934da15f6a512692/images/cc-by-nc-sa.pdf -------------------------------------------------------------------------------- /images/cc-by-nc-sa.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 | -------------------------------------------------------------------------------- /images/disk-usage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XiangyunHuang/msg/1c7109c91ddc76b7b4c37e1a934da15f6a512692/images/disk-usage.png -------------------------------------------------------------------------------- /images/gwidgets-color-selector1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XiangyunHuang/msg/1c7109c91ddc76b7b4c37e1a934da15f6a512692/images/gwidgets-color-selector1.png -------------------------------------------------------------------------------- /images/gwidgets-color-selector2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XiangyunHuang/msg/1c7109c91ddc76b7b4c37e1a934da15f6a512692/images/gwidgets-color-selector2.png -------------------------------------------------------------------------------- /images/html-interface.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XiangyunHuang/msg/1c7109c91ddc76b7b4c37e1a934da15f6a512692/images/html-interface.png -------------------------------------------------------------------------------- /images/pork-price-orig.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XiangyunHuang/msg/1c7109c91ddc76b7b4c37e1a934da15f6a512692/images/pork-price-orig.pdf -------------------------------------------------------------------------------- /images/pork-price-orig.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XiangyunHuang/msg/1c7109c91ddc76b7b4c37e1a934da15f6a512692/images/pork-price-orig.png -------------------------------------------------------------------------------- /images/randomForest-music.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XiangyunHuang/msg/1c7109c91ddc76b7b4c37e1a934da15f6a512692/images/randomForest-music.png -------------------------------------------------------------------------------- /images/rgl-animation19.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XiangyunHuang/msg/1c7109c91ddc76b7b4c37e1a934da15f6a512692/images/rgl-animation19.png -------------------------------------------------------------------------------- /images/rgl-animation4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XiangyunHuang/msg/1c7109c91ddc76b7b4c37e1a934da15f6a512692/images/rgl-animation4.png -------------------------------------------------------------------------------- /images/rgl-beta.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XiangyunHuang/msg/1c7109c91ddc76b7b4c37e1a934da15f6a512692/images/rgl-beta.pdf -------------------------------------------------------------------------------- /images/rgl-beta.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XiangyunHuang/msg/1c7109c91ddc76b7b4c37e1a934da15f6a512692/images/rgl-beta.png -------------------------------------------------------------------------------- /images/rgl-ch4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XiangyunHuang/msg/1c7109c91ddc76b7b4c37e1a934da15f6a512692/images/rgl-ch4.png -------------------------------------------------------------------------------- /images/rgl-china-map.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XiangyunHuang/msg/1c7109c91ddc76b7b4c37e1a934da15f6a512692/images/rgl-china-map.png -------------------------------------------------------------------------------- /images/smoothScatter-ggplot.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XiangyunHuang/msg/1c7109c91ddc76b7b4c37e1a934da15f6a512692/images/smoothScatter-ggplot.pdf -------------------------------------------------------------------------------- /images/smoothScatter-ggplot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XiangyunHuang/msg/1c7109c91ddc76b7b4c37e1a934da15f6a512692/images/smoothScatter-ggplot.png -------------------------------------------------------------------------------- /images/titlepic.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XiangyunHuang/msg/1c7109c91ddc76b7b4c37e1a934da15f6a512692/images/titlepic.pdf -------------------------------------------------------------------------------- /images/worst-graph.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XiangyunHuang/msg/1c7109c91ddc76b7b4c37e1a934da15f6a512692/images/worst-graph.pdf -------------------------------------------------------------------------------- /images/worst-graph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XiangyunHuang/msg/1c7109c91ddc76b7b4c37e1a934da15f6a512692/images/worst-graph.png -------------------------------------------------------------------------------- /images/yihui-name-wordle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XiangyunHuang/msg/1c7109c91ddc76b7b4c37e1a934da15f6a512692/images/yihui-name-wordle.png -------------------------------------------------------------------------------- /includes/data-url.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /includes/ga_script.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 10 | -------------------------------------------------------------------------------- /includes/header.html: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /index.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "现代统计图形" 3 | subtitle: "Modern Statistical Graphics" 4 | author: 5 | - 赵鹏 6 | - 谢益辉 7 | - 黄湘云 8 | date: "`r Sys.Date()`" 9 | site: bookdown::bookdown_site 10 | documentclass: book 11 | bibliography: 12 | - book.bib 13 | - MSG-packages.bib 14 | - Modern-Stat-Graphics.bib 15 | biblio-style: plainnat 16 | natbiboptions: "authoryear,square" 17 | link-citations: yes 18 | graphics: yes 19 | mathspec: yes 20 | lot: yes 21 | lof: yes 22 | classoption: "UTF8,twoside" 23 | papersize: b5 24 | colorlinks: yes 25 | geometry: 26 | - tmargin=2.5cm 27 | - bmargin=2.5cm 28 | - lmargin=3.5cm 29 | - rmargin=2.5cm 30 | description: "现代统计图形书稿" 31 | subject: "统计图形, 数据可视化" 32 | keywords: 33 | - R 语言 34 | - 统计图形 35 | - 统计学 36 | - 图形元素 37 | - 图形系统 38 | - 统计图形软件 39 | - 数据可视化 40 | github-repo: XiangyunHuang/msg 41 | url: 'https\://bookdown.org/xiangyun/msg/' 42 | --- 43 | 44 | 45 | # 推荐语 {#welcome .unnumbered} 46 | 47 | 互联网技术革命对统计学之影响是向人类社会量化解析自然生态和社会生态的深度进军,广义统计是一个深入发展统计思想期待的重心,其中,最为突出的具有龙头发展地位的是广义统计系统数据信息的可视化,它是当今人类社会发展在互联网平台化所有领域中实现认知、社会学习、分析研究、系统应用扁平化的科学手段。面对可视化的“现代统计图形”,可以适应任何人所拥有知识起点之认知学习,激发社会化学习研究之创新,对所有学科知识交叉应用可以升起不落的“发展力太阳”。对此,《现代统计图形》做出了杰出的贡献。 48 | 49 | ```{block2, type = 'flushright', html.tag = 'p'} 50 | 赵彦云 51 | 中国人民大学统计学院教授 52 | ``` 53 | 54 | 我认识老谢有 19 年了,等这本书的出版就等了 14 年。我在工作、学习中从这本书里获得了很多灵感,我自己写的书里也多次引用了这本书网络版的内容。这不仅是一本统计图形的工具书和参考书,更可以作为统计思维的人生之书。 55 | 56 | ```{block2, type = 'flushright', html.tag = 'p'} 57 | 李舰 58 | 统计之都理事,《统计之美》作者,九封医疗首席数据科学家 59 | ``` 60 | 61 | 《现代统计图形》的雏形始于 2007 年,那一年我还在中国人民大学统计学院读大四,益辉师 62 | 兄(老谢)比我高一级。他能坚持 14 年把书稿高质量地呈现给读者,彰显着现代统计人的初心。 63 | 64 | 这本书以浅显而有趣的图形示例将统计学和可视化的历史娓娓道来,是一本难得的以图形为主线、探讨统计学和统计可视化的高质量通识读物。书中内容无不体现作者对现代统计和统计图形的深刻思考,例如“神奇数字”和“作图原则”等章节。这本书既可作为高校师生的统计计算或者统计软件中可视化章节的主要参考书,也可作为严谨学术作图的指导手册。 65 | 66 | ```{block2, type = 'flushright', html.tag = 'p'} 67 | 李丰 68 | 中央财经大学统计与数学学院副院长、副教授 69 | ``` 70 | 71 | 缺乏信心作出预期的图形吗?希望用 R 基本代码随意生成任何图形吗?欲用“半傻瓜”方法 构造漂亮而又时髦的图形吗?渴望知道图形背后的原理及数学吗?愿意把画图作为乐趣吗?想 要轻松地阅读一本关于编程软件的书吗?答案很简单:读这本《现代统计图形》! 72 | 73 | ```{block2, type = 'flushright', html.tag = 'p'} 74 | 吴喜之 75 | 中国人民大学统计学院教授 76 | ``` 77 | 78 | 纷繁嘈杂的数据经过科学的处理、加工之后,映入眼帘的是生动、直观、一图胜千言的统计图形,这个美妙的过程称为可视化分析,是数据科学中极为重要的部分。 79 | 80 | 益辉兄的这本《现代统计图形》不仅高屋建瓴,概述了可视化分析的前世今生和哲学理念。 同时,内容包罗万象,精彩纷呈。从最常用的条形图、直方图,到根据不同场景和数据自定义图 形;从各种图形的思想和理论,到具体的案例和代码;从作图的细节参数,到不同的作图框架; 处处闪耀着智慧的火花,令人目不暇接,收获颇丰。 81 | 82 | 本人有幸在 10 年前,参与了这本书初稿的审阅校对,更有幸的是 10 年后,亲眼看见这本书 正式与读者见面。甚为欣慰,特此推荐。 83 | 84 | ```{block2, type = 'flushright', html.tag = 'p'} 85 | 魏太云 86 | 统计之都理事会第二任主席 87 | ``` 88 | 89 | 90 | 这本书是我读过的最有意思、最吸引人的关于统计图形的书。我想,无论是否了解图形可视 化,是否是统计专业人士,是否有 R 语言编程经验,都能从这本书中发现许多有价值的内容并从 中受益。这本书在介绍常用统计图形的同时,用实例深入阐述各种图形的用法和含义,指导我们 如何选择和使用统计图形,在授人以鱼的同时授人以渔。 91 | 92 | ```{block2, type = 'flushright', html.tag = 'p'} 93 | 曹洋 94 | 博士,生物信息分析师 95 | ``` 96 | 97 | 98 | 看这本书之前我以为 R 语言我算入门了,看完之后深感自己的无知。 99 | 100 | ```{block2, type = 'flushright', html.tag = 'p'} 101 | 姜军 102 | 武汉大学临床医学在读博士研究生 103 | ``` 104 | 105 | 106 | 优秀的统计思想和统计方法需要优雅的统计图形来表达。《现代统计图形》使用鲜活的经典 实例深入浅出地阐释了统计图形的设计原则,给出了一套全面且实用的参考图库与绘图技巧,理 论与实践并重,值得科研人员和数据科学从业者常备和参考。 107 | 108 | 109 | ```{block2, type = 'flushright', html.tag = 'p'} 110 | 肖楠 111 | 默沙东实验室生物统计和研究决策科学部副主任科学家 112 | ``` 113 | 114 | 读完此书,耳目一新。这本书开篇从统计作图的历史娓娓道来,中间辅以实例系统地介绍了 不同类型的统计作图,最后从心理学角度总结了作图的原则。读者可以深切地感受到作者对数学 生命的敬畏和热忱。全书既融汇了科学与艺术,又十分接地气,是同类书中极为难得的一种存在。 115 | 116 | 117 | ```{block2, type = 'flushright', html.tag = 'p'} 118 | 吴晟 119 | 暨南大学质谱仪器与大气环境研究所副研究员 120 | ``` 121 | 122 | 123 | 大数据统计或挖掘结果的图形化,是人们快速观察并发现数据隐藏信息或新颖知识的有效方法。统计图形不仅是大数据统计和挖掘结果的可视化利器,也是高水平学术成果的重要组成部分。 124 | 《现代统计图形》集趣味性、知识性、技术性和实践性于一体,是作者浓缩多年用 R 语言制作统 计图形经验的匠心之作。这本书不仅可作为高校统计学、数据科学与大数据技术等专业本科生的选修课教材,也可作为高校硕士和博士研究生用 R 语言作图的快速入门参考书。利用这本书提供 的 R 语言作图源程序,读者可轻松掌握其基本方法,并在不知不觉中快速成长为使用 R 语言及 其绘图包 ggplot2 制作统计图形的高手。 125 | 126 | 127 | ```{block2, type = 'flushright', html.tag = 'p'} 128 | 黄德才 129 | 浙江工业大学教授、博士生导师,资深数据科学专家, 130 | “十一五”“十二五”国家级规划教材作者,浙江省教学名师 131 | ``` 132 | 133 | 数据可视化的学习之中,存在“道”与“术”两个不同层面。“术”为工具的使用,包括程序语言、作图函数等;“道”为作图思想,是逻辑、统计知识、审美等多方面的综合。这本书极难得地将二者相结合,不空谈道,也不局限于术。在每个阶段读,都有不同的收获。同时,还辅以百余种常见统计图形为案例解析,实乃不可多得之佳作。 134 | 135 | ```{block2, type = 'flushright', html.tag = 'p'} 136 | 王建栋 137 | 南京信息工程大学大气物理学院教授 138 | ``` 139 | 140 | 翻开这本书,不会觉得自己在念教材,也感觉不到科技制图“严谨性”带来的束缚,更像是 141 | 在和几位艺术家聊天,畅谈研究数据的美学呈现。 142 | 143 | ```{block2, type = 'flushright', html.tag = 'p'} 144 | 王小享 145 | 南方科技大学研究助理教授 146 | ``` 147 | 148 | 如果你一直在寻找一本能循循善诱带你入门,又能一路托你到卓尔不群的统计图形真经,就 是这本《现代统计图形》。但是作者们并非仅仅醉心于打造一本“图形形式美”的工具辞典,对 贯通数学原理与图形表达,从认知逻辑出发优化视觉传达,权衡图形的直观呈现与统计价值等问题的思考暴露了三位作者的“数学艺术家”本质。木心有句:“思维、情操的创造性必然伴随着形式的创造性。”说的就是这些人、这本书吧。 149 | 150 | ```{block2, type = 'flushright', html.tag = 'p'} 151 | 刘倩 152 | 中央财经大学财经研究院副研究员 153 | ``` 154 | 155 | 这本书溯流从源地带着读者领略数据可视化技术的本质之“道”:可视化终究是数学的直观图形表达,而熟练偏向底层的技术才能使得表达过程不受束缚。即使这本书被谢大拖稿 14 年,依然被众多读者千呼万唤始出来,足见其超越技术本身,而道出数据可视化之本质的价值。 156 | 157 | ```{block2, type = 'flushright', html.tag = 'p'} 158 | 夏骁凯 159 | 华南师范大学计算认知科学博士研究生 160 | ``` 161 | 162 | 统计学及统计可视化是当前大数据时代最炙手可热的学问。如何挖掘数据价值,让数据直观 地呈现并为我们所用,数据可视化功不可没。值得思考的是,我们所使用的可视化展现方式是否 清晰准确地表达出了数据背后的价值。对于大部分人来说,Excel 是办公场合使用最多的软件, 几乎垄断了我们的日常工作。然而,这种数据可视化方式具有局限性,限制了统计工作者想要自 定义统计图形的“能动性发挥”,可视化效率不高。相较于其他作图软件,R 语言具有极强的“可 定制性”。《现代统计图形》以风趣幽默的语言、浅显易懂的图形案例介绍了统计图形的历史,从应用角度全面介绍了 R 语言作图及其绘图包 ggplot2 作图的源程序,详细讲述了 R 语言作图系统 中的各个细节,让我们由浅入深地掌握数据可视化的精髓。读者在这本书中不仅能学到 R 语言制 作统计图形的实用技术,还可以拓展制作统计图形的探索性思维。对于初学 R 语言的众多科研工 作者来说,这本书实在是不可错过的入门启蒙书。 163 | 164 | ```{block2, type = 'flushright', html.tag = 'p'} 165 | 于志国 166 | 南京信息工程大学水文与水资源工程学院副院长、教授、博士生导师 167 | ``` 168 | 169 | 《现代统计图形》读起来就像一本由武林前辈撰写的兵器图谱,其中不仅包含兵器(R 作图 元素及工具)展示、武功心法(统计学知识及作图原则),更有不少江湖趣事(统计图形简史及 案例分析)。不管你是江湖少侠(熟悉 R 或统计学),或者初入江湖(刚开始学习 R 或统计学), 我相信这本书都能帮你提高一层境界。即便是如我一般的小白,也多半会被作者们幽默诙谐的语 言、娓娓道来的叙述,以及层层递进的剖析深深吸引,而受益匪浅。 170 | 171 | ```{block2, type = 'flushright', html.tag = 'p'} 172 | 叶飞 173 | 纽约城市大学数学助理教授 174 | ``` 175 | 176 | 177 | 喜欢谢益辉的人很多,但要认识真实的谢益辉或许要从这部作品开始。他对统计艺术的赤诚 态度、对统计可视化创作的厚积薄发,这在人大统计专业我所教过的毕业生中并不多见。统计之 灵借着益辉通俗的文字、生动的图形和缜密的语言对我们低语,传达着一代青年统计学者对数据 之美的追求、对科学信念的执着,和他独特的对数据垃圾与艺术作品差别的关注与思考,《现代 统计图形》值得关注。 178 | 179 | 180 | ```{block2, type = 'flushright', html.tag = 'p'} 181 | 王星 182 | 中国人民大学统计学院副教授,《中国大百科全书》第三版统计学卷编委 183 | ``` 184 | 185 | 这是一本“时间的玫瑰”,历久弥新,芬芳四溢。 186 | 187 | 写书之人向读书之人表达尊重的最高级方式是行文如流水,专业功底隐藏在明晰简练的文字 之下。读这本统计图形的专著,感觉不到图形与文字间的隔阂,也感觉不到有意或无意间的权威 性。书中用“我们”的口吻表达作者与读者间平等的沟通关系。 188 | 189 | 作者亦非常注重美感,读者从书的排版、布局、文字、图形和颜色的区分等方面都可以感受 到美的力量。 190 | 191 | 了解或熟知谢益辉其人的读者,从此书中可读出作者青春的印记,“四时可爱唯春日,一事能狂便少年”。完全陌生的读者,看到此书便是缘分,愿你在运行书中代码、思考作者提出的小结问题之中,获得启蒙。 192 | 193 | ```{block2, type = 'flushright', html.tag = 'p'} 194 | 李承文 195 | 不懂统计学的平常百姓 196 | ``` 197 | 198 | 谢益辉是 R 扩展包 bookdown 的作者,他和两位合作者的这本新书可以作为学习 R 统计图形的教材和参考手册,其中许多图形是现有中文 R 语言教材中没有涉及的。书中还精练、准确地概 括了 R 基础作图、ggplot2 作图系统,并用许多饶有趣味的实例深入浅出地介绍了重要的统计图形 原则。 199 | 200 | ```{block2, type = 'flushright', html.tag = 'p'} 201 | 李东风 202 | 北京大学数学科学学院副教授 203 | ``` 204 | 205 | 在我的理解中,统计分析的过程和把大象关在冰箱里的步骤是一样的。第一步,把庞杂的原 始数据整理成统计软件可以接受的形式;第二步,运用恰当的方法进行分析;第三步,通过图形 等方式,把结果表达为人类便于理解的形式。我们的教育只重视第二步,导致很多人于实际中在 第一步和第三步中遇到了困难。对于第一步,必须要学习一门处理数据的语言,比如 R。而对于 第三步,一直以来没有很好的解决方案,直到这本《现代统计图形》的出现。 206 | 207 | ```{block2, type = 'flushright', html.tag = 'p'} 208 | 姜晓东 209 | 湖南师范大学医学院讲师 210 | ``` 211 | 212 | 我们已经进入了眼球经济时代,获得注意力就是获得财富。在我多年指导学生的实践中,越 发感受到图对于提升科研成果价值的重要性。今日翻开《现代统计图形》,如获至宝。作者深入 浅出地将枯燥的作图工具娓娓道来,一张张精美的图片更映射出作者的情感、心境和人生态度。相信这本书定能使广大读者发现作图的快乐! 213 | 214 | ```{block2, type = 'flushright', html.tag = 'p'} 215 | 袁自冰 216 | 华南理工大学教授、博士生导师 217 | ``` 218 | 219 | 你或许追过周更的动漫,季更的网剧,年更的小说,但你追过十年更的《现代统计图形》吗?我追过。 220 | 221 | 惊闻被谢大戏称为“古统”的《现代统计图形》即将问世,一时竟有不知今夕何年之感。翻 了翻过去的通讯记录,发现整整十年前谢大曾发了封邮件,问道:“你最近有空帮我写两节书 吗?”从此我知道了这本书的存在,并开启了长达十年的催更之路。 222 | 223 | 谢大所谓“古统”虽是戏谑之语,但也能读出其中的疑问:什么是统计图形的现代元素?按我的理解,统计图形最大的价值就在于它能加深人们对其背后数据的认识,而现代与否,关键在于图形能否为数据和问题提供了新的思考和解读。“图”虽旧“码”,其命维新。相信在阅读这本书后,读者会得益于其中的“探案过程”,学会用统计图形这一强大的工具去探究数据背后的秘密。 224 | 225 | ```{block2, type = 'flushright', html.tag = 'p'} 226 | 邱怡轩 227 | 统计之都理事,上海财经大学青年教师 228 | ``` 229 | 230 | 赵鹏老师的博客“大鹏志”曾陪伴我走过学 R 语言最为艰难的一段时光。他用虽朴实无华但 不失风趣的语言,以讲道理的方式,循循善诱地告诉你为什么如此做、应该和可以怎么做。此后 出版的《学 R:零基础学习 R 语言》更是让我爱不释手、如获至宝。而打开《现代统计图形》之后,惊叹图形在 R 语言中可以如此被“调教”,一步步地跟着这本书的思路走,相信每个人都可以找到属于自己风格的绘图方式,特此推荐。 231 | 232 | ```{block2, type = 'flushright', html.tag = 'p'} 233 | 杨雷峰 234 | 博士,生态环境部华南环境科学研究所研究员 235 | ``` 236 | 237 | 238 | 多年后,面对即将出版的《现代统计图形》,我不禁回想起,在网上初见其书稿的那个遥远 的下午。 239 | 240 | 我想,当年看过网上流传的《现代统计图形》书稿的人大多应该和我一样,一直盼着它的正 式出版,然而,作者一“鸽”就是多年(后来才听说原来是磨刀去了 —— knitr、bookdown),直到最近终于要成书了。R 作为一门为统计而生的语言,长处之一就是其灵活的作图系统 —— 正所谓一图胜千言,有比把枯燥的数字变为漂亮的图形更好地展示统计思想的方法吗?然而 R 作图系统的灵活和强大也带来了相对陡峭的学习曲线。在《现代统计图形》里,作者们用流畅风趣的 语言为读者讲解构成图形的元素,并展示在 R 里如何将它们组合起来表达统计思想,在降低学习难度的同时,还为学习统计图形增添了许多乐趣。 241 | 242 | 书稿诞生后的这些年里,R 的江湖里兴兴废废出现了各种作图系统。大浪淘沙后,现在最常使用的除了 R 的基础作图系统外,就是 ggplot2 了。在这本书中,作者们不仅介绍了各个作图系统,还展示了用基础作图系统和 ggplot2 实现相同图形的代码。R 的基础作图系统和 ggplot2 在作 图上各有千秋,用这两种方式实现同一图形能更好地让读者体会各系统的优劣,以便在自己的数 据分析中选用合适的方式绘制图形。 243 | 244 | 245 | ```{block2, type = 'flushright', html.tag = 'p'} 246 | 张列弛 247 | 日本基础生物研究所技术支援员 248 | ``` 249 | 250 | 第一次看到《现代统计图形》的书稿大约是在 10 年前,当时惊艳于它对统计图形全面、系统的讲解,惊为天人。10 年后,重读此书——即便 ggplot2 已经成为统计图形的标配,但我们依然可以从问题到数据、数据到图形的诸多案例中明白:为什么呈现的图形是这个样子。该书分门别类地讲述了统计图形的图库、准则和系统,字里行间传递的“为什么”则更为精彩,正本溯源是《现代统计图形》最大的价值。如果读者第一次阅读此书,相信也会和我 10 年前的感受一样,enjoy it ! 251 | 252 | ```{block2, type = 'flushright', html.tag = 'p'} 253 | 刘思喆 254 | 51Talk 首席数据科学家 255 | ``` 256 | 257 | 信息爆炸,数据称王,好文章不看辞藻看图表。这本书好似一本秘籍,照着例子依猫画虎, 可练外功,任意数据均可成图做表;照着目录由简入繁,可获内功,枯燥数据变成生动图表。所 有和数据打交道的人,都值得入手一本,需要的时候翻翻。你会发现这本书不需要全部读懂,读 半本就可治天下图表。 258 | 259 | ```{block2, type = 'flushright', html.tag = 'p'} 260 | 陈正 261 | 西交利物浦大学健康与环境科学系副教授 262 | ``` 263 | 264 | 作为科研成果展示形式的关键组成部分,作图发挥着不可替代的重要作用。当今很多顶级 期刊采用的论文,大多配有恰如其分且赏心悦目的插图,这不仅有助于准确而深刻地表达学术 思想和结果,而且会令论文大为增色。为此,我向我的学生们推荐这本《现代统计图形》。这本 书细致入微地从基本的统计思想出发,详述了常见科技制图的理论和实例,是我的学生赵鹏继 265 | 《学 R:零基础学习 R 语言》一书出版后,与另外两位作者合著的又一力作。 266 | 267 | 268 | ```{block2, type = 'flushright', html.tag = 'p'} 269 | 朱彤 270 | 北京大学教授、博士生导师,北京大学环境科学与工程学院院长 271 | ``` 272 | 273 | 可视化是人与数据沟通的桥梁,也是人类观察数据世界的放大镜。可视化相关的教材很多,很多描绘了通过可视化看到数据的种种绚丽多彩的画卷,但是很少能向读者详细解释如何构建这样的“放大镜”。从这一意义上来说,这本作者聚十四年之力而作的《现代统计图形》尤其值得推荐。它不只介绍了可视化的概念和方法,书中对每一个常用的可视化方法都给出了 R 语言的实验细节,这是很多可视化实践者的福音。这本书的发行,必将极大地促进可视化的推广和应用。 274 | 275 | 276 | ```{block2, type = 'flushright', html.tag = 'p'} 277 | 袁晓如 278 | 北京大学“百人计划”研究员,北京大学信息科学技术学院博士生导师 279 | ``` 280 | -------------------------------------------------------------------------------- /interactives/faithful.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XiangyunHuang/msg/1c7109c91ddc76b7b4c37e1a934da15f6a512692/interactives/faithful.pdf -------------------------------------------------------------------------------- /interactives/heatmaply.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XiangyunHuang/msg/1c7109c91ddc76b7b4c37e1a934da15f6a512692/interactives/heatmaply.pdf -------------------------------------------------------------------------------- /interactives/lib/crosstalk-1.0.0/css/crosstalk.css: -------------------------------------------------------------------------------- 1 | /* Adjust margins outwards, so column contents line up with the edges of the 2 | parent of container-fluid. */ 3 | .container-fluid.crosstalk-bscols { 4 | margin-left: -30px; 5 | margin-right: -30px; 6 | white-space: normal; 7 | } 8 | 9 | /* But don't adjust the margins outwards if we're directly under the body, 10 | i.e. we were the top-level of something at the console. */ 11 | body > .container-fluid.crosstalk-bscols { 12 | margin-left: auto; 13 | margin-right: auto; 14 | } 15 | 16 | .crosstalk-input-checkboxgroup .crosstalk-options-group .crosstalk-options-column { 17 | display: inline-block; 18 | padding-right: 12px; 19 | vertical-align: top; 20 | } 21 | 22 | @media only screen and (max-width:480px) { 23 | .crosstalk-input-checkboxgroup .crosstalk-options-group .crosstalk-options-column { 24 | display: block; 25 | padding-right: inherit; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /interactives/lib/crosstalk-1.0.0/js/crosstalk.min.js: -------------------------------------------------------------------------------- 1 | !function a(b,c,d){function e(g,h){if(!c[g]){if(!b[g]){var i="function"==typeof require&&require;if(!h&&i)return i(g,!0);if(f)return f(g,!0);var j=new Error("Cannot find module '"+g+"'");throw j.code="MODULE_NOT_FOUND",j}var k=c[g]={exports:{}};b[g][0].call(k.exports,function(a){var c=b[g][1][a];return e(c?c:a)},k,k.exports,a,b,c,d)}return c[g].exports}for(var f="function"==typeof require&&require,g=0;gb?1:void 0}Object.defineProperty(c,"__esModule",{value:!0});var f=function(){function a(a,b){for(var c=0;c0&&void 0!==arguments[0]?arguments[0]:this._allKeys,b=Object.keys(this._handles).length;if(0===b)this._value=null;else{this._value=[];for(var c=0;c?@\[\\\]^`{|}~])/g,"\\$1")}function f(a){var b=h(a);Object.keys(i).forEach(function(c){if(b.hasClass(c)&&!b.hasClass("crosstalk-input-bound")){var d=i[c];g(d,a)}})}function g(a,b){var c=h(b).find("script[type='application/json'][data-for='"+e(b.id)+"']"),d=JSON.parse(c[0].innerText),f=a.factory(b,d);h(b).data("crosstalk-instance",f),h(b).addClass("crosstalk-input-bound")}Object.defineProperty(c,"__esModule",{value:!0}),c.register=b;var h=a.jQuery,i={};a.Shiny&&!function(){var b=new a.Shiny.InputBinding,c=a.jQuery;c.extend(b,{find:function(a){return c(a).find(".crosstalk-input")},initialize:function(a){c(a).hasClass("crosstalk-input-bound")||f(a)},getId:function(a){return a.id},getValue:function(a){},setValue:function(a,b){},receiveMessage:function(a,b){},subscribe:function(a,b){c(a).data("crosstalk-instance").resume()},unsubscribe:function(a){c(a).data("crosstalk-instance").suspend()}}),a.Shiny.inputBindings.register(b,"crosstalk.inputBinding")}()}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{}],7:[function(a,b,c){(function(b){"use strict";function c(a){if(a&&a.__esModule)return a;var b={};if(null!=a)for(var c in a)Object.prototype.hasOwnProperty.call(a,c)&&(b[c]=a[c]);return b.default=a,b}var d=a("./input"),e=c(d),f=a("./filter"),g=b.jQuery;e.register({className:"crosstalk-input-checkboxgroup",factory:function(a,b){var c=new f.FilterHandle(b.group),d=void 0,e=g(a);return e.on("change","input[type='checkbox']",function(){var a=e.find("input[type='checkbox']:checked");0===a.length?(d=null,c.clear()):!function(){var e={};a.each(function(){b.map[this.value].forEach(function(a){e[a]=!0})});var f=Object.keys(e);f.sort(),d=f,c.set(f)}()}),{suspend:function(){c.clear()},resume:function(){d&&c.set(d)}}}})}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{"./filter":2,"./input":6}],8:[function(a,b,c){(function(b){"use strict";function c(a){if(a&&a.__esModule)return a;var b={};if(null!=a)for(var c in a)Object.prototype.hasOwnProperty.call(a,c)&&(b[c]=a[c]);return b.default=a,b}var d=a("./input"),e=c(d),f=a("./util"),g=c(f),h=a("./filter"),i=b.jQuery;e.register({className:"crosstalk-input-select",factory:function(a,b){var c=[{value:"",label:"(All)"}],d=g.dataframeToD3(b.items),e={options:c.concat(d),valueField:"value",labelField:"label",searchField:"label"},f=i(a).find("select")[0],j=i(f).selectize(e)[0].selectize,k=new h.FilterHandle(b.group),l=void 0;return j.on("change",function(){0===j.items.length?(l=null,k.clear()):!function(){var a={};j.items.forEach(function(c){b.map[c].forEach(function(b){a[b]=!0})});var c=Object.keys(a);c.sort(),l=c,k.set(c)}()}),{suspend:function(){k.clear()},resume:function(){l&&k.set(l)}}}})}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{"./filter":2,"./input":6,"./util":11}],9:[function(a,b,c){(function(b){"use strict";function c(a){if(a&&a.__esModule)return a;var b={};if(null!=a)for(var c in a)Object.prototype.hasOwnProperty.call(a,c)&&(b[c]=a[c]);return b.default=a,b}function d(a,b){for(var c=a.toString();c.length=i&&m<=j&&k.push(b.keys[l])}k.sort(),d.set(k),p=k}}),{suspend:function(){d.clear()},resume:function(){p&&d.set(p)}}}})}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{"./filter":2,"./input":6}],10:[function(a,b,c){"use strict";function d(a){if(a&&a.__esModule)return a;var b={};if(null!=a)for(var c in a)Object.prototype.hasOwnProperty.call(a,c)&&(b[c]=a[c]);return b.default=a,b}function e(a){return a&&a.__esModule?a:{default:a}}function f(a,b){if(!(a instanceof b))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(c,"__esModule",{value:!0}),c.SelectionHandle=void 0;var g=function(){function a(a,b){for(var c=0;c0&&void 0!==arguments[0]?arguments[0]:null,c=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null;f(this,a),this._eventRelay=new i.default,this._emitter=new m.SubscriptionTracker(this._eventRelay),this._group=null,this._var=null,this._varOnChangeSub=null,this._extraInfo=m.extend({sender:this},c),this.setGroup(b)}return g(a,[{key:"setGroup",value:function(a){var b=this;if(this._group!==a&&(this._group||a)&&(this._var&&(this._var.off("change",this._varOnChangeSub),this._var=null,this._varOnChangeSub=null),this._group=a,a)){this._var=(0,k.default)(a).var("selection");var c=this._var.on("change",function(a){b._eventRelay.trigger("change",a,b)});this._varOnChangeSub=c}}},{key:"_mergeExtraInfo",value:function(a){return m.extend({},this._extraInfo?this._extraInfo:null,a?a:null)}},{key:"set",value:function(a,b){this._var&&this._var.set(a,this._mergeExtraInfo(b))}},{key:"clear",value:function(a){this._var&&this.set(void 0,this._mergeExtraInfo(a))}},{key:"on",value:function(a,b){return this._emitter.on(a,b)}},{key:"off",value:function(a,b){return this._emitter.off(a,b)}},{key:"close",value:function(){this._emitter.removeAllListeners(),this.setGroup(null)}},{key:"value",get:function(){return this._var?this._var.get():null}}]),a}()},{"./events":1,"./group":4,"./util":11}],11:[function(a,b,c){"use strict";function d(a,b){if(!(a instanceof b))throw new TypeError("Cannot call a class as a function")}function e(a){for(var b=arguments.length,c=Array(b>1?b-1:0),d=1;d 4 | Gilles van den Hoven 5 | Michael Geary 6 | Stefan Petre 7 | Yehuda Katz 8 | Corey Jewett 9 | Klaus Hartl 10 | Franck Marcia 11 | Jörn Zaefferer 12 | Paul Bakaus 13 | Brandon Aaron 14 | Mike Alsup 15 | Dave Methvin 16 | Ed Engelhardt 17 | Sean Catchpole 18 | Paul Mclanahan 19 | David Serduke 20 | Richard D. Worth 21 | Scott González 22 | Ariel Flesler 23 | Jon Evans 24 | TJ Holowaychuk 25 | Michael Bensoussan 26 | Robert Katić 27 | Louis-Rémi Babé 28 | Earle Castledine 29 | Damian Janowski 30 | Rich Dougherty 31 | Kim Dalsgaard 32 | Andrea Giammarchi 33 | Mark Gibson 34 | Karl Swedberg 35 | Justin Meyer 36 | Ben Alman 37 | James Padolsey 38 | David Petersen 39 | Batiste Bieler 40 | Alexander Farkas 41 | Rick Waldron 42 | Filipe Fortes 43 | Neeraj Singh 44 | Paul Irish 45 | Iraê Carvalho 46 | Matt Curry 47 | Michael Monteleone 48 | Noah Sloan 49 | Tom Viner 50 | Douglas Neiner 51 | Adam J. Sontag 52 | Dave Reed 53 | Ralph Whitbeck 54 | Carl Fürstenberg 55 | Jacob Wright 56 | J. Ryan Stinnett 57 | unknown 58 | temp01 59 | Heungsub Lee 60 | Colin Snover 61 | Ryan W Tenney 62 | Pinhook 63 | Ron Otten 64 | Jephte Clain 65 | Anton Matzneller 66 | Alex Sexton 67 | Dan Heberden 68 | Henri Wiechers 69 | Russell Holbrook 70 | Julian Aubourg 71 | Gianni Alessandro Chiappetta 72 | Scott Jehl 73 | James Burke 74 | Jonas Pfenniger 75 | Xavi Ramirez 76 | Jared Grippe 77 | Sylvester Keil 78 | Brandon Sterne 79 | Mathias Bynens 80 | Timmy Willison 81 | Corey Frang 82 | Digitalxero 83 | Anton Kovalyov 84 | David Murdoch 85 | Josh Varner 86 | Charles McNulty 87 | Jordan Boesch 88 | Jess Thrysoee 89 | Michael Murray 90 | Lee Carpenter 91 | Alexis Abril 92 | Rob Morgan 93 | John Firebaugh 94 | Sam Bisbee 95 | Gilmore Davidson 96 | Brian Brennan 97 | Xavier Montillet 98 | Daniel Pihlstrom 99 | Sahab Yazdani 100 | avaly 101 | Scott Hughes 102 | Mike Sherov 103 | Greg Hazel 104 | Schalk Neethling 105 | Denis Knauf 106 | Timo Tijhof 107 | Steen Nielsen 108 | Anton Ryzhov 109 | Shi Chuan 110 | Berker Peksag 111 | Toby Brain 112 | Matt Mueller 113 | Justin 114 | Daniel Herman 115 | Oleg Gaidarenko 116 | Richard Gibson 117 | Rafaël Blais Masson 118 | cmc3cn <59194618@qq.com> 119 | Joe Presbrey 120 | Sindre Sorhus 121 | Arne de Bree 122 | Vladislav Zarakovsky 123 | Andrew E Monat 124 | Oskari 125 | Joao Henrique de Andrade Bruni 126 | tsinha 127 | Matt Farmer 128 | Trey Hunner 129 | Jason Moon 130 | Jeffery To 131 | Kris Borchers 132 | Vladimir Zhuravlev 133 | Jacob Thornton 134 | Chad Killingsworth 135 | Nowres Rafid 136 | David Benjamin 137 | Uri Gilad 138 | Chris Faulkner 139 | Elijah Manor 140 | Daniel Chatfield 141 | Nikita Govorov 142 | Wesley Walser 143 | Mike Pennisi 144 | Markus Staab 145 | Dave Riddle 146 | Callum Macrae 147 | Benjamin Truyman 148 | James Huston 149 | Erick Ruiz de Chávez 150 | David Bonner 151 | Akintayo Akinwunmi 152 | MORGAN 153 | Ismail Khair 154 | Carl Danley 155 | Mike Petrovich 156 | Greg Lavallee 157 | Daniel Gálvez 158 | Sai Lung Wong 159 | Tom H Fuertes 160 | Roland Eckl 161 | Jay Merrifield 162 | Allen J Schmidt Jr 163 | Jonathan Sampson 164 | Marcel Greter 165 | Matthias Jäggli 166 | David Fox 167 | Yiming He 168 | Devin Cooper 169 | Paul Ramos 170 | Rod Vagg 171 | Bennett Sorbo 172 | Sebastian Burkhard 173 | nanto 174 | Danil Somsikov 175 | Ryunosuke SATO 176 | Jean Boussier 177 | Adam Coulombe 178 | Andrew Plummer 179 | Mark Raddatz 180 | Dmitry Gusev 181 | Michał Gołębiowski 182 | Nguyen Phuc Lam 183 | Tom H Fuertes 184 | Brandon Johnson 185 | Jason Bedard 186 | Kyle Robinson Young 187 | Renato Oliveira dos Santos 188 | Chris Talkington 189 | Eddie Monge 190 | Terry Jones 191 | Jason Merino 192 | Jeremy Dunck 193 | Chris Price 194 | Amey Sakhadeo 195 | Anthony Ryan 196 | Dominik D. Geyer 197 | George Kats 198 | Lihan Li 199 | Ronny Springer 200 | Marian Sollmann 201 | Corey Frang 202 | Chris Antaki 203 | Noah Hamann 204 | David Hong 205 | Jakob Stoeck 206 | Christopher Jones 207 | Forbes Lindesay 208 | John Paul 209 | S. Andrew Sheppard 210 | Leonardo Balter 211 | Roman Reiß 212 | Benjy Cui 213 | Rodrigo Rosenfeld Rosas 214 | John Hoven 215 | Christian Kosmowski 216 | Liang Peng 217 | TJ VanToll 218 | -------------------------------------------------------------------------------- /interactives/lib/plotly-htmlwidgets-css-1.46.1/plotly-htmlwidgets.css: -------------------------------------------------------------------------------- 1 | /* 2 | just here so that plotly works 3 | correctly with ioslides. 4 | see https://github.com/ropensci/plotly/issues/463 5 | */ 6 | 7 | slide:not(.current) .plotly.html-widget{ 8 | display: none; 9 | } 10 | -------------------------------------------------------------------------------- /interactives/lib/rglWebGL-binding-0.100.30/rglWebGL.js: -------------------------------------------------------------------------------- 1 | /* el is the div, holding the rgl object as el.rglinstance, 2 | which holds x as el.rglinstance.scene 3 | x is the JSON encoded rglwidget. 4 | */ 5 | 6 | 7 | HTMLWidgets.widget({ 8 | 9 | name: 'rglWebGL', 10 | 11 | type: 'output', 12 | 13 | factory: function(el, width, height) { 14 | el.width = width; 15 | el.height = height; 16 | var rgl = new rglwidgetClass(), 17 | onchangeselection = function(e) { 18 | for (var i = 0; i < rgl.scene.crosstalk.sel_handle.length; i++) 19 | rgl.clearBrush(except = e.rglSubsceneId); 20 | rgl.selection(e, false); 21 | }, 22 | onchangefilter = function(e) { 23 | rgl.selection(e, true); 24 | }; 25 | 26 | return { 27 | renderValue: function(x) { 28 | var i, pel, player, groups; 29 | 30 | x.crosstalk.group = groups = [].concat(x.crosstalk.group); 31 | x.crosstalk.id = [].concat(x.crosstalk.id); 32 | x.crosstalk.key = [].concat(x.crosstalk.key); 33 | x.crosstalk.sel_handle = new Array(groups.length); 34 | x.crosstalk.fil_handle = new Array(groups.length); 35 | x.crosstalk.selection = []; 36 | for (i = 0; i < groups.length; i++) { 37 | x.crosstalk.sel_handle[i] = new crosstalk.SelectionHandle(groups[i], {sharedId: x.crosstalk.id[i]}); 38 | x.crosstalk.sel_handle[i].on("change", onchangeselection); 39 | x.crosstalk.fil_handle[i] = new crosstalk.FilterHandle(groups[i], {sharedId: x.crosstalk.id[i]}); 40 | x.crosstalk.fil_handle[i].on("change", onchangefilter); 41 | } 42 | rgl.initialize(el, x); 43 | rgl.initGL(); 44 | 45 | /* We might have been called after (some of) the players were rendered. 46 | We need to make sure we respond to their initial values. */ 47 | 48 | if (typeof x.players !== "undefined") { 49 | var players = [].concat(x.players); 50 | for (i = 0; i < players.length; i++) { 51 | pel = document.getElementById(players[i]); 52 | if (pel) { 53 | player = pel.rglPlayer; 54 | if (player && !player.initialized) { 55 | rgl.Player(pel, player); 56 | player.initialized = true; 57 | } 58 | } 59 | } 60 | } 61 | rgl.drag = 0; 62 | rgl.drawScene(); 63 | }, 64 | 65 | resize: function(width, height) { 66 | el.width = width; 67 | el.height = height; 68 | el.rglinstance.resize(el); 69 | el.rglinstance.drawScene(); 70 | } 71 | }; 72 | } 73 | }); 74 | -------------------------------------------------------------------------------- /interactives/lib/rglwidgetClass-2/rgl.css: -------------------------------------------------------------------------------- 1 | .rglPlayer { 2 | width: auto; 3 | height: auto; 4 | } 5 | 6 | .rglPlayer .rgl-button { 7 | width: auto; 8 | display: inline-block; 9 | font-size: 75%; 10 | } 11 | 12 | .rglPlayer .rgl-slider { 13 | display: inline-block; 14 | width: 30%; 15 | } 16 | 17 | .rglPlayer .rgl-label { 18 | display: inline; 19 | padding-left: 6px; 20 | padding-right: 6px; 21 | } 22 | -------------------------------------------------------------------------------- /interactives/lib/typedarray-0.1/typedarray.min.js: -------------------------------------------------------------------------------- 1 | (function(global){"use strict";var undefined=void 0;var MAX_ARRAY_LENGTH=1e5;function Type(v){switch(typeof v){case"undefined":return"undefined";case"boolean":return"boolean";case"number":return"number";case"string":return"string";default:return v===null?"null":"object"}}function Class(v){return Object.prototype.toString.call(v).replace(/^\[object *|\]$/g,"")}function IsCallable(o){return typeof o==="function"}function ToObject(v){if(v===null||v===undefined)throw TypeError();return Object(v)}function ToInt32(v){return v>>0}function ToUint32(v){return v>>>0}var LN2=Math.LN2,abs=Math.abs,floor=Math.floor,log=Math.log,max=Math.max,min=Math.min,pow=Math.pow,round=Math.round;(function(){var orig=Object.defineProperty;var dom_only=!function(){try{return Object.defineProperty({},"x",{})}catch(_){return false}}();if(!orig||dom_only){Object.defineProperty=function(o,prop,desc){if(orig)try{return orig(o,prop,desc)}catch(_){}if(o!==Object(o))throw TypeError("Object.defineProperty called on non-object");if(Object.prototype.__defineGetter__&&"get"in desc)Object.prototype.__defineGetter__.call(o,prop,desc.get);if(Object.prototype.__defineSetter__&&"set"in desc)Object.prototype.__defineSetter__.call(o,prop,desc.set);if("value"in desc)o[prop]=desc.value;return o}}})();function makeArrayAccessors(obj){if(obj.length>MAX_ARRAY_LENGTH)throw RangeError("Array too large for polyfill");function makeArrayAccessor(index){Object.defineProperty(obj,index,{get:function(){return obj._getter(index)},set:function(v){obj._setter(index,v)},enumerable:true,configurable:false})}var i;for(i=0;i>s}function as_unsigned(value,bits){var s=32-bits;return value<>>s}function packI8(n){return[n&255]}function unpackI8(bytes){return as_signed(bytes[0],8)}function packU8(n){return[n&255]}function unpackU8(bytes){return as_unsigned(bytes[0],8)}function packU8Clamped(n){n=round(Number(n));return[n<0?0:n>255?255:n&255]}function packI16(n){return[n>>8&255,n&255]}function unpackI16(bytes){return as_signed(bytes[0]<<8|bytes[1],16)}function packU16(n){return[n>>8&255,n&255]}function unpackU16(bytes){return as_unsigned(bytes[0]<<8|bytes[1],16)}function packI32(n){return[n>>24&255,n>>16&255,n>>8&255,n&255]}function unpackI32(bytes){return as_signed(bytes[0]<<24|bytes[1]<<16|bytes[2]<<8|bytes[3],32)}function packU32(n){return[n>>24&255,n>>16&255,n>>8&255,n&255]}function unpackU32(bytes){return as_unsigned(bytes[0]<<24|bytes[1]<<16|bytes[2]<<8|bytes[3],32)}function packIEEE754(v,ebits,fbits){var bias=(1<.5)return w+1;return w%2?w+1:w}if(v!==v){e=(1<=pow(2,1-bias)){e=min(floor(log(v)/LN2),1023);f=roundToEven(v/pow(2,e)*pow(2,fbits));if(f/pow(2,fbits)>=2){e=e+1;f=1}if(e>bias){e=(1<>1}}bits.reverse();str=bits.join("");bias=(1<0){return s*pow(2,e-bias)*(1+f/pow(2,fbits))}else if(f!==0){return s*pow(2,-(bias-1))*(f/pow(2,fbits))}else{return s<0?-0:0}}function unpackF64(b){return unpackIEEE754(b,11,52)}function packF64(v){return packIEEE754(v,11,52)}function unpackF32(b){return unpackIEEE754(b,8,23)}function packF32(v){return packIEEE754(v,8,23)}(function(){function ArrayBuffer(length){length=ToInt32(length);if(length<0)throw RangeError("ArrayBuffer size is not a small enough positive integer.");Object.defineProperty(this,"byteLength",{value:length});Object.defineProperty(this,"_bytes",{value:Array(length)});for(var i=0;i=1&&Type(arguments[0])==="object"&&arguments[0]instanceof $TypedArray$){return function(typedArray){if(this.constructor!==typedArray.constructor)throw TypeError();var byteLength=typedArray.length*this.BYTES_PER_ELEMENT;Object.defineProperty(this,"buffer",{value:new ArrayBuffer(byteLength)});Object.defineProperty(this,"byteLength",{value:byteLength});Object.defineProperty(this,"byteOffset",{value:0});Object.defineProperty(this,"length",{value:typedArray.length});for(var i=0;i=1&&Type(arguments[0])==="object"&&!(arguments[0]instanceof $TypedArray$)&&!(arguments[0]instanceof ArrayBuffer||Class(arguments[0])==="ArrayBuffer")){return function(array){var byteLength=array.length*this.BYTES_PER_ELEMENT;Object.defineProperty(this,"buffer",{value:new ArrayBuffer(byteLength)});Object.defineProperty(this,"byteLength",{value:byteLength});Object.defineProperty(this,"byteOffset",{value:0});Object.defineProperty(this,"length",{value:array.length});for(var i=0;i=1&&Type(arguments[0])==="object"&&(arguments[0]instanceof ArrayBuffer||Class(arguments[0])==="ArrayBuffer")){return function(buffer,byteOffset,length){byteOffset=ToUint32(byteOffset);if(byteOffset>buffer.byteLength)throw RangeError("byteOffset out of range");if(byteOffset%this.BYTES_PER_ELEMENT)throw RangeError("buffer length minus the byteOffset is not a multiple of the element size.");if(length===undefined){var byteLength=buffer.byteLength-byteOffset;if(byteLength%this.BYTES_PER_ELEMENT)throw RangeError("length of buffer minus byteOffset not a multiple of the element size");length=byteLength/this.BYTES_PER_ELEMENT}else{length=ToUint32(length);byteLength=length*this.BYTES_PER_ELEMENT}if(byteOffset+byteLength>buffer.byteLength)throw RangeError("byteOffset and length reference an area beyond the end of the buffer");Object.defineProperty(this,"buffer",{value:buffer});Object.defineProperty(this,"byteLength",{value:byteLength});Object.defineProperty(this,"byteOffset",{value:byteOffset});Object.defineProperty(this,"length",{value:length})}.apply(this,arguments)}throw TypeError()}Object.defineProperty($TypedArray$,"from",{value:function(iterable){return new this(iterable)}});Object.defineProperty($TypedArray$,"of",{value:function(){return new this(arguments)}});var $TypedArrayPrototype$={};$TypedArray$.prototype=$TypedArrayPrototype$;Object.defineProperty($TypedArray$.prototype,"_getter",{value:function(index){if(arguments.length<1)throw SyntaxError("Not enough arguments");index=ToUint32(index);if(index>=this.length)return undefined;var bytes=[],i,o;for(i=0,o=this.byteOffset+index*this.BYTES_PER_ELEMENT;i=this.length)return;var bytes=this._pack(value),i,o;for(i=0,o=this.byteOffset+index*this.BYTES_PER_ELEMENT;i0){o._setter(to,o._getter(from));from=from+direction;to=to+direction;count=count-1}return o}});Object.defineProperty($TypedArray$.prototype,"every",{value:function(callbackfn){if(this===undefined||this===null)throw TypeError();var t=Object(this);var len=ToUint32(t.length);if(!IsCallable(callbackfn))throw TypeError();var thisArg=arguments[1];for(var i=0;i1?arguments[1]:undefined;var k=0;while(k1?arguments[1]:undefined;var k=0;while(k0){n=Number(arguments[1]);if(n!==n){n=0}else if(n!==0&&n!==1/0&&n!==-(1/0)){n=(n>0||-1)*floor(abs(n))}}if(n>=len)return-1;var k=n>=0?n:max(len-abs(n),0);for(;k1){n=Number(arguments[1]);if(n!==n){n=0}else if(n!==0&&n!==1/0&&n!==-(1/0)){n=(n>0||-1)*floor(abs(n))}}var k=n>=0?min(n,len-1):len-abs(n);for(;k>=0;k--){if(t._getter(k)===searchElement)return k}return-1}});Object.defineProperty($TypedArray$.prototype,"map",{value:function(callbackfn){if(this===undefined||this===null)throw TypeError();var t=Object(this);var len=ToUint32(t.length);if(!IsCallable(callbackfn))throw TypeError();var res=[];res.length=len;var thisp=arguments[1];for(var i=0;i=2){accumulator=arguments[1]}else{accumulator=t._getter(k++)}while(k=2){accumulator=arguments[1]}else{accumulator=t._getter(k--)}while(k>=0){accumulator=callbackfn.call(undefined,accumulator,t._getter(k),k,t);k--}return accumulator}});Object.defineProperty($TypedArray$.prototype,"reverse",{value:function(){if(this===undefined||this===null)throw TypeError();var t=Object(this);var len=ToUint32(t.length);var half=floor(len/2);for(var i=0,j=len-1;ithis.length){throw RangeError("Offset plus length of array is out of range")}byteOffset=this.byteOffset+offset*this.BYTES_PER_ELEMENT;byteLength=array.length*this.BYTES_PER_ELEMENT;if(array.buffer===this.buffer){tmp=[];for(i=0,s=array.byteOffset;ithis.length){throw RangeError("Offset plus length of array is out of range")}for(i=0;imax?max:v}start=ToInt32(start);end=ToInt32(end);if(arguments.length<1){start=0}if(arguments.length<2){end=this.length}if(start<0){start=this.length+start}if(end<0){end=this.length+end}start=clamp(start,0,this.length);end=clamp(end,0,this.length);var len=end-start;if(len<0){len=0}return new this.constructor(this.buffer,this.byteOffset+start*this.BYTES_PER_ELEMENT,len)}});function makeTypedArray(elementSize,pack,unpack){var TypedArray=function(){Object.defineProperty(this,"constructor",{value:TypedArray});$TypedArray$.apply(this,arguments);makeArrayAccessors(this)};if("__proto__"in TypedArray){TypedArray.__proto__=$TypedArray$}else{TypedArray.from=$TypedArray$.from;TypedArray.of=$TypedArray$.of}TypedArray.BYTES_PER_ELEMENT=elementSize;var TypedArrayPrototype=function(){};TypedArrayPrototype.prototype=$TypedArrayPrototype$;TypedArray.prototype=new TypedArrayPrototype;Object.defineProperty(TypedArray.prototype,"BYTES_PER_ELEMENT",{value:elementSize});Object.defineProperty(TypedArray.prototype,"_pack",{value:pack});Object.defineProperty(TypedArray.prototype,"_unpack",{value:unpack});return TypedArray}var Int8Array=makeTypedArray(1,packI8,unpackI8);var Uint8Array=makeTypedArray(1,packU8,unpackU8);var Uint8ClampedArray=makeTypedArray(1,packU8Clamped,unpackU8);var Int16Array=makeTypedArray(2,packI16,unpackI16);var Uint16Array=makeTypedArray(2,packU16,unpackU16);var Int32Array=makeTypedArray(4,packI32,unpackI32);var Uint32Array=makeTypedArray(4,packU32,unpackU32);var Float32Array=makeTypedArray(4,packF32,unpackF32);var Float64Array=makeTypedArray(8,packF64,unpackF64);global.Int8Array=global.Int8Array||Int8Array;global.Uint8Array=global.Uint8Array||Uint8Array;global.Uint8ClampedArray=global.Uint8ClampedArray||Uint8ClampedArray;global.Int16Array=global.Int16Array||Int16Array;global.Uint16Array=global.Uint16Array||Uint16Array;global.Int32Array=global.Int32Array||Int32Array;global.Uint32Array=global.Uint32Array||Uint32Array;global.Float32Array=global.Float32Array||Float32Array;global.Float64Array=global.Float64Array||Float64Array})();(function(){function r(array,index){return IsCallable(array.get)?array.get(index):array[index]}var IS_BIG_ENDIAN=function(){var u16array=new Uint16Array([4660]),u8array=new Uint8Array(u16array.buffer);return r(u8array,0)===18}();function DataView(buffer,byteOffset,byteLength){if(!(buffer instanceof ArrayBuffer||Class(buffer)==="ArrayBuffer"))throw TypeError();byteOffset=ToUint32(byteOffset);if(byteOffset>buffer.byteLength)throw RangeError("byteOffset out of range");if(byteLength===undefined)byteLength=buffer.byteLength-byteOffset;else byteLength=ToUint32(byteLength);if(byteOffset+byteLength>buffer.byteLength)throw RangeError("byteOffset and length reference an area beyond the end of the buffer");Object.defineProperty(this,"buffer",{value:buffer});Object.defineProperty(this,"byteLength",{value:byteLength});Object.defineProperty(this,"byteOffset",{value:byteOffset})}function makeGetter(arrayType){return function GetViewValue(byteOffset,littleEndian){byteOffset=ToUint32(byteOffset);if(byteOffset+arrayType.BYTES_PER_ELEMENT>this.byteLength)throw RangeError("Array index out of range");byteOffset+=this.byteOffset;var uint8Array=new Uint8Array(this.buffer,byteOffset,arrayType.BYTES_PER_ELEMENT),bytes=[];for(var i=0;ithis.byteLength)throw RangeError("Array index out of range");var typeArray=new arrayType([value]),byteArray=new Uint8Array(typeArray.buffer),bytes=[],i,byteView;for(i=0;i 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | histogram with plotly 14 | 15 | 16 |
    17 |
    18 |
    19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /interactives/plotly-histogram.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XiangyunHuang/msg/1c7109c91ddc76b7b4c37e1a934da15f6a512692/interactives/plotly-histogram.pdf -------------------------------------------------------------------------------- /model.Rmd: -------------------------------------------------------------------------------- 1 | # 模型 {#cha:model} 2 | 3 | > “嗯,人的身高十有八九可以从他的步幅的长度推测出来,这极容易推算,但是让我把枯燥的数字摆出来算给你看实在是毫无用处。我在屋外的粘土路上和屋内的尘土上找到了那人的脚印,此外我还有一个法子验证我的计算:当一个人在墙上写字时,他会本能地将字写在视线以上的地方,而那个血字正好离地面六英尺。这推算实在是简单得像儿戏一般。” 4 | > 5 | > 我问:“那他的年龄呢?” 6 | > 7 | > “好的,如果一个人不费吹灰之力就能一步跨出四英尺半,他不可能年老体衰,因为花园小径上的泥坑恰好长四英尺半,他显然是一步跨过去的,漆皮靴则是绕过去的,所以这也没有什么神秘之处。我只不过是将我在那篇文章中推崇的一些观察和演绎的规则应用在日常生活中罢了。你还有什么不明白的地方吗?” 8 | > 9 | > --- 柯南·道尔《血字的研究》 10 | 11 | 在 \@ref(sec:begin) 小节中我们曾经提到三种基本的统计分析方式,其中包括推断统计分析和探索统计分析,表面上看来,统计模型在前者中占主导地位,而统计图形在后者中往往起着非常重要的作用,然而本章要探讨和揭示的则是以探索为目标的统计图形与以推断为目标的统计模型之间的联系。 12 | 13 | ## 线性模型 {#sec:lm} 14 | 15 | - 一元回归:散点图 16 | - 回归诊断:方差齐性假设、线性假设、独立性假设、正态性假设、离群点 17 | 18 | ## 方差分析 {#sec:aov} 19 | 20 | - 类似回归 21 | - 箱线图等 22 | 23 | ## 局部加权回归散点平滑法 {#sec:lowess} 24 | 25 | `lowess()` `loess()` `scatter.smooth()` `runmed()` ? 26 | 27 | [用局部加权回归散点平滑法观察二维变量之间的关系](https://cosx.org/2008/11/lowess-to-explore-bivariate-correlation-by-yihui) 28 | 29 | ## 稳健回归模型 {#sec:rlm} 30 | 31 | - 离群点 32 | 33 | ## 广义线性模型 {#sec:glm} 34 | 35 | - 回归诊断 36 | 37 | ## 线性混合效应模型 {#sec:lme} 38 | 39 | Gelman 讲“为啥不关心多重比较的问题”:(随机效应模型究竟做了什么?) 40 | 41 | ## 主成分分析和因子分析 42 | 43 | - 成分方差 44 | - Biplot 45 | 46 | ## 聚类分析 {#sec:cluster} 47 | 48 | - 谱系图 49 | - K-Means 的散点图 50 | - 基于密度的聚类 51 | 52 | ## 判别分析 53 | 54 | - 成分的散点图 55 | - LDA/二次判别分析 56 | 57 | ## 多维标度分析 58 | 59 | `cmdscale()`, `isoMDS()` 60 | 61 | - 高维数据的二维(低维)表示 62 | 63 | ## 时间序列分析 64 | 65 | 经典 ARMA、谱分析等 66 | 67 | 自相关 `acf()` 偏自相关 `pacf()` 时滞图 `lag.plot()` 68 | 69 | 累积周期图 `cpgram()` 70 | 71 | ## 空间分析 72 | 73 | 克里金插值 74 | 75 | 样本变差图 76 | 77 | 变差云图 78 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /msg-pkgs.Rmd: -------------------------------------------------------------------------------- 1 | # 本书 R 包 {#chap:MSG} 2 | 3 | ```{r include=FALSE} 4 | library(MSG) 5 | library(RColorBrewer) 6 | ``` 7 | 8 | 为了配合本书的写作,作者特意编写了一个 R 包名为 **MSG**(Modern Statistical Graphics 的缩写),该包目前已经发布到 CRAN,所以读者可以用以下方式直接安装: 9 | 10 | ```{r install-MSG,eval=FALSE} 11 | install.packages('MSG') 12 | ``` 13 | 14 | 也可以从作者开发页面下载()并自行编译安装。这里简要介绍一下它包含的函数和数据。 15 | 16 | ## 函数说明 {#sec:MSG-fun} 17 | 18 | `andrews_curve()` 19 | : 根据数据矩阵画调和曲线图,参见 \@ref(sec:andrews-curve) 小节 20 | 21 | `char_gen()` 22 | : 给定一个长度为 2 的字符向量,生成一个字符矩阵,其中包含若干个第 2 个字符和一个第 1 个字符,看用户从一大堆第 2 个字符中辨别第 1 个字符的难度,这可以是我们选择点的外观的一种依据,参见 \@ref(subsec:clear-symbols) 小节 23 | 24 | ```{r} 25 | char_gen(c('6', '9')) # 你能找出 6 吗? 26 | ``` 27 | 28 | `cut_plot()` 29 | : 将一幅散点图的横坐标离散化,并计算每组内点的纵坐标的均值,参见 \@ref(subsec:data-processing) 小节 30 | 31 | `heart_curve()` 32 | : 画心形曲线,用作低层作图函数的练习 33 | 34 | `vec2col()` 35 | : 将一个向量通过 **RColorBrewer** 包中的调色板转化为颜色向量,如: 36 | 37 | ```{r} 38 | # factor 类型的向量通常用离散调色板 39 | vec2col(factor(c(1,1,2,3,4,4,4))) 40 | # 数值型数据用连续调色板 41 | vec2col(rnorm(20)) 42 | ``` 43 | 44 | ## 数据说明 45 | 46 | 47 | `assists` 48 | : 湖人和骑士比赛中的助攻数据 49 | 50 | `BinormCircle` 51 | : 人造数据:两个独立的正态分布随机变量(10000 行实现值),加上半径为 0.5 的圆上的点的坐标(10000 行) 52 | 53 | `canabalt` 54 | : 游戏末日狂奔中的得分和游戏设备数据(从 Twitter 消息获得) 55 | 56 | `ChinaLifeEdu` 57 | : 2005 年中国各省市的人均预期寿命和受高等教育人数 58 | 59 | `cn_vs_us` 60 | : 中美国力对比数据 61 | 62 | `eq2010` 63 | : 四川省在 2010 年的地震数据,包括发生地点的经纬度和震级 64 | 65 | `Export.USCN` 66 | : 1994 年到 2004 年中美出口额数据 67 | 68 | `gov.cn.pct` 69 | : 中国政府网站中出现的各个百分比数据的频数(2009 年 12 月 17 日通过 Google 抓取) 70 | 71 | `murcia` 72 | : 西班牙 Murcia 省的土壤成分数据 73 | 74 | `music` 75 | : 四类艺术家的音乐频率数据,两类来自古典乐,两类来自摇滚乐 76 | 77 | `PlantCounts` 78 | : 植物数目与海拔高度的数据,共两列,每一行数据记录了某一海拔高度上植物数目 79 | 80 | `quake6` 81 | : 1973 年到 2010 年全球地震中超过 6 级的地震数据,包括经度、纬度、时间等信息 82 | 83 | `tvearn` 84 | : 2011 年 2 月最高收入的美剧演员数据,包括收入、电视剧类型、性别、电视剧评分等信息 85 | 86 | 87 | -------------------------------------------------------------------------------- /msg.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 | 15 | AutoAppendNewline: Yes 16 | 17 | BuildType: Website 18 | -------------------------------------------------------------------------------- /postscript.Rmd: -------------------------------------------------------------------------------- 1 | # 后记 {#postscript} 2 | 3 | 2007 年 2 月,我在家读 A Handbook of Statistical Analysis Using R [@Everitt06],书中读完前八章之后,萌发了写一篇关于统计图形综述的念头,于是用 LaTeX 匆匆写了几页,但几天后发现这不是一篇综述能讲清楚的事情,于是改为写一本三十来页的小册子,依次介绍 R 语言中的那些基本的统计图形,然而几天后发现这也不是一本小册子能装得下的内容,因为当时国内统计界对 R 语言的了解相对较少,所以有必要把统计图形放在整个 R 的系统下来讲述,于是在几经折腾之后,写这本书的念头诞生了。 4 | 5 | 如我在 \@ref(sec:begin) 小节中提到的,国内统计图形应用的现状并不是很理想,然而,在这本书中,我结合大量的数据实例给出了各式各样统计图形的展示,其中有相当一部分图形在我们的论文、书籍或报告中并不常见,原因是什么我并不太清楚,也许受软件束缚,也许并不知道有这些图形的存在,无论如何,本书作为一本指南读物,对软件实现以及图形种类都做出了介绍,对统计图形的问题也算是提供了一种解决方案,不过我的主要目的并不在于单纯介绍图形,而是想借现有的图形思想启发读者,一方面使读者能够明白图形的基本构造方法,这样可以让我们容易就能借“它山之石”为自己所用,即看到别人的好图形,自己便知道如何去做;另一方面希望读者通过观摩各种图形的思想,在自己的数据分析过程中找到图示的灵感,用更巧妙的方式揭示信息。 6 | 7 | 国外的统计图形和可视化水平相对国内看起来要领先许多,我于 2008 年 6 月在德国不来梅的 Jacobs 大学参加了一个统计图形会议(Data Viz VI),见到了统计图形领域的一些“长老”和新秀,如第 \@ref(chap:principle) 章提到的 Michael Friendly,这位老爷子在统计图形上作了大量工作,尤其是分类数据的可视化以及统计图形历史的总结等等,我正是从他那里了解到了统计图形的一些历史,包括本书第 \@ref(cha:history) 章介绍的图形;参会的还有 AT\&T 统计研究部门的 Deborah Swayne、Iowa 州立大学的 Di Cook (目前已经是我的导师)、沃顿商学院的 Andreas Buja ( [GGobi](http://www.ggobi.org) 最初就是他们三人合作写出来的)、Systat 公司的 Leland Wilkinson (The Grammar of Graphics 的作者), 同时我也见到了一些年轻学者如 Hadley Wickham (**ggplot2** 包的作者)、Michael Lawrence (**RGtk2** 包的作者)等;通过与这些人的接触,我进一步意识到了国内在统计图形方面和国外的差距,回来之后更加坚定了我完成这样一本统计图形书籍的信念。 8 | 9 | 2008 年到 2009 年这本书进展到了第 \@ref(cha:gallery) 章,也就是图形的海洋,这一章耗费了很长时间,因为内容几乎是无止境的。实际上到了 09 年进展就很慢了,这一年夏天来到了 Iowa 州立大学,渐渐搁置了这本书几乎没再动过笔。2010 年 5 月,周筠老师闯进了我的邮箱(我以前隐约听说过她,但没正式联系过),问起这本书,对话非常简单,几乎可以缩写为: 10 | 11 | > 我:“中不?” 12 | 13 | > 周:“中!” 14 | 15 | > 我:“好,归你了!” 16 | 17 | 于是乎,我的 CPU 又开始轰隆隆运转起来。 18 | 19 | 在 Iowa 州立大学的学习让我更新了不少知识,尤其是在两位专家 Di Cook 和 Heike Hofmann 的影响下。原本这书的目标只是介绍基础图形系统,也就是第 \@ref(cha:elements) 章和第 \@ref(cha:gallery) 章的内容,但随着知识的积累和 R 自身的更新,写着写着我就感觉落后于时代了,书名中有“现代”二字,如果只是基础图形系统,那么实在不敢称“现代”,所以在完成第 \@ref(cha:gallery) 章之后,我加入了第 \@ref(chap:system) 章内容,这样起码在工具上有点现代的感觉,例如 ggplot2 和动画,都是 R 里面的新事物。纯粹的工具介绍对读者来说可能会显得枯燥,尽管大部分图形都有对应的数据,但那些数据往往都是很老旧的数据,人人皆知;Cook 经常对我“怒吼”:No more `iris` data!!她对我使用旧数据有一肚子不满,我想这大概可以代表一部分读者的心情,于是这本书有了第 \@ref(chap:data) 章,我尽力找了新数据,要说它们有多新呢?图 \@ref(fig:pork-price-orig) 是我 2011 年 2 月 22 日收到的邮件,23 日我把它写进了书里。第 \@ref(chap:principle) 章放在最后作为所有技术性内容之后的一个总结,但它实际上也交织着技术性内容;我在导读中引用了“形而上者谓之道,形而下者谓之器”,实际上我觉得道与器无法分家,这道理就像“学而不思则罔,思而不学则怠”一样。 20 | 21 | 本书每章开头都挑选了一段《福尔摩斯探案集》中的文字,其内容与各章内容有一定关联,这也是由于我个人在上高中时就喜欢看福尔摩斯,并且我认为统计图形也可以看作是一种小小的“探案”。探案集中我最喜欢的一篇是《血字的研究》,尤其欣赏该篇的第二部分中大篇的景色描写,以及对主人公杰弗逊·霍普坚韧不拔性格的刻画,这种波澜壮阔的笔法,令我着实艳羡不已,只可惜我没这种文字功夫能把书写得如此吸引人,于是只能寄希望于“一图胜千言”了。 22 | 23 | 诗云:人事有代谢,往来成古今。江山留胜迹,我辈复登临。 24 | 25 | -------------------------------------------------------------------------------- /preamble.tex: -------------------------------------------------------------------------------- 1 | \usepackage{booktabs} 2 | \usepackage{animate} 3 | \usepackage[lotdepth=2,lofdepth=1]{subfig} 4 | \usepackage{makeidx} 5 | \makeindex 6 | \usepackage{cancel} 7 | % https://github.com/CTeX-org/ctex-kit/issues/331 8 | \RecustomVerbatimEnvironment{Highlighting}{Verbatim}{commandchars=\\\{\},formatcom=\xeCJKVerbAddon} 9 | 10 | \usepackage[cc]{titlepic} 11 | \titlepic{\includegraphics[width=4in]{images/titlepic}} 12 | \usepackage[labelfont=bf,singlelinecheck=off,textfont=it]{caption} 13 | -------------------------------------------------------------------------------- /preface.Rmd: -------------------------------------------------------------------------------- 1 | # 自序 {#preface .unnumbered} 2 | 3 | 4 | 2019 年底,陈兴璐编辑给我写了封邮件,问我是否有兴趣写一本中文书。这问题可以说是问到我心坎上了。2018 年我在给赵鹏的《学 R》一书写推荐序时就表达了写中文书的强烈愿望,只不过写书这种工作最好是用大块连续的时间去做,而如今多数人的时间都已经严重碎片化,我也不例外(尽管还在假装抗争),所以想要徒手写出一本新书的可能性已经微乎其微,于是我想到这本书稿。 5 | 6 | 赵鹏在本书后序《古统新修记》中提到了这本书的历史。从我 2007 年开始写这本书起,就不断有人问我何时出版。当年我把这本书的信息放在博客的某个网页上,后来我把这个网页藏了起来,但现在仍然可以看见当年很多读者在下面的留言。有些读者甚至说“银子都准备好了”。一晃十三年过去,我也从一个不靠谱的小青年成长为一个不靠谱的小中年,唯一不变的是这书仍然没有出版,惊喜不惊喜……我不知道以前当大家在谈论拖延症时大家在谈论些什么,反正以后大概可以谈谈这本被我拖成《古代统计图形》的《现代统计图形》了。 7 | 8 | 这些年来,我曾想过找人帮忙把这本书稿重新整理一下,也有不少人提出要给我帮忙,但我开发了几年 R Markdown 之后,实在不想再打开这本书的 LaTeX 原稿件,所以也没和这些志愿者一起推进。恰好在兴璐编辑问我的三个月之前,我终于咬咬牙把这本书的旧稿子全盘交给了黄湘云(他之前也问过我好几次了),请他帮忙把原始的 LaTeX 格式转化为更简单的 R Markdown 格式,结果他三下五除二很快就把这事搞定了,这解决了出版的两大障碍之一。另一大障碍是,由于此书写于十几年前,彼时 ggplot 还不像现在这样一统江湖,于是这本书的旧稿将重点放在 R 的基础图形上(第9章),而对 ggplot 的介绍则很简略。这个大坑,则被赵鹏卷起袖子给填了,书中凡是能用 ggplot 作的图,他都用 ggplot 重写了一遍,好歹算是把《古代统计图形》拉上了《近代统计图形》的台阶。我依然觉得“现代”二字有点名过其实,但现在总算不像一年前那么心虚了。 9 | 10 | 话说回来,我们当然不能说用什么工具作图才称得上“现代”。“现代”与否,不应该与工具挂上钩,否则我们一来容易陷入工具崇拜,二来也很容易变成拿着锤子找钉子的人,也就是查理芒格说的“铁锤人倾向”。如果不注意作图的目的和原则,那么优秀的作图工具也一样可能制造图形垃圾,就如同 LaTeX 也绝对可以排出把人丑哭的版式一样。也许有读者还记得,我在最早版本的序言中引用过顾炎武在《日知录》中引用《易经》中的一句话:“形而上者谓之道,形而下者谓之器。”那时候年少轻狂,引这种话有卖弄之嫌(装作很厉害的样子),不过也有一份真心实意在里面,也就是期待读者能得“道”。如今不敢说什么“道”不“道”的大话,只能说要是本书对读者有所启发的话,则善莫大焉。 11 | 12 | 我本人当然不算图形领域的专家,而且这些年的工作重心离这个领域越来越远,但我也斗胆讲我的两个观察,不知是否确切: 13 | 14 | 一是随着数据科学的浪潮,数据可视化也被推上了浪尖,图形成了数据科学不可或缺的一个组成成分,但我们似乎越来越依赖现成的作图工具和系统(哪怕自己写代码作图,也是用现成的库),难以见到新颖的数据展示方式,而那些新颖的图形,往往需要用更原始和底层的方式创造出来。在第2章中,我提到用写代码的方式作图能提供高度的定制性,意即越高层的工具,定制性相应也会更弱,所以也更容易束缚创新。有鉴于此,时至今日,我仍然认为读者不妨了解一下 R 的基础作图系统,而不必随大流、认准了 ggplot 不撒手。基础作图系统用起来当然是繁琐一些,但它提供了所有的图形元素供你调遣,而且对数据形式也没有任何假设(不必非得是整齐的数据框),有时候可能会更方便和自由。注意,我是学基础作图系统长大的,所以我的这个观念可能有偏差。当年我玩这些点线面的时候,感觉就像玩画笔,可以说乐在其中、不能自拔,比如图 9.6 让我兴奋地发现原来渐进色可以这样创造(虽然很低效),图 6.4 是我受那幅著名的拿破仑远征图(图 1.6)启发而创造出来的,图 3.14 中的篮球场地则是我按照场地标准尺寸“一笔一划”地用点线圈画出来的(可见曾经有多闲)。第9章最后那个画温度计的练习,可能是我当年沉迷基础作图系统的最好例证。打个比方,用 ggplot 或其它高层作图系统就像是上帝捉住你的手在画图,而用基础图形系统则需要你捉住上帝的手来画。 15 | 16 | 二是如今画图似乎朝着美观方面一边倒,而难以见到把数学原理与图形结合表达的例子。我不太明白这是果真成了看脸的时代,还是说统计理论与图形的隔阂更深了。坦言之,我读研究生之后开始不太喜欢数学,但有时候看到一种数学方法以图形的方式巧妙表达出来时,还是觉得很惊喜的。例如第 7.8 小节中的四瓣图,和第 6.12 小节中的调和曲线图。对于后者,我本科大四时还仔细验证过那个欧氏距离(当然现在恐怕三角函数的积分都忘了),并感叹这家伙是怎么想出这么绝妙的方法来的。不知这些“古代统计图形”,能否启发我们创造出更多有数学灵魂的图形?其实也未必一定要追求这些看起来高端的东西,有时候一个简单的想法也许就能启发我们,例如图 5.3 中的向日葵散点图,它的想法很简单,而名字又多有诗意。我想表达的意思是,一幅图不管用什么形式表达,只要你注入了特别的心意,它自然会萌发出生命力而动人。正如中岛美雪一首歌所唱的:生命的别名就是心。 17 | 18 | 所以就算这本书稿拖了十四年,我自问仍是有一定的出版价值的;内容方面依旧有一些闪光点,只不过以我如今的文字标准,有些地方文绉绉的表达我自己也看不惯了。我猜这本书至少会有两类购买者:一类是等它等了十几年的,不为别的,就图买个情怀,以纪念逝去的青春,也许买回去最终只是吃火锅的时候垫桌脚;另一类是冲着鄙人的虚名来的(我为你们赐名“冲虚道长”)。对第一类读者,我只想说,吃火锅的时候请叫上我;对第二类读者,你们来就来,还买什么东西嘛,非要买的话,我也只好第无数次重复我的告诫:读书的时候自己多判断,不要被我一面之词忽悠入坑。 19 | 20 | 本书每章开头都挑选了一段《福尔摩斯探案集》中的文字,其内容与各章内容有一定关联(有些关联需要一定的脑洞才能理解),这也是由于我个人在上高中时就喜欢看福尔摩斯,并且我认为统计图形也可以看作是一种小小的“探案”。探案集中我最喜欢的一篇是《血字的研究》,尤其欣赏该篇的第二部分中大篇的景色描写,以及对主人公杰弗逊·霍普坚韧不拔性格的刻画,这种波澜壮阔的笔法,令我着实艳羡不已,只可惜我没这种文字功夫能把书写得如此吸引人,于是只能寄希望于“一图胜千言”了。 21 | 22 | 最后,我要感谢在写作过程中给我提供过帮助的人们,包括我在中国人民大学本硕期间的导师赵彦云老师、人大学弟学妹和统计之都的朋友们(如魏太云、邱怡轩、郑冰、李皞、方莹、李丰、王晓伟、李承文、肖楠、姜晓东等等)、爱荷华州立大学的师友们(如殷腾飞、我的导师 Di Cook 和 Heike Hofmann)。本书修订过程中也收到了来自张列弛、Song Li、JackieMe、Yang Cao、Jonie Yao、tiansworld 等人的贡献。这本书先后得到了多位编辑的付出,包括周筠老师(约十年前)、卢鸫翔编辑、陈兴璐编辑、王军花编辑等,其间我掉过链子,很不好意思,在此觍着脸一并感谢。当然,这本书最终的出版,离不开我的两位苦力合作者黄湘云和赵鹏;要不是他们玩命推,我估计这本书稿可能真的要留给未来的考古队来发掘了。 23 | 24 | ```{block2, type = 'flushright', html.tag = 'p'} 25 | 谢益辉 26 | 于美国奥马哈 27 | ``` 28 | -------------------------------------------------------------------------------- /principle.Rmd: -------------------------------------------------------------------------------- 1 | # 原则 {#chap:principle} 2 | 3 | ```{r,include=FALSE} 4 | library(MSG) 5 | ``` 6 | 7 | > “情况的确如此,我在这方面有直觉。偶尔也会出现一些较复杂的案子,那我就得忙碌一阵子,亲自去查访一番。要知道,我有许多特殊知识,可以用来解开这些谜团,而且能轻易地解决问题。那篇文章中讨论的推理的原则,让你很鄙视,但对我的实际工作却是无价之宝。敏锐的观察力是我的第二天性。我们俩第一次见面时;我说起你是从阿富汗来的,你那时似乎很惊讶哩。” 8 | > 9 | > --- 柯南·道尔《血字的研究》 10 | 11 | 从技术角度来说,作图是一件很容易的事情,但作一幅好的统计图形则并非易事,它需要一些指导原则。那么图形优劣的评判标准是什么?最直接的标准就是,读者能否通过图形清楚地了解数据中的信息。这涉及到对读者群体的心理学研究和对数据的反复思考,比如,饼图和条形图分别用角度和长度来表达数值大小,那么人眼对角度和长度的感知精度是一样的吗?心理学调查结果显示并非如此:人眼对角度的感知较差。迄今为止,专门做过统计图形方面的心理学研究的统计学家寥寥无几,其中成果最显著的当属 @Cleveland85 ,本章也主要基于他的一些观点进行总结与展开。 12 | 13 | ## 数据至上 {#sec:data-priority} 14 | 15 | 数据是宝贵的,它们也许来自艰辛的问卷调查,或是繁琐的实验测量,因此我们应该尽量珍惜,但现实状况是我们经常有意或无意糟蹋数据,这样的情形包括:表达数据的元素被次要图形元素遮挡,数据的特征无法在图中凸显,或者数据经过了不恰当的人工处理等。 16 | 17 | ### 分清主次 {#subsec:element-priority} 18 | 19 | 对于一幅图形而言,显然并非所有的图形元素都同等重要。例如,散点图中的点应该是最重要的元素,等高线图中的线更重要,等等。因此,我们不能让次要的图形元素干涉数据的表达,要让图形显得干净、清晰。主要元素的外观要仔细选择,使数据在图中占有最重要的视觉地位,而不会被标签等元素遮挡或干涉。用 Cleveland 的话说,就是要让数据突出来(stand out)。 20 | 21 | R 基础图形系统中默认的点的样式是空心点,在很多情况下这并不是一个好的选择,因为空心点在图中看起来太不起眼,尤其是数据点较少的时候。我们延续 \@ref(subsec:tvearn) 小节中的美剧演员收入数据,在这里讨论如何突出主要元素。图 \@ref(fig:pay-rating-points) 是演员收入与电视剧评分的散点图,左图使用了默认的空心点,右图使用了实心点。这批数据的样本量只有 72,左图中的点所用的墨水可能和坐标轴等次要元素差不多,所以数据在图中也显得不够突出,而实心点则很明显占据了一幅图的视觉重心。 22 | 23 | ```{r pay-rating-points,fig.scap="演员收入与电视剧评分的空心和实心散点图",fig.cap="(ref:pay-rating-points)",fig.width=7,fig.height=3.5} 24 | par(mfrow = c(1, 2)) 25 | data("tvearn", package = "MSG") 26 | plot(pay/10^4 ~ rating, data = tvearn, ylab = "pay (10^4)") # 默认为空心点 27 | plot(pay/10^4 ~ rating, data = tvearn, pch = 19, ylab = "pay (10^4)") # 改为实心点 28 | ``` 29 | 30 | (ref:pay-rating-points) 演员收入与电视剧评分的空心和实心散点图:右图的视觉冲击力更强。 31 | 32 | 这两幅图实际上有个共同的问题,就是图中空白区域太大,这是由最高收入的那位演员引起的。这种情况也从一定程度上降低了图形元素的表达效率,因为我们放眼望去,一幅图的一半区域都是空白,绝大部分点都集中在图的下半部分。当然,在这个问题上我们也不能绝对化,因为有时候这种大片空白能反衬处离群点 — 取决于我们要显示的重点是什么。当图中存在离群点时,解决办法之一就是取对数,这种办法能减轻数量级的影响,数据越大,则被拉向原点的幅度越大。我们都知道,取对数的前提条件是数字全都大于零,正好本例中的收入数据满足这个条件。对收入取过对数之后的散点图如图 \@ref(fig:pay-rating-log) 上图,由于此时纵坐标的刻度意义变了,我们在读图的时候需要了解数字 n 实际上代表的是 $10^{n}$,例如 6 与 5 的差距并非 1,而是 10 倍,即 $10^{6-5}$。 33 | 34 | 如果我们关心的对象是收入和评分的关系,那么一幅散点图就足够了;如果我们还想进一步从图中了解每个点代表的演员是谁,那么我们就需要往图中加文本标签。标签是有很强显示力的工具,它能直截了当告诉我们信息,然而由于它的体积相对较大,若处理不当,反而会让图中充满文本信息,从而失去了数据本身的意义。在本例中添加姓名标签不容易做到自动化,即自动安排标签的位置让它们不要重叠,因为本身这幅图中的点就已经有重叠,不过 **maptools** 包中的 `pointLabel()` 函数提供了基于模拟退火算法和遗传算法的添加标签方案,它能尽量做到不让标签重叠。事实上即使用这些算法来添加标签,也仍然会出现大量的遮挡现象,如图 \@ref(fig:pay-rating-log) 下图,这也是受本书版面大小所限,读者可以运行代码在更大的图形窗口中查看结果,重叠程度应该会轻一些。 35 | 36 | 37 | ```{r pay-rating-log,fig.cap="(ref:pay-rating-log)",fig.scap=" 取对数的收入与评分散点图以及演员名称 ",fig.width=7,fig.height=3.5} 38 | par(mfrow = c(1, 2), mar = c(3.2, 3.6, .05, .05)) 39 | plot(log10(pay) ~ rating, data = tvearn, pch = 19) 40 | plot(log10(pay) ~ rating, data = tvearn, pch = 20, ylab = "", col = "red") 41 | with(tvearn, car::pointLabel(rating, log10(pay), labels = actor, 42 | cex = .6, col = "#00000099", xpd = TRUE)) 43 | ``` 44 | 45 | (ref:pay-rating-log) 取对数的收入与评分散点图以及演员名称:纵轴的收入数据经过了以 10 为底的对数处理;下图中加上了演员的名称。 46 | 47 | 48 | 顺便提一下,关于文本标签的使用,多维标度分析(Multidimensional Scaling,MDS)是一个非常适合以标签展示为主的统计学方法。简言之,MDS 的思想是将高维空间中个体之间的距离在低维空间中尽量准确地表达出来,这里的低维空间通常是二维平面。因为我们关心的重点是个体与个体之间的距离,那么最好将个体的某种特征画在图中,最直接的想法当然就是个体的名称,此时图的重点就是这些名称标签,所以尽管标签的背后对应着坐标点,我们也不必把点画出来,甚至坐标轴都可以完全去掉。图 \@ref(fig:music-mds) 是 \@ref(subsec:music) 小节提到过的音乐数据的 MDS 平面图,这幅图没有坐标轴,也没有点,只有曲目名称,这就足够了,因为这些名称之间的距离就是它们在标准化之后的原始数据上的距离(注意原始数据有 10 个变量,这里降维为 2),从图中我们可以很快看出曲目之间的相似性。 49 | 50 | 51 | ```{r music-mds,fig.cap="(ref:music-mds)",fig.scap=" 音乐数据的多维标度分析平面图 ",fig.width=4,fig.height=4} 52 | data("music", package = "MSG") 53 | par(mar = c(0, 2, 0, 0)) 54 | # 标准化所有频率变量到 0-1 之间并计算曲目之间欧式距离 55 | st.music <- apply(music[, -(1:2)], 2, function(x) { 56 | (x - min(x)) / (max(x) - min(x)) 57 | }) 58 | fit <- cmdscale(dist(st.music)) 59 | plot(fit, type = "n", ann = FALSE, axes = FALSE) 60 | text(fit[, 1], fit[, 2], rownames(music), cex = .7, xpd = TRUE) 61 | ``` 62 | 63 | (ref:music-mds) 音乐数据的多维标度分析平面图:Eels 的 Saturday Morning 离所有曲目最远,维瓦尔第的 V8 也比较独特。 64 | 65 | 关于图形元素主次关系,还牵涉到一些细节设置问题,这也是我们在附录 \@ref(cha:tricks) 介绍那么多图形细节的原因之一。例如,坐标轴的刻度短线的方向默认朝外(`tcl` 参数),这是合理的设置,如果刻度线朝内伸去的话,就可能会干涉到作图区域的元素;又如 `xaxs` 和 `yaxs` 参数,它们默认会先让作图区域的范围向外扩展 4\%,这样坐标轴和作图数据的边界之间就留出了一片小空间,数据中的最小值和最大值都不会紧贴坐标轴,也能让主要图形元素充分显示出来,不受坐标轴线的干扰。 66 | 67 | ### 符号明确可分 {#subsec:clear-symbols} 68 | 69 | 我们经常遇到需要在图中表达分组信息的情况,如图 \@ref(fig:point-iris),此时我们应该选择差异最大的外观,以免各组数据无法区分开来。例如空心圆圈和空心方框的区别就不够显著,但空心圆圈和实心圆圈就有很明显的区别。为了检验两种符号是否有足够的区分度,我们可以用 **MSG** 包中的 `char_gen()` 函数生成一个字符方阵,看我们是否能从中快速找出不同的字符,例如 O 和 Q 很相似,所以从一群 Q 的方阵中找一个 O 可能就很困难,但从一群 Q 中找星号*则要容易得多: 70 | 71 | ```{r char-gen-demo} 72 | char_gen(c("O", "Q"), n = 320, nrow = 8) # 从 Q 中找 O 73 | char_gen(c("*", "Q"), n = 320, nrow = 8) # 从 Q 中找 * 74 | ``` 75 | 76 | 关于符号使用的问题,@Robinson03 是一篇很好的参考短文,这里我们不再深入介绍,但是要提醒注意的是,尽管我们可以小心挑选外形差别大的符号,当数据分组数目特别大的时候这种努力往往过犹不及,因为人眼的识别能力毕竟有限,太多太杂的符号混在同一幅图中很可能无法清楚表达任何信息,仅仅是制造图形垃圾而已。 77 | 78 | ### 谨慎处理数据 {#subsec:data-processing} 79 | 80 | 通常数据只有经过处理才能揭示我们想要知道的信息,例如给我们全国所有人的身高数据只会让我们被淹没在数字中,但一个均值或者中位数就能告诉我们身高的平均水平。人们可能因为这个原因形成了处理数据的习惯,但这对于统计图形来说往往是灾难,宝贵的原始信息被毁灭于人为处理。在图形中,我们提倡尽量表达原始数据,而不要人为处理数据,包括不要省略数据,以及不要离散化数据。 81 | 82 | @Cleveland85 中给了一个省略数据的例子,可以看作是 1986 年美国挑战者号航天飞机失事的原因之一,大意是航天局的工程师们在发射之前研究了 O 型环的故障与温度的关系,他们看的是一幅散点图,横轴为温度,纵轴为 O 型环发生故障的数量,从散点图中来看,温度与这个零件的故障数量并没有什么联系,然而最终挑战者号还是因为温度原因发射失败并解体爆炸。那么这幅散点图有什么问题呢?首先,它只画出了零件失效的情况,而省略了零件未失效的那些观测数据,退一步讲,他们的散点图中即使没有观察到零件失效与温度的关系,也不能代表温度与零件不失效没有关系,而事实是如果把零件未失效的数量和相应的温度加上去的话,我们就能观察到低温情况下 O 型环容易发生故障;其次,这幅散点图中的温度范围不够大,而发射当前的气温是 31 华氏度(零下 1 摄氏度),属于超低温,这样的情况也没有在以往数据中观察到,因此这个发射行动是非常鲁莽的。 83 | 84 | 图形相比起表格的优势之一就是它能以较小的空间展示很多信息,10 行数据和 1000 行数据占用的空间可能没有区别,而表格则不然,数据越多就需要更大的空间展示。我们几乎没有必要刻意删减原始数据再画图,即使需要删减,通常也应该在看完全局数据之后再决定看局部数据。 85 | 86 | 离散化数据是人们更常用的数据处理手段,并且这种手段的缺点更不容易被发觉。所有离散化,就是将原本连续的数据人为分组,例如,将年龄分为 0-5 岁、5-10 岁、……。作者猜想这种处理方式一方面是陈旧的计算手段留下来的糟粕,因为分组统计更容易计算,另一方面也是受一些基于分类数据统计方法的引诱,例如列联表的各种“精美”分析,换句话说,我们在拿方法硬套数据。为什么我们不推荐将连续数据离散化?原因非常简单:连续数据包含的信息比离散数据多,离散化处理会损失信息。或者通俗解释:你问一个人的年龄,若得到的回答是“20 岁到 50 岁之间”,你必然觉得不够满意。 87 | 88 | 图形中的离散化现象很普遍,如 \@ref(sec:base-graph-ext) 小节的风向图就是一个例子,当然风向图中的离散化也许有一定道理。更多情况下是不必要的离散化,如根据不同年龄组计算身高的均值,这种情况下我们完全可以画身高和年龄(连续变量)的散点图,此时身高和年龄的关系一目了然,而不需要从一组组均值中去看它们的关系。更严重的问题是,离散化的分组往往带有任意性,我们可以按 5 岁一个区间分组,也可以按 10 岁一个区间,这种任意性的存在可能会导致结果的截然不同,甚至让我们得出相反的结论。 89 | 90 | ```{r cut-plot,fig.cap="(ref:cut-plot)",fig.scap=" 任意离散化连续数据得到的不同结果 ",fig.width=6,fig.height=3} 91 | set.seed(319) 92 | x <- rnorm(100) 93 | y <- rnorm(100) 94 | par(mfrow = c(1, 2)) # 以下 cut_plot() 函数来自 MSG 包 95 | cut_plot(x, y, c(-2.02, -0.9, -0.3, 1, 2, 2.5), col = "gray") 96 | cut_plot(x, y, c(-2.02, 0, 0.25, 0.5, 2.8, 3), col = "gray") 97 | ``` 98 | 99 | (ref:cut-plot) 任意离散化连续数据得到的不同结果:左右散点图中数据完全相同,只是离散化分组区间不同,导致趋势截然不同。 100 | 101 | 图 \@ref(fig:cut-plot) 展示了离散化数据的一种弊病:左右两幅图中的数据完全相同,只是两幅图中我们用了不同的分组区间去将变量 x 离散化为 5 组,竖着的虚线表示分组的端点,在每一组内我们计算 y 的均值并连线,左图中我们看到数据有下降趋势,右图则显示为上升趋势,而事实是 x 和 y 是独立的,毫无关系。这就像盲人摸象的故事 — 有人摸到了耳朵说它像扇子,有人摸到了尾巴说像绳子。 102 | 103 | 同样我们也不能将“不处理数据”这条原则绝对化,有一种情况下处理数据会让图形表达更清楚,那就是从原始数据中不易直接观察的数据,例如两条折线的差异,或两组数据均值的差异,或观察增长率。 @Xie10Thesis 分别使用等方差和异方差假设对同一批数据作了 t 检验并得到 P 值,为了比较这两组 P 值的差异,作者将它们作差再画图,原因是绝大多数 P 值都非常接近,若直接画两组 P 值的散点图,那么得到的几乎是一条直线,看不出差异,而作差之后,差异就变得很明显了。这样的数据处理并没有改变原始数据的性质,只是将同样的数据换一种形式表达,没有损失任何信息。读者可以再回顾第 \@ref(cha:history) 章中的图 \@ref(fig:playfair86),这幅图可以换个角度来表达,即画出出口减去进口的值,这样只需要和 0 对比就知道顺差逆差的情况了。 104 | 105 | 还有一种可能需要略微处理数据的情况,就是当数据中重叠的点很多时,我们要想办法让读者能够读出这些重叠的信息,随机略微打乱数据点的位置是一种处理办法(参见图 \@ref(fig:discrete-var)),尽管它看起来修改了原始数据,但只要经过充分的解释说明,读者应该不会被误导;如果我们不处理数据,当然也有办法表达这些重叠信息,如 \@ref(sec:plot-default) 小节。 106 | 107 | 实际应用中我们常常还会遇到一种“无意识的处理数据”,即原始数据并没有经过任何中间处理,但最终画到图中的时候被无意识地改变了意义。这一点我们在 \@ref(subsec:music) 小节中曾经提到过,即“事后诸葛亮”的做法。当我们已知图中元素分类信息的时候,我们可以用一些特殊标记(如颜色)来表达这个信息,当分类信息未知时,我们就需要慎重了,典型的应用如聚类分析,有时候聚类结果可能比较牵强,但经过给数据标记颜色的处理,读者会被刻意引导到特定结论上。后面 \@ref(sec:psychology) 小节我们再看例子。 108 | 109 | 110 | ## 节约墨水 111 | 112 | 谈到墨水问题,我们不得不提及可视化大师级人物 Edward Tufte,他发明了一个有趣的词,叫图形垃圾(chartjunk),所谓的图形垃圾就是一幅图形中的多余元素,它们对表达数据毫无帮助,甚至掩盖或歪曲数据中的信息。我们身边的图形垃圾实在数不胜数,例如毫无意义的渐变色背景(有人可能会争论这是为了美观考虑),或者用复杂的图形表达简单的数据。 113 | 114 | Cleveland 也有类似的观点,提倡用尽量简单的图形元素表达尽量多的数据信息;用更量化的指标来说,就是“数据/墨水比”要尽量高,意思是用少的墨水打印出多的数据。Cleveland 提出他的点图(\@ref(sec:dotchart) 小节)也有此考虑,因为点图中的图形元素占用的空间小,但和条形图一样能表达出数字的大小。 115 | 116 | @Tufte01 中提到一个浪费墨水的极端例子,他本人的评论为“它可能是史上出版物中最糟糕的图形”(This may well be the worst graphic ever to find its way into print),这幅图由《美国教育》杂志发表,如图 \@ref(fig:worst-graph) 所示。这幅看起来很炫目的图到底画了什么?其实只是 5 个数字,记录了 1972 到 1976 年中,25 岁及以上的美国大学生录取比例;该图的标题为“大学新生年龄结构”,而这个所谓的“结构”分两类,一类是 25 岁及以上的新生,另一类是 25 岁以下的,这两个比例相加为 100\%,图下半边代表了 25 岁及以上的学生,这 5 个比例分别为 28.0\%、29.2\%、32.8\%、33.6\% 和 33.0\%,这便是这幅三维立体图形要表达的全部,图的上半边是下半边的“倒影”。Michael Friendly 在他的数据可视化网站()中也给出了评价: 117 | 118 | > 一幅图形可以用三种手段之一来装潢,一是让人眼花缭乱的颜色,二是 3D 效果,三是伪装得就像有丰富的内容一样,而这幅图动用了全部三种手段。 119 | 120 | 通过这一则例子,相信读者可以深刻体会到什么是图形垃圾。 121 | 122 | ```{r worst-graph,fig.scap="史上最糟糕的图形垃圾",fig.cap="(ref:worst-graph)",echo=FALSE} 123 | knitr::include_graphics(path = "images/worst-graph.png", dpi = NA) 124 | ``` 125 | 126 | (ref:worst-graph) 史上最糟糕的图形垃圾:一幅超级复杂的图形,一共表达了 5 个数字,即 1972 年到 1976 年中,25 岁及以上的美国大学新生比例。 127 | 128 | 129 | ## 设计布局 130 | 131 | 这里说的布局主要指 \@ref(sec:plot) 小节提到的纵横比,它是一个对图形解释非常重要的概念,尤其是折线图。简单来说,纵横比影响的是图形元素宽高的比率。“瘦高”的图中,折线的斜率大,给人的感觉是升降趋势非常剧烈,而“矮胖”的图中,趋势变化则看起来平缓一些。 132 | 133 | ```{r sunspots-asp,fig.cap="(ref:sunspots-asp)",fig.scap="不同纵横比设置下的太阳黑子时序图",fig.width=4.8,fig.height=4.8} 134 | layout(matrix(1:2, 2), heights = c(2, 1)) 135 | par(mar = c(4, 4, 0.1, 0.1)) 136 | plot(sunspots) 137 | plot(sunspots, asp = .1) 138 | ``` 139 | 140 | (ref:sunspots-asp) 不同纵横比设置下的太阳黑子时序图:上图没有特别的纵横比设置,下图的 `asp` 参数为 0.1,从下图可以看出太阳黑子数量随着时间上升比下降速度更快。 141 | 142 | R 基础图形系统中通常用 `asp` 参数设置纵横比,下面我们给一个经典例子来说明它的作用。图 \@ref(fig:sunspots-asp) 是 1749 到 1984 年太阳黑子数量的时间序列图,数据为月度数据。我们都知道太阳黑子的数量有周期性,其周期大约为 11 年,这一点在图中可以很容易看出来(折线有规律地起伏)。图 \@ref(fig:sunspots-asp) 上图使用的是默认纵横比设置,即纵横比随着图形大小自动调整,而下图中固定为 0.1,初看起来这两幅图没有什么不同,但下图揭示了一个重要的发现:太阳黑子数量上升时的速度比下降的速度更快,注意观察图中上升的折线比下降的折线更陡峭,而上图中则很难看出这个现象,因为折线斜率太大。关于图 \@ref(fig:sunspots-asp) 我们要补充说明的是,下图并不是简单地把高度压缩了一下而已,即使图形的高度更高,图中的折线形状也不会变化,关于这一点读者可以自行验证。 143 | 144 | Cleveland 对这个问题的建议是调整纵横比让所有的折线的倾斜角度平均值接近 $45\,^{\circ}$(banking to $45\,^{\circ}$),因为人眼对 $45\,^{\circ}$ 附近的角度感知最精确,而对太大或者太小的角度感知都很差,例如图 \@ref(fig:sunspots-asp) 中为什么我们更难看出上图中的折线角度差异? 145 | 146 | 纵横比是一个很有用的调整图形感观的工具,换句话说,用它来撒谎也是很容易的,不过如今的读者应该都能识破这种小伎俩了。 147 | 148 | ## 附带解释 149 | 150 | 尽管我们说“一图胜千言”,但图形本身对于不同读者来说可能会有不同的解读,甚至有些读者未必能理解一幅图的真正意思,这时候就很有必要提供附带文字解释。附带解释有两种方式,一种是向图中加上文本标注,这种方式有很大的局限性,因为图的空间毕竟有限,而且过多的文本标注可能会使图形本身失去重心(\@ref(subsec:element-priority) 小节);另外一种方式就是图的标题,据作者的观察,这一点在英语文献中似乎做得相对好一些,图形通常有明确的标题,而且标题就像一段完整的话,但大多数中文文献中的图都惜字如金,图的标题只有一句话,关于图的解释通常放在正文中,这种做法可能会让读者无法专注于图形的阅读,因为需要不断回到正文结合相应的解释文字来理解图形。 151 | 152 | 理想情况下,一幅图配上相应的标题文字解释,应该能够形成一个相对独立而完整的故事。但话说回来,如果一幅图需要太多的文字解释,那么这幅图本身的设计质量也值得怀疑,作者可能需要考虑简化图形。本书中大部分图形都遵循了添加详细标题文字解释的原则。从技术上而言,这只是 LaTeX 中 figure 环境的 `\caption{}` 而已。 153 | 154 | ## 考虑心理 {#sec:psychology} 155 | 156 | 图形中有许多的心理因素需要考虑,相信读者应该看过一些关于视觉欺骗的图片,图 \@ref(fig:optical-illusion) 就是一个经典示例,其实红线和黑线一样长,但由于箭头方向内外朝向的问题,使得红线看起来更长。类似的心理因素还包括: 157 | 158 | ```{r optical-illusion,fig.cap="(ref:optical-illusion)",fig.scap="一个经典的视觉欺骗示例",fig.width=4.8,fig.height=1.5} 159 | set.seed(320) 160 | par(mar = c(1, 0, 1, 0), xpd = TRUE) 161 | plot.new() 162 | h <- runif(4) 163 | v <- runif(4, 0, .4) 164 | arrows(v[1:2], h[1:2], v[1:2] + .6, h[1:2], angle = 45, code = 3) 165 | arrows(v[3:4], h[3:4], v[3:4] + .6, h[3:4], angle = 135, code = 3, col = 2) 166 | ``` 167 | 168 | (ref:optical-illusion) 一个经典的视觉欺骗示例:红线和黑线谁更长? 169 | 170 | - 红色为夸张色,所以红色区域可能看起来比实际大小更大 171 | - 大区域中的填充颜色看起来比小区域更深一些 172 | - 同一个角度在不同方向上放置可能会导致它看起来不一样,例如从水平线出发的角度和从 $45\,^{\circ}$ 角出发的同一个角看起来大小不同,这会影响饼图的解读 173 | 174 | @Cleveland85 在一些心理学实验基础上将一系列视觉判断任务按照人眼感知精度从高到低排了以下顺序: 175 | 176 | 1. 位置 177 | 2. 长度 178 | 3. 斜率和角度 179 | 4. 面积 180 | 5. 体积 181 | 6. 颜色(顺序:色调、饱和度和亮度) 182 | 183 | 根据这个顺序,散点图和 Cleveland 点图表达的信息最精确,因为我们看的是点的位置,而气泡图表达信息则不太精确,因为我们要看气泡的面积。 184 | 185 | 以上的研究结论当然很重要,但这里我们还要指出另一种影响心理的做法。\@ref(subsec:data-processing) 小节提到了对数据附加标记的做法,这在聚类分析中尤其常见,并且它也对人的心理有很大的影响。例如,从原散点图中我们根本看不出聚类现象,但经过颜色或其它方式标注,我们被无意识引导到了聚类现象上。图 \@ref(fig:kmeans-cluster) 就是这样的例子:数据完全是没有规律的随机数,如果我们对它做 K-Means 聚类,结果如左图;如果做 $\alpha$ 凸包计算,结果如右图。表面上看来,这些随机数中似乎有规律,但实际上我们看到的都是假象。左图用不同样式的点作了标记,所以看起来上下分别有聚类;右图因为有连线的存在,诱使我们认为图中有个“空心”。$\alpha$ 凸包由 **alphahull** 包 [@alphahull] 生成,详细原理我们就不在这里介绍了,大意是从散点图中根据一个参数 $\alpha$ 的取值找出所有的凸包(convex hull,用一个圈包住一些点),$\alpha$ 越小则找到的凸包越多,反之越少。 186 | 187 | ```{r kmeans-cluster,fig.cap="(ref:kmeans-cluster)",fig.scap="(ref:kmeans-cluster-s)",fig.width=7,fig.height=3.5} 188 | set.seed(320) 189 | par(mfrow = c(1, 2)) 190 | x <- matrix(rnorm(200), ncol = 2) 191 | plot(x, 192 | pch = c(4, 19)[kmeans(x, centers = 2)$cluster], 193 | xlab = expression(x[1]), ylab = expression(x[2]) 194 | ) 195 | library(alphahull) 196 | plot(ahull(x, alpha = 0.4), xlab = expression(x[1]), ylab = expression(x[2])) 197 | ``` 198 | 199 | (ref:kmeans-cluster) 不存在聚类的 K-Means 聚类散点图(左)和 $\alpha$ 凸包(右) 200 | 201 | (ref:kmeans-cluster-s) 不存在聚类的 K-Means 聚类散点图和 $\alpha$ 凸包 202 | 203 | 204 | ## 统计原则 205 | 206 | 对统计图形来说,自然也应该有统计学上的考虑,这些考虑说到底仍然是以“尊重数据”为核心。本节用两个例子来说明统计图形中应该考虑的一些统计原则,一是直方图,二是误差线图。 207 | 208 | 直方图实际上也是对数据离散化分组,所以它不可避免有一定的随意性,只是这里的分组有一定的理论背景,并非像 \@ref(subsec:data-processing) 小节中提到的例子那样没有章法、毁灭信息。无论如何,它还是隐藏了原始数据,因此我们认为在画直方图(包括移动平均直方图)时,若有可能,则尽量加上密度曲线,或者坐标轴须(\@ref(sec:rug) 小节),因为密度曲线不受分组区间的影响,坐标轴须能反映原始数据的位置。试想,若有一大批数据集中正好在某个分组边界上,那么这个边界点归于左边组或右边组会在很大程度上影响直方图的形状,而在密度曲线上则会显示出这里有较高的密度值。 209 | 210 | 误差线图似乎是统计学工作者极为常用的一种图形,本书没有介绍它,因为这种图形对数据的毁灭程度往往更高。误差线图通常是一个连续变量对一个分类变量画的图,基于分类变量的每个类别,分别计算连续变量的均值 $\bar{X}$ 及标准差 $s$,然后用短横线标记出 $\bar{X}\pm m\cdot s/\sqrt{n}$ 的位置,其中 $n$ 是组内样本量,$m$ 是一个倍数,可以是 1 或 2 或其它数字,这样做的原因是短横线的标记表达了均值的置信区间(置信度取决于 $m$,例如 $m=2$ 时大约是 95\% 置信区间)。当然,这种做法理论上没有什么不对,但问题就在于原本我们有所有的连续变量数据,而误差线图把这些数据压缩为了均值和标准差,严重损失了数据信息;如果我们更挑剔一点,$\bar{X}\pm m\cdot s/\sqrt{n}$ 作为置信区间是需要假设条件的(如正态分布等),为什么我们一定要用这个需要假设前提的对称的区间呢? 211 | 212 | @Koyama10 给出了一个很好的例子来说明误差线图的弊端,这里我们也可以模拟类似的数据。图 \@ref(fig:error-bars) 展示了一幅误差线图(左)和它背后的真实数据(右)。如果只看误差线图,那么我们的印象可能是 A 组和 B 组的分布一样,因为它们的均值相同(条形图的高度代表均值),均值的标准误也相同(误差线的高度代表 2 倍的标准误),C 组和 D 组也一样。真实情况是 A、B、C、D 组背后的数据分布大不相同:A 组为均匀分布,样本量 20,B 组只有 2 个点,C 组由 1 个离群点和剩下聚成一团的 9 个点构成,D 组由两类点构成。对数据的压缩掩盖了这些完全不同的分布情况。 213 | 214 | 尽管直方图和误差线图都很流行,但读者也需要慎重考虑其使用。借用统计学家 Frank Harrell Jr 的话,“就像人有人权一样,数据也有数据权,请尊重数据权。” 215 | 216 | ```{r error-bars,fig.cap="(ref:error-bars)",fig.scap=" 误差线图的弊端 ",fig.width=7,fig.height=3.5} 217 | set.seed(321) 218 | par(mfrow = c(1, 2), mar = c(4, 4, .5, .1)) 219 | y <- c(runif(20), c(.43, .54), c(.6, runif(9, .3, .4)), 220 | c(runif(6, .5, .6) - .12, runif(4, .15, .22) + .12)) - .2 221 | x <- factor(rep(LETTERS[1:4], c(20, 2, 10, 10))) 222 | mid <- barplot(m <- tapply(y, x, mean), col = 1:4, ylim = c(0, .4))[, 1] 223 | s <- 2 * tapply(y, x, sd) / sqrt(table(x)) 224 | arrows(mid, m - s, mid, m + s, code = 2, col = 1:4, angle = 90, length = .15) 225 | stripchart(y ~ x, vertical = TRUE, method = "jitter", pch = 20) 226 | ``` 227 | 228 | (ref:error-bars) 误差线图的弊端:左图为常见的误差线图,右图是误差线图背后的完整数据。 229 | 230 | ## 思考与练习 231 | 232 | 1. 我们可以把 \@ref(subsec:clear-symbols) 小节中的找 `O` 和找 `*` 的任务当作一个游戏请你的朋友来玩,若有可能,请记录下他 / 她完成这两个任务分别使用的时间以及相关背景信息(在不侵犯隐私的情况下)并发给作者;或者用 **MSG** 包中的 `char_gen()` 函数生成更多任务去玩。 233 | 234 | 2. Cleveland 提出了 \@ref(sec:dotchart) 小节介绍的 Cleveland 点图,它的优势之一是数据 / 墨水比相对较高,因为一个点占用的面积比一个矩形条要小得多,看起来这些点也表达了和条形图等量的信息。仔细观察 Cleveland 点图,你认为它是否“让数据突出出来”了?或者你认为点图读起来方便吗? 235 | 236 | 3. @Lane09 是一篇相对较新的关于作图原则的论文,它总结了不少有用的原则,请阅读这篇论文并考虑其中的原则是否都有足够的说服力。例如作者建议通常情况下不要用背景颜色,而我们知道 ggplot2 系统的图形通常都带有灰色背景,它们是否有冲突? 237 | 238 | 4. Michael Friendly 的数据可视化网站是一个具有丰富图形资源的网站,尤其值得称道的是他对统计图形历史的资料总结,同时他也给了很多劣质图形的例子,比如某杂志封面上关于康奈尔大学的学费和排名折线图(),可以说是极具误导性,请仔细阅读这些案例,并寻找我们身边的杂志和媒体中有哪些糟糕的统计图形。 239 | 240 | 5. 压缩数据导致损失信息并不是统计图形特有的现象,我们身边经常能看到这种例子。比如我们看到一些重要的统计数据只公布均值的时候,甚至觉得不平,感觉就像“被(均值)代表”了一样。我们应该采取怎样的行动让人们拥有足够的保护原始数据的意识? 241 | 242 | -------------------------------------------------------------------------------- /programming.Rmd: -------------------------------------------------------------------------------- 1 | \cleardoublepage 2 | 3 | # (APPENDIX) 附录 {#appendix .unnumbered} 4 | 5 | # 程序初步 {#chap:programming} 6 | 7 | 如第 \@ref(cha:tools) 章所讲,R 的编程方式是面向对象(Object-Oriented)的,这里我们把 R 中的数据类型简要介绍一下,以便读者能熟练操纵数据;此外,我们也简要介绍一下 R 编程中的选择与循环语句以及输入输出的操作。 8 | 9 | ## 对象类型 {#sec:object} 10 | 11 | 在 R 的系统中,几乎任何东西都是对象 \index{对象}。使用对象的好处在于它们都可以重用(Reuse)。例如我们可以建立并拟合一个回归模型(不妨称之为 fit),这个对象中包含了若干子对象,在后面的计算中我们随时可以调用这个对象中的子对象,如残差向量(`fit$residuals` 或 `resid(fit)`)、系数估计(`fit$coefficients` 或 `coef(fit)`)等。面向对象的编程方式尤其在涉及到大量计算的工作中会大显身手,刚才我们提到的只是做一个回归模型,看起来优势并不明显,但如果我们想用某个因变量针对 1000 个自变量分别作回归,然后看看回归系数的 t 值或者 AIC 值的分布情况等等,这时“对象”操作的便利性就充分体现出来了,相比之下,读者不妨考虑用 SPSS 或其它软件如何完成类似的任务及其难度。掌握了 R 的对象之后,在 R 的世界编程基本就可以畅通无阻了。 12 | 13 | ### 向量 14 | 15 | 向量 \index{向量}(vector)是最简单的数据结构,它是若干数据点的简单集合,如从 1 到 10 的数字: 16 | 17 | ```{r vector-demo1} 18 | 1:10 19 | ``` 20 | 21 | 通常我们可以用函数 `c()` 拼接一些数字或字符生成一个向量,如: 22 | 23 | ```{r vector-demo2} 24 | c(7.11, 9.11, 9.19, 1.23) 25 | ``` 26 | 27 | 我们可以将一个向量赋值给一个变量: 28 | 29 | ```{r vector-demo3} 30 | (x <- c(7.11, 9.11, 9.19, 1.23)) 31 | ``` 32 | 33 | 注意 R 中赋值符号可以是 `<-` 或 `=`,或 `->`(从左往右赋值),或者使用 `assign()` 函数进行赋值。向量的运算一般都是针对每一个元素的运算,如: 34 | 35 | ```{r vector-demo4} 36 | 1 / x 37 | x + 1 38 | ``` 39 | 40 | 实际上以向量的形式进行元素运算是 R 语言计算的重要特征。通过中括号和下标值可以提取向量中的元素或者改变相应位置的元素: 41 | 42 | ```{r vector-demo5} 43 | x[c(1, 4)] 44 | (tmp <- x) # 将 x 赋值给 tmp 45 | tmp[1] <- 10 46 | tmp 47 | ``` 48 | 49 | 利用现有的向量可以继续利用 `c()` 生成新的向量: 50 | 51 | ```{r vector-demo6} 52 | (y <- c(x, 12.19)) 53 | ``` 54 | 55 | 向量的长度可以用 `length()` 获得: 56 | 57 | ```{r vector-demo7} 58 | length(y) 59 | ``` 60 | 61 | 我们还可以用 `names()` 给向量的每一个元素命名: 62 | 63 | ```{r vector-demo8} 64 | names(x) <- LETTERS[1:length(x)] 65 | x 66 | ``` 67 | 68 | 对于有名称的向量,我们可以用名称提取向量的元素(获取数据子集的方式通常有三种:整数下标、子对象名称以及逻辑值): 69 | 70 | ```{r vector-demo9} 71 | x[c("B", "A")] 72 | ``` 73 | 74 | 函数 `sort()` 可以对向量排序(顺序或倒序): 75 | 76 | ```{r vector-demo10} 77 | sort(x) 78 | ``` 79 | 80 | 因为很多统计量的计算是针对一维数据的,所以用向量操作起来会非常方便,例如计算样本方差 $\sum_{i=1}^{n}(x_{i}-\bar{x})^{2}/(n-1)$: 81 | 82 | ```{r vector-demo11} 83 | sum((x - mean(x))^2) / (length(x) - 1) 84 | var(x) # 自带函数 var() 作为对比 85 | ``` 86 | 87 | 当然 R 提供了现成的方差函数 `var()`,我们不必将代码写得那么复杂,从上面的输出可以看出,直接根据公式写的代码和方差函数计算的结果是一样的。另外,向量操作可以节省显式循环的使用,如果在 C 语言或 VB 等其它程序语言中,我们只能使用几段循环来计算方差数值,因为其中涉及到两个求和函数。 88 | 89 | 使用函数 `seq()` 和 `rep()` 可以生成规则的序列,前者提供了等差数列的功能,后者可以将向量或元素重复多次从而生成新的向量,如: 90 | 91 | ```{r vector-demo12} 92 | 10:1 # 冒号表示步长为 1 或-1 的序列 93 | seq(7, 9, .2) # 步长为 0.2 94 | seq(7, 9, length.out = 6) # 生成向量长度为 6 95 | rep(2, 10) 96 | rep(1:3, 5) # 整个向量重复 5 次 97 | rep(1:3, each = 5) # 每个元素重复 5 次 98 | rep(1:3, 1:3) # 每个元素分别重复 1、2、3 次 99 | ``` 100 | 101 | 向量除了可以是数值型之外,还可以是逻辑值、字符等,如: 102 | 103 | ```{r vector-demo13} 104 | (z <- (x < 5)) 105 | letters # letters 和 LETTERS 是 R 中的字符常量 106 | ``` 107 | 108 | 注意逻辑值的 `TRUE` 和 `FALSE` 可以简写为 `T` 和 `F`,但强烈建议这两个逻辑值不要使用简写 [^tf-note]。我们也可以用逻辑向量提取向量的元素,如: 109 | 110 | ```{r vector-demo14} 111 | x[!z] # 满足“非 z”的元素 112 | ``` 113 | 114 | R 中有三种特殊的值:缺失值 `NA`,非数值 `NaN` 和无穷大 / 无穷小 `Inf/-Inf`。非数值通常由无意义的数学计算产生,如 `0/0`;注意分子不为 0 而分母为 0 时,结果是无穷大。 115 | 116 | [^tf-note]: 倾向简写这两个逻辑值的人容易倾向简写任何字符,比如变量名;据作者的经验,很多人的程序出错就在这里,因为使用了 `T` 或 `F` 作为变量名,而在程序的某些地方又将 `T` 或 `F` 作为逻辑值对待,如这段条件语句 `x = 1; T = NULL; ...; if ((x > 0) == T ) ...` 会出错,因为条件 `(x > 0) == T` 返回的结果是长度为 0 的逻辑值,不符合 `if` 语句的要求。 117 | 118 | ### 因子 119 | 120 | 因子(factor)对应着统计中的分类数据,它的形式和向量很相像,只是因子数据具有水平(level)和标签(label),前者即分类变量的不同取值,后者即各类取值的名称。 121 | 122 | 因子型数据可以由 `factor()` 函数生成,如: 123 | 124 | ```{r factor-demo1} 125 | (x <- factor(c(1, 2, 3, 1, 1, 3, 2, 3, 3), levels = 1:3, labels = c("g1", "g2", "g3"))) 126 | ``` 127 | 128 | 我们可以对因子型数据求频数、将其转化为整数或字符型向量。注意整数是因子型数据的本质:它本身以整数存储,但表现出来是字符,原理就是把整数对应的标签显示出来,这种存储方式在很多情况下可以大大节省存储空间(存整数往往比存字符串占用空间小)。 129 | 130 | ```{r factor-demo2} 131 | table(x) 132 | as.integer(x) 133 | as.character(x) 134 | ``` 135 | 136 | 因子型数据在分类汇总时比较有用,例如在 `tapply()` 中: 137 | 138 | ```{r factor-demo3} 139 | y <- 1:9 140 | data.frame(y, x) # x 和 y 并列放的样子 141 | tapply(y, x, mean) # x 的每一组的均值 142 | ``` 143 | 144 | 因子型数据中有一种特殊的类型,就是有序因子,它与普通的因子型数据的区别在于各个分类之间是有顺序的,即统计上所说的定序变量;与此相关的函数为 `ordered()`。 145 | 146 | ### 数组和矩阵 147 | 148 | 数组和矩阵是具有维度属性(dimension)的数据结构,注意这里的维度并非统计上所说的“列”,而是一般的计算机语言中所定义的维度(数据下标的个数)。矩阵是数组的特例,它的维度为 2。数组和矩阵可以分别由 `array()` 和 `matrix()` 函数生成。注意矩阵中所有元素必须为同一种类型,要么全都是数值,要么全都是字符,后面我们会介绍数据框,它会放宽这个要求。这里给出一些示例说明数组和矩阵的用法: 149 | 150 | ```{r array-demo1} 151 | (x <- array(1:24, c(3, 4, 2))) # 3 维数组示例 152 | dim(x) # 维数 153 | x[, , 1] # 第 3 维第 1 个位置上的元素 154 | x[1, , 2] 155 | (x <- matrix(1:12, nrow = 2, ncol = 6)) # 矩阵示例 156 | x[1, 5] 157 | x < 5 # 逻辑比较:是否小于 5? 158 | x[x < 5] # 以逻辑矩阵为下标挑选出小于 5 的元素 159 | x^2 # 各个元素的平方 160 | x %*% t(x) # 矩阵乘法 X * X'(t() 为转置函数) 161 | # 一个随机方阵 162 | (x <- matrix(c(2, 9, 4, 5, 6, 8, 1, 3, 7), 3)) 163 | solve(x) # 求逆 164 | # 还有 eigen(x) 求特征根与特征向量, 165 | # 用 qr(x) 对矩阵作 QR 分解,等等 166 | # 我们还可以用 cbind() 和 rbind() 等函数 167 | # 将几个向量拼接成矩阵 168 | ``` 169 | 170 | 矩阵相对于数组来说在统计中应用更为广泛,尤其是大量的统计理论都涉及到矩阵的运算。顺便提一下,R 包 **Matrix** [@Matrix] 在处理(高维)稀疏或稠密矩阵时效率会比 R 自身的矩阵运算效率更高。 171 | 172 | ### 数据框和列表 173 | 174 | 数据框(data frame)和列表(list)是 R 中非常灵活的数据结构。数据框也是二维表的形式,与矩阵非常类似,区别在于数据框只要求各列内的数据类型相同,而各列的类型可以不同,比如第 1 列为数值,第 2 列为字符,第 3 列为因子,等等;列表则是更灵活的数据结构,它可以包含任意类型的子对象。数据框和列表分别可以由 `data.frame()` 和 `list()` 生成。 175 | 176 | ```{r df-demo1} 177 | # 生成一个数据框,前两列为数值型,后一列为字符型 178 | data.frame(x = rnorm(5), y = runif(5), z = letters[1:5]) 179 | # 包含四个子对象的列表 180 | (Lst <- list(name = "Fred", wife = "Mary", no.children = 3, child.ages = c(4, 7, 9))) 181 | Lst$child.ages # 用名称提取子对象 182 | Lst[[2]] # 用整数下标提取子对象 183 | # 甚至可以试试 list(first = Lst, second = 1:10) 之类的语句(list 嵌套 list) 184 | ``` 185 | 186 | 矩阵的本质是(带有维度属性的)向量,数据框的本质是(整齐的)列表。 187 | 188 | ### 函数 189 | 190 | R 中也可以自定义函数 \index{函数},以便编程中可以以不同的参数值重复使用一段代码。定义函数的方式为:`function(arglist) expr return(value)`;其中 `arglist` 是参数列表,`expr` 是函数的主体,`return()` 用来返回函数值。例如我们定义一个求峰度 $\sum_{i=1}^{n}(x_{i}-\bar{x})^{4}/(n\cdot\mathrm{Var}(x))-3$ 的函数 `kurtosis()` 如下: 191 | 192 | ```{r function-demo1} 193 | kurtosis <- function(x, na.rm = FALSE) { 194 | # 去掉缺失值 195 | if (na.rm) x <- x[!is.na(x)] 196 | return(sum((x - mean(x))^4) / (length(x) * var(x)^2) - 3) 197 | } 198 | ``` 199 | 200 | 该函数有两个参数,数据向量 x 和是否删除缺失值 `na.rm`,后者有默认值 `FALSE`;下面我们用均匀分布的随机数测试一下: 201 | 202 | ```{r function-demo2} 203 | # 理论上均匀分布的峰度应该小于 1,以下为一个模拟结果 204 | kurtosis(runif(100)) 205 | ``` 206 | 207 | 注意,R 中有时候不必用函数 `return()` 指定返回值,默认情况下,函数主体的最后一行即为函数返回值。 208 | 209 | 最后,因为本书的很多作图函数都是泛型函数,我们也补充一点泛型函数 \index{泛型函数}(generic function)的作用原理。实际上泛型函数是根据第一个参数的类(class)去调用了相应的“子函数”。以 `plot()` 为例,我们用 `methods()` 查看一下它有哪些作图方法: 210 | 211 | ```{r function-demo3} 212 | head(methods("plot"), 24) # 显示前 24 个 213 | ``` 214 | 215 | 可以看出,这些函数都是以 `plot.*` 的形式定义的,其中*便是类的名称。泛型函数的基本原理就是:传给 `plot()` 的第一个参数是何种类,则调用何种函数进行作图。例如 `iris` 的类是 `data.frame`,那么 `plot(iris)` 就会调用 `plot.data.frame()` 作图(散点图矩阵)。下面的代码有助于进一步说明这种原理: 216 | 217 | ```{r function-demo4,small.mar=FALSE} 218 | class(iris) 219 | plot(iris) # 调用 plot.data.frame() 作散点图矩阵 220 | x <- density(faithful$waiting) 221 | class(x) 222 | par(mar = c(4, 4, 3, 0.5)) 223 | plot(x) # 调用 plot.density() 作密度曲线 224 | ``` 225 | 226 | 我们也可以对现有的泛型函数进行扩充,使它们适应我们自定义的类。例如 `print()` 也是一个常用的泛型函数,当我们在 R 控制台中键入一个对象以显示其内容时,实际上是调用了该函数以打印出对象的内容。现在我们定义一个 `print.xie()`,使得类名称为 xie 的对象在屏幕上打印出来时数值为真实数值的 2 倍: 227 | 228 | ```{r function-demo5} 229 | # 本例告诉我们,眼见未必为实! 230 | x <- 1:5 231 | class(x) # 原始的类 232 | class(x) <- "xie" # 改变类 233 | x 234 | print.xie <- function(x) print.default(unclass(2 * x)) 235 | x # 打印出来的数值变成了原来的 2 倍 236 | str(x) # 但真正的构成是: 237 | ``` 238 | 239 | 至此,读者应该能够明白有些作图函数既可以直接接受数据作为参数又可以接受公式作为参数的原因了,如 `boxplot()`。 240 | 241 | ## 操作方法 242 | 243 | 在了解对象的几种基本类型之后,我们需要知道如何对这些对象进行简单的四则运算之外的操作。在计算机程序和算法中,最常见结构的就是选择分支结构和循环结构,通过这样的程序语句,我们可以进一步控制和操纵对象;同时,在执行计算机程序时,我们也常常需要一些输入输出的操作。 244 | 245 | ### 选择与循环 246 | 247 | 一般来说,计算机程序都是按代码先后顺序执行的,而有时候我们希望代码能够按照一定的判断条件执行,或者将一个步骤执行多次,此时我们就需要选择 \index{选择语句}和循环结构 \index{循环语句}的程序。 248 | 249 | R 提供了如下一些实现选择和循环的方法: 250 | 251 | - `if(cond) expr if(cond) cons.expr else alt.expr` 252 | - `for(var in seq) expr` 253 | - `while(cond) expr` 254 | 255 | `cond` 为条件(其计算结果为逻辑值 `TRUE` 或 `FALSE`),`expr` 为一段要执行的代码,通常放在大括号{}中,除非代码只有一行。 256 | 257 | 选择结构的函数还有 `ifelse()` 和 `swith()`,请读者自行查阅帮助文件。关于选择和循环,这里仅给出一个简单的综合示例,不再详加说明: 258 | 259 | ```{r if-demo} 260 | # x 初始为空 261 | x <- NULL 262 | for (i in 1:10) { 263 | rnd <- rnorm(1) 264 | # 如果随机数在 [-1.65, 1.65] 范围内则放到 x 中去 265 | if (rnd < 1.65 & rnd > -1.65) { 266 | x <- c(x, rnd) 267 | } 268 | } 269 | x 270 | ``` 271 | 272 | ### 输入与输出 273 | 274 | 对于统计程序来说,输入的一般是数据,而输出一般是图形和表格,它们在 R 中都容易实现。前者可以参考 `read.table()` 系列函数,后者则可以使用图形设备(附录 \@ref(sec:device))以及 `write.table()` 系列函数。具体使用方法请查阅这些函数的帮助文件。 275 | 276 | 277 | ## 思考与练习 278 | 279 | 1. 绝对离差中位数(Median Absolute Deviation,MAD):MAD 是对数据分散程度的一种刻画,它的定义为 $\text{MAD}(x)=C\cdot\text{median}(|x-\text{median}(x)|)$,其中 $C$ 是一个给定的常数,通常取值为 `1/qnorm(.75)`;若原数据服从正态分布,MAD 将是标准差的一个渐近估计。编写函数计算数据 `women$height` 的绝对离差中位数,并与 R 自带的 `mad()` 函数作比较;若有兴趣,请欣赏 `mad()` 函数的源代码(如何将程序写得安全而简练)。 280 | 281 | 2. 变量选择问题:某多元线性回归模型包含了 20 个自变量,根据某些经验,自变量的个数在 3 个左右是最合适的。要求建立所有可能的回归模型,并根据 AIC 准则找出最优模型以及相应的自变量。 282 | 283 | 提示:所有回归模型数目为组合数 $C_{20}^{3}=1140$,可使用函数 `combn()` 生成所有组合,用 `lm()` 建模,并用 `AIC()` 提取 AIC 值。 284 | 285 | 3. DNA 序列的反转互补:给定一个 DNA 序列字符串,要求将它的字符序列顺序颠倒过来,然后 A 与 T 互换,C 与 G 互换。例如原序列为“TTGGTAGTGC”,首先将它顺序反转为“CGTGATGGTT”,然后互补为“GCACTACCAA”。 286 | 287 | 提示:可以采用 `substr()` 暴力提取每一个字符的方式,然后用繁琐的循环和选择语句实现本题的要求;但利用 R 的向量化操作功能会大大加速计算速度,可采用的函数有:拆分字符串 `strsplit()`、反转向量 `rev()`、拼接字符串 `paste()`。仔细考虑如何利用名字和整数的下标功能实现序列的互补。 288 | 289 | 4. 接受 --- 拒绝抽样(Acceptance-Rejection Sampling):当我们不容易从某个分布中生成随机数的时候,若有另一个分布的密度函数 $g(x)$ 能控制前一个分布的密度函数 $f(x)$ 的核 $h(x)$(此处“控制”的意思是 $\exists M>0,\ h(x)/g(x)\leq M,\ \forall x$,核 $h(x)$ 正比于 $f(x)$),而且我们很方便从 $g(\cdot)$ 中生成随机数,那么我们可用“接受 --- 拒绝抽样”的方式从 $f(\cdot)$ 中生成随机数。步骤如下: 290 | 291 | (a) 从 $g(\cdot)$ 中生成随机数 $x_{i}$; 292 | 293 | (b) 从均匀分布 $U(0,1)$ 中生成随机数 $u_{i}$; 294 | 295 | (c) 若 $u_{i}\leq h(x_{i})/(M\cdot g(x_{i}))$,则记下 $x_{i}$; 296 | 297 | 重复以上步骤若干次,被接受的 $x_{i}$ 的概率密度函数即为 $f(\cdot)$。根据以上抽样步骤用适当的循环和选择语句生成服从以下分布的随机数: 298 | 299 | $$f(\theta)=c\frac{\exp(\theta)}{1+\exp(\theta)}\frac{1}{\sqrt{2\pi}}\exp(-\theta^{2}/2)$$ 300 | 301 | 其中,$c$ 为使得 $f(\theta)$ 积分为 1 的常数。 302 | 303 | 304 | 305 | -------------------------------------------------------------------------------- /references.Rmd: -------------------------------------------------------------------------------- 1 | \backmatter 2 | \printindex 3 | 4 | `r if (knitr::is_html_output()) ' 5 | # 参考文献 {#references .unnumbered} 6 | '` 7 | -------------------------------------------------------------------------------- /sidebar.lua: -------------------------------------------------------------------------------- 1 | -- https://github.com/jgm/pandoc/issues/2106#issuecomment-371355862 2 | function Span(el) 3 | if el.classes:includes("todo") then 4 | return { 5 | pandoc.RawInline("latex", "\\textcolor{red}{\\textbf{TODO: }"), 6 | el, 7 | pandoc.RawInline("latex", "}") 8 | } 9 | end 10 | end 11 | 12 | function Div(el) 13 | if el.classes:includes("sidebar") then 14 | return { 15 | pandoc.RawBlock("latex", "\\begin{shaded}"), 16 | el, 17 | pandoc.RawBlock("latex", "\\end{shaded}") 18 | } 19 | end 20 | 21 | if el.classes:includes("base") then 22 | return { 23 | pandoc.RawBlock("latex", "\\begin{shaded} \\textbf{In Base R}"), 24 | el, 25 | pandoc.RawBlock("latex", "\\end{shaded}") 26 | } 27 | end 28 | 29 | if el.classes:includes("tidyverse") then 30 | return { 31 | pandoc.RawBlock("latex", "\\begin{shaded} \\textbf{In Tidyverse}"), 32 | el, 33 | pandoc.RawBlock("latex", "\\end{shaded}") 34 | } 35 | end 36 | 37 | if el.classes:includes("rmdnote") then 38 | return { 39 | pandoc.RawBlock("latex", "\\begin{shaded}\\textcolor[RGB]{251,188,5}{\\textbf{注意}}"), 40 | el, 41 | pandoc.RawBlock("latex", "\\end{shaded}") 42 | } 43 | end 44 | 45 | if el.classes:includes("warning") then 46 | return { 47 | pandoc.RawBlock("latex", "\\begin{shaded} \\textcolor{red}{\\textbf{警告}}"), 48 | el, 49 | pandoc.RawBlock("latex", "\\end{shaded}") 50 | } 51 | end 52 | end 53 | -------------------------------------------------------------------------------- /style.css: -------------------------------------------------------------------------------- 1 | .caption { 2 | color: #777; 3 | margin-top: 10px; 4 | } 5 | p code { 6 | white-space: inherit; 7 | } 8 | pre { 9 | word-break: normal; 10 | word-wrap: normal; 11 | } 12 | pre code { 13 | white-space: inherit; 14 | } 15 | 16 | /* Sidebar formating --------------------------------------------*/ 17 | 18 | .sidebar { 19 | border: 1px solid #ccc; 20 | border-left-width: 5px; 21 | border-radius: 5px; 22 | padding: 1em; 23 | margin: 1em 0; 24 | } 25 | 26 | .rmdwarning { 27 | border: 1px solid #EA4335; 28 | border-left-width: 5px; 29 | border-radius: 5px; 30 | padding: 1em; 31 | margin: 1em 0; 32 | } 33 | 34 | .rmdnote { 35 | border: 1px solid #FBBC05; 36 | border-left-width: 5px; 37 | border-radius: 5px; 38 | padding: 1em; 39 | margin: 1em 0; 40 | } 41 | 42 | .rmdtip { 43 | border: 1px solid #34A853; 44 | border-left-width: 5px; 45 | border-radius: 5px; 46 | padding: 1em; 47 | margin: 1em 0; 48 | } 49 | .book .book-body .page-wrapper .page-inner section.normal div.rmdwarning > :first-child, 50 | .book .book-body .page-wrapper .page-inner section.normal div.rmdnote > :first-child, 51 | .book .book-body .page-wrapper .page-inner section.normal div.rmdtip > :first-child, 52 | .book .book-body .page-wrapper .page-inner section.normal div.sidebar > :first-child { 53 | margin-top: 0; 54 | } 55 | 56 | .book .book-body .page-wrapper .page-inner section.normal div.rmdwarning > :last-child, 57 | .book .book-body .page-wrapper .page-inner section.normal div.rmdnote > :last-child, 58 | .book .book-body .page-wrapper .page-inner section.normal div.rmdtip > :last-child, 59 | .book .book-body .page-wrapper .page-inner section.normal div.sidebar > :last-child { 60 | margin-bottom: 0; 61 | } 62 | 63 | div.rmdwarning::before { 64 | display: block; 65 | content: "警告"; 66 | color: #EA4335; 67 | font-size: 1.1em; 68 | font-weight: bold; 69 | margin-bottom: 0.25em; 70 | } 71 | 72 | div.rmdnote::before { 73 | display: block; 74 | content: "注意"; 75 | color: #FBBC05; 76 | font-size: 1.1em; 77 | font-weight: bold; 78 | margin-bottom: 0.25em; 79 | } 80 | 81 | div.rmdtip::before { 82 | display: block; 83 | content: "提示"; 84 | color: #34A853; 85 | font-size: 1.1em; 86 | font-weight: bold; 87 | margin-bottom: 0.25em; 88 | } 89 | 90 | .todo { 91 | display: block; 92 | border: 1px solid #4285f4; 93 | border-left-width: 5px; 94 | border-radius: 5px; 95 | padding: 0.5em 1em; 96 | margin: 1em 0; 97 | } 98 | 99 | .todo::before { 100 | content: "TO DO: "; 101 | font-weight: bold; 102 | color: #4285f4; 103 | } 104 | 105 | .flushright { 106 | text-align: right; 107 | } 108 | 109 | blockquote > p:last-child { 110 | text-align: right; 111 | } 112 | blockquote > p:first-child { 113 | text-align: inherit; 114 | } 115 | -------------------------------------------------------------------------------- /texlive.txt: -------------------------------------------------------------------------------- 1 | adobemapping 2 | amsfonts 3 | amscls 4 | animate 5 | arphic 6 | beamer 7 | cancel 8 | caption 9 | cjk 10 | cjkpunct 11 | cns 12 | colortbl 13 | cm-super 14 | ctablestack 15 | ctex 16 | dvipng 17 | environ 18 | everyhook 19 | everysel 20 | fandol 21 | fonts-tlwg 22 | fp 23 | garuda-c90 24 | helvetic 25 | jknapltx 26 | latex-base-dev 27 | luatexbase 28 | luatexja 29 | makeindex 30 | makecell 31 | mathpazo 32 | mathspec 33 | media9 34 | mptopdf 35 | ms 36 | multirow 37 | ncntrsbk 38 | norasi-c90 39 | ocgx2 40 | pdfcrop 41 | pdflscape 42 | pgf 43 | pgfplots 44 | platex 45 | platex-tools 46 | psnfss 47 | ptex 48 | ptex-base 49 | ptex-fonts 50 | preview 51 | rsfs 52 | smartdiagram 53 | sourceserifpro 54 | sourcecodepro 55 | sourcesanspro 56 | svn-prov 57 | tabu 58 | threeparttable 59 | threeparttablex 60 | titlepic 61 | translator 62 | trimspaces 63 | ttfutils 64 | type1cm 65 | ucs 66 | uhc 67 | ulem 68 | uplatex 69 | upquote 70 | uptex 71 | uptex-base 72 | uptex-fonts 73 | varwidth 74 | wadalab 75 | wrapfig 76 | xcjk2uni 77 | xecjk 78 | xpinyin 79 | zhmetrics 80 | zhmetrics-uptex 81 | zhnumber 82 | subfig 83 | tcolorbox 84 | -------------------------------------------------------------------------------- /tools.Rmd: -------------------------------------------------------------------------------- 1 | # 工具 {#cha:tools} 2 | 3 | > "好了,好了,我的好伙计,就这么办吧。我们在这房子里共同生活了好几年,如果再蹲在同一座牢房里就更有意思了。华生,我跟你说实话。我一直有个想法:我要是当罪犯,一定是超一流的。这是我在这方面难得的一次机会。看这儿!"他从抽屉里拿出一个整洁的皮制小袋,打开来亮出里面几件闪亮的工具。"这是最新最好的盗窃工具,镀镍的撬棒,镶着金刚石的玻璃刀,万能钥匙,以及对付现代文明所需要的各种新玩意儿。我这儿还有在黑暗中使用的灯。一切都准备好了。你有走路不出声的鞋吗?" 4 | > 5 | > --- 柯南·道尔《查尔斯·密尔沃顿》 6 | 7 | 那么我们面对这么多工具应该如何选择呢?我们认为主要的准则有三点:一是统计计算功能齐全,二是统计元素易于控制,三是图形类型丰富多样。 8 | 9 | ## 选择作图工具 10 | 11 | 在人们通常的观念中,图形往往代表着简单,然而直观与简单是两个不同的概念,图形的首要作用的确是直观展示信息,然而这里的信息未必是简单的。一幅优秀的统计图形背后也许隐藏着重要的统计量,而统计量是统计图形的最关键构成因素。 12 | 13 | 我们通常见到的所谓统计图形也许只是 Microsoft Office Excel 的产物,事实上,Excel 的图形总体看来只有三种,第一种是表现绝对数值大小,如条形图、柱形图、折线图等,第二种是表现比例,如饼图,第三种则是表示二维平面上的变量关系,如 X-Y 散点图;从更广泛的意义上来说,Excel 展示的几乎都是原始数据,基于数据的统计推断的意味比较淡薄。而统计学的核心研究对象是什么?答案应该是分布(Distribution),注意分布不仅包含一元变量的分布,而且更重要的是多元变量的分布,诸如"均值 "、"方差"、"相关"和"概率"等概念都可以归为分布的范畴。无论 Excel 的图形如何搭配色彩、怎样变得立体化,都跳不出上面三种类型的限制,而且不能全面妥善表达统计学的要义。可能正是因为这样的原因,统计图形界内的一位大家 Leland Wilkinson 才说给统计刊物投稿时永远不要用 Excel 作图。本书所要介绍的 R 语言能表达的统计量种类极其丰富,甚至可以毫不夸张地说,任何理论上可以计算出来的统计量都能在 R 中很方便地以图形的方式表达出来。 14 | 15 | 除了统计量之外,我们也应对图形本身的组成元素给予足够的重视,这些元素包括点、线(直线、曲线、线段和箭头等)、多边形、文本、图例、颜色等,往往这部分工作都由常见的统计软件替我们做了---我们不必自己设计点、线等基本元素,但同时也就意味着我们几乎无法灵活运用这些元素(比如在图中添加点、线、文本标注等)。表面上看似计算机软件给我们省了不少麻烦,实际上,这种限制的弊端要大于那一点微不足道的好处。我们在实际工作中遇到不少这样的例子,比如往散点图中添加若干条不同的回归直线(根据某自变量不同分类的回归、或者是不同分位数的分位回归 [@quantreg] 直线等)、在图中添加箭头或者文本标注甚至添加包含希腊字母、微积分符号、上下标的数学公式,等等,不一而足,对于这些简单问题,用传统的(商业)统计软件恐怕太难解决,原因就在于,它们让计算机替代了太多本可以由用户完成的工作。当然,总体来说,自动化是好事,但如果为了自动化而大幅度牺牲可定制性,则可能得不偿失了。 16 | 17 | 相比之下,R 语言汇集统计计算与统计图示两种功能于一身,灵活的面向对象(Object-Oriented,OO)编程方式让我们可以很方便地控制图形输出,从而制作出既精美又专业的统计图形。我们说图形并不意味着简单,指的是统计图形的构造可以很细致入微(包括统计量的选定和图形元素的设计),而不是指 R 作图的过程或程序很复杂。同样,如果我们总是需要在图形细节上耗费大量的精力,那么 R 也将不是一个好工具。在后面第 \@ref(cha:elements) 、\@ref(cha:gallery) 章和附录 \@ref(cha:tricks) 中,我们会介绍若干作图的细节问题,读者需要斟酌美观与效率之间的平衡,避免陷入细节泥潭;实际上 ggplot2 是一个可以大幅减少细节设置的图形系统,我们将会在第 \@ref(sec:ggplot2) 小节中详细介绍。 18 | 19 | 要想真正精通统计图形,则应首先要练好基本功(例如对图形基础元素或构造作深入的了解),然后在此基础上通过各种抽象、提炼和认识最终上升到理性认识的境界(自如地表达统计量)。 20 | 21 | ## R 语言简介 {#sec:R-intro} 22 | 23 | R[@base] 是一款优秀的统计软件,同时也是一门统计计算与作图的语言,它最初由奥克兰大学统计学系的 Ross Ihaka 和 Robert Gentleman 编写 [@Ihaka96];自 1997 年起 R 开始由一个核心团队(R Core Team)开发,这个团队的成员大部分来自大学机构(统计及相关院系), 24 | 包括牛津大学、华盛顿大学、威斯康星大学、爱荷华大学、奥克兰大学等,除了这些作者之外,R 还拥有一大批贡献者(来自哈佛大学、加州大学洛杉矶分校、麻省理工大学等),他们为 R 编写代码、修正程序缺陷和撰写文档。迄今为止,R 中的程序包(Package)已经是数以千计,各种统计前沿理论方法的相应计算机程序都会在短时间内以软件包的形式得以实现,这种速度是其它统计软件无法比拟的。除此之外,R 还有一个重要的特点,那就是它是自由、开源的!由于 R 背后的强大技术支持力量和它在统计理论及应用上的优势,加上目前国内对 R 的了解相对较少,我们期望能够通过本书引进并推动它在国内的广泛使用。 25 | 26 | R 的功能概括起来可以分为两方面,一是统计计算(Statistical Computation),二是统计图示(Graphics);一般而言,图形往往比较直观而且相对简易,因此本书从 R 图形入手,以期给初学者提供一份好的 R 作图入门学习资料。 27 | 28 | 安装好 R 之后,Windows 用户可以从程序快捷方式直接启动 R,Linux 用户则可从终端敲入命令 `R` 启动;在启动 R 时,屏幕上会显示类似如下信息,告诉我们 R 是由很多贡献者一起协作编写的自由软件,用户可以在一定许可和条件(GPL)下重新发布它: 29 | 30 | ```{r} 31 | cat(head(system("R -e NULL", TRUE)[-1], 16), sep = "\n") 32 | ``` 33 | 34 | 从技术上来讲,R 是一套用于统计计算和图示的综合系统,它由一个语言系统(R 语言)和运行环境构成,后者包括图形、调试器(Debugger)、对某些系统函数的调用和运行脚本文件的能力。R 的设计原型是基于两种已有的语言:S 语言 [@Becker88] 以及 Sussman 的 Scheme,因此它在外观上很像 S,而背后的执行方式和语义是来自 Scheme。 35 | 36 | R 的核心是一种解释性计算机语言,大部分用户可见的函数都是用 R 语言编写的,而用户也可以调用 C、C++ 或者 FORTRAN 程序以提高运算效率。正式发行的 R 版本中默认包括了 **base**(R 基础包)、**stats**(统计函数包)、**graphics**(图形包)、**grDevices**(图形设备包)、**datasets**(数据集包)等基础程序包,其中包含了大量的统计模型函数,如:线性模型/广义线性模型、非线性回归模型、时间序列分析、经典的参数/非参数检验、聚类和光滑方法等,还有大批灵活的作图程序。此外,附加程序包(add-on 37 | packages)中也提供了各式各样的程序用于特殊的统计学方法,但这些附加包都必须先安装到 R 的系统中才能够使用 [@Hornik-FAQ]。 38 | 39 | 本书不会过多涉及到附加包,所介绍图形主要基于 R 自身的 **graphics** 包,当然也不可避免会使用 **base** 和 **grDevices** 等基础包中的函数,这些基础包一般不用特别加载,R 在启动的时候会自动加载进来;在第 \@ref(cha:gallery) 章中会使用一些附加包介绍特殊的统计数据和统计方法、模型涉及到的图形,例如分类数据(Categorical Data)会提到 **vcd** 包,生存分析会用到 **survival** 包。当我们需要调用附加包时,可以使用 `library()` 函数,例如加载 **MSG** 包: 40 | 41 | ```{r} 42 | search() 43 | ``` 44 | 45 | R 的官方网站 中对 R 有详细介绍,我们也可以从它在世界各地的镜像(CRAN:,全称 Comprehensive R Archive Network)下载 R 的安装程序和附加包,通常我们可以在 R 中用函数 `install.packages()` 安装附加包,Windows 用户也可以从菜单中点击安装:Packages $\Rightarrow$ Install Package(s)),注意,附加包都是从镜像上下载的,因此安装时要保证网络连接正常;当然我们也可以先将程序包下载到本地计算机上然后安装。 46 | 47 | 可能令读者感到不便的一点是,R 不像别的统计软件那样有图形用户界面(GUI,即 Graphical User Interface,后面附录 \@ref(cha:GUI) 会详细介绍),因此它不会显得很傻瓜,它的界面常常是命令行界面(CLI,即 Command Line Interface),即:输入一些代码,R 就会输出相应的运算结果或其它输出结果。因此,在正式使用 R 之前,我们有必要大致了解一下 R 的运作方式。 48 | 49 | 计算机科学家 Nikiklaus Wirth 曾经提出,程序语言的经典构成是“数据结构+算法”:数据结构是程序要处理的对象,算法则是程序的灵魂。R 也不例外,它有自己独特的数据结构,这些数据结构尤其适应统计分析的需要,它们包括:向量(vector)、矩阵(matrix)、数据框(data frame)、列表(list)、数组(array)、因子(factor)和时间序列(ts)等。当然,数值、文本和逻辑数据都可以在 R 中灵活使用,附录 \@ref(sec:object) 中给出了一些简单的数据操作例子,请读者通过阅读该章熟悉 R 的数据对象;至于算法,我们暂时可以不去过多了解,因为 R 中已经包含了大量设计好的函数,对于一般的用户来讲都不必自行设计算法,除非有特殊或者自定义的算法,那么也可以根据 R 的语法规则编写程序代码。 50 | 51 | 对 R 的运作方式粗略了解之后,我们再回头看看 GUI。一个软件的菜单、按钮、对话框等 GUI 组件不可能无限增多,否则软件会变得无比庞大臃肿,而繁杂的操作顺序的难度将很可能会超过使用程序命令行的难度,而且非开源的 GUI 隐藏了计算原理,除了程序的原始编写者,无人知道输出结果究竟是以怎样的方式计算出来。随着统计学的发展,各种新方法、模型必将不断涌现,我们现在不妨试想未来的统计软件用户界面将会变成什么样子。看看 R 的发展,可以体会到这种思路:菜单不可能无限增加,但是程序、函数都是可以无限增加的---道理很简单,因为它们不受计算机屏幕的限制。迄今为止,R 的仓库(repository)中附加包的数量已经超过 14800 个,这还只是 CRAN 上的仓库,不算另外两个站点 [Bioconductor](https://www.bioconductor.org/)、 [R-Forge](https://r-forge.r-project.org/) 和 [Omegahat](http://www.omegahat.net/),以及 [Github](https://github.com) 上处于开发状态的。在这样的大仓库中,我们可以找到最前沿的统计理论方法的实现,所需做的仅仅就是下载一个通常在几十 K 到几百 K 的一个附加包。值得注意的是,R 的主安装程序大小约为 70 M。相比之下,SPSS 的安装程序已达六七百 M,而 SAS 的基本安装程序也有数百 M,从程序大小角度便可知 R 语言的精炼。 52 | 53 | 关于 R 更深入的介绍,请参考官方发行的若干手册和网站上的大量学习材料,如官方的 7 本手册(均可从 R 的安装目录或者网站 上找到): 54 | 55 | - An Introduction to R (R-intro) 56 | - R Data Import/Export (R-data) 57 | - R Installation and Administration (R-admin) 58 | - Writing R Extensions (R-exts) 59 | - R Internals (R-ints) 60 | - The R Language Definition (R-lang) 61 | 62 | 其中 R-intro 手册附录 A 中给了一个很好的代码入门演示,推荐读者通过这个演示初步熟悉 R 语言;其它手册都比较偏重 R 的底层介绍,不适合对计算机程序没有深入了解的初学者阅读;另外还有 Emmanuel Paradis 编写的《R for Beginners》也是较好的入门教材(已由本书作者及其他几位合作者翻译为中文,可从统计之都 获得);丁国徽也已将几本官方手册翻译为中文,R-intro 的中文翻译文档可以在官方网站上下载。鉴于目前 R 的中文资料并不多,我们也推荐 R 的初学者和爱好者通过访问统计之都网站及其 R 语言版块 共同学习、讨论和研究。 63 | 64 | 最后需要特别指出的是,R 拥有一套完善而便利的帮助系统,这对于初学者也是很好的资源。若已知函数名称,我们可以简单用问号 `?` 来获取该函数的帮助,比如查询计算均值的函数帮助,只需要在命令行中敲入 `?mean`,则会弹出一个窗口显示该函数的详细说明。大多数情况下 `?` 等价于 `help()` 函数;但有少数特殊的函数在查询其帮助时需要在函数名上加引号,比如 `?+` 就查不到加法的帮助信息,而 `?'+'` 或者 `help('+')` 则可顺利查到加法帮助信息,类似的还有 `if`、`for` 等。 65 | 66 | 一般来说,帮助窗口会显示一个函数所属的包、用途、用法、参数说明、返回值、参考文献、相关函数以及示例,这些信息是相当丰富的。当然,更多情况下是我们并不知道函数名称是什么,此时也可以使用搜索功能,即函数 `help.search()`(几乎等价于双问号 `??`)。例如我们想知道方差分析的函数名称,则可输入命令 `help.search('analysis of variance')`,弹出的信息窗口会显示与搜索关键词相关的所有函数,如 `aov()` 等。 67 | 68 | 知道名称以后,接下来我们就可以通过前面讲到的 "`?`" 来查询具体函数的帮助信息。函数 `help.start()` 可以打开一个网页浏览器用来浏览帮助。如果这些帮助功能还不够用,例如有时候需要的函数在已经安装的包中找不到,那么可以到官方网站上搜索:,网站上的邮件列表导航(Mailing List Archives)也是很有用的资源,其中有大批统计相关学科的著名教授以及世界各地的统计研究者和 R 爱好者在那里用邮件的方式公开回答各种关于 R 的问题。 69 | 70 | 我们在此如此强调帮助系统,目的在于告诉读者,要想学好 R 语言,除了阅读相关书籍资料,也应该自己多多动手利用信息资源解决自己的问题。 71 | 72 | ## 安装 R 语言 73 | 74 | 读者在进入下一章学习之前,可以先将 R 安装在自己的计算机上,以便阅读时可以随时打开 R 进行实际操作。R 软件可以在多种操作系统上运行,包括 Windows、Linux 以及 MacOS 等,进入官方网站主页会发现中间有 Download 一项,点击进入便可以看到世界各地的 R 镜像,任意选择一个进入,比如选择清华大学 ,我们就会看到 R 安装程序和源代码的下载页面,此时只需要根据自己的操作系统选择相应的链接进入下载即可。例如 Windows 用户应该选择链接 Windows 进入,然后下载基础安装包(base),其中 R-\*.\*.\*-win.exe 字样(\*.\*.\* 表示版本号,例如 2.12.2)的链接便是 Windows 安装程序;Linux 用户则可根据具体的系统如 RedHat、Ubuntu 等选择对应的链接,既可以用现成的二进制包,也可以自行下载源代码编译安装,但要注意如果从源代码编译,则需要自己解决依赖问题,这些依赖包如果不存在,那么运行 `./configure` 的时候会给出提示。注意 R 语言一直在开发更新中,通常每隔三个月会发布一次新版本,请读者务必注意检查自己的 R 是否为最新版本,这一点尤其 Windows 用户容易忽略,据作者本人的经验,保持 R 软件跟上最新版本通常利大于弊。 75 | 76 | 由于 R 是完全开放源代码的,所以我们可以自由修改代码以构建符合自己需要的程序,但是一方面这需要一定的其它程序语言技能(典型的如 C 语言),因为 R 的很多基础函数都是用 C 写的;另一方面,对于绝大多数用户,其实没有必要对基础包进行修改——既然 R 是开源软件,其代码必然受到很多用户监视,这样就会最大程度减少程序错误、优化程序代码,而且我们也可以在 R 里面自定义函数,这些函数可以保存起来,以后同样还能继续使用,或者自行编写 R 包。对比起来,现今的商业统计软件都将源代码和计算过程封闭在用户完全不知道的黑匣子中,而在用户界面上花大量的功夫,这对统计来说,毫无疑问并非长久之计。我们相信,随着读者对 R 的深入了解,一定能体会到这个软件的真正强大之处。 77 | 78 | 下面我们以一个例子开始 R 图形之旅,见图 \@ref(fig:ggplot2-minard) ,它是基于 **ggplot2** 包的拿破仑远征图,可以说是 R 画图灵活性的一个典型代表。 79 | 80 | (ref:fig-ggplot2-minard) 在 R 中用 **ggplot2** 包重制拿破仑远征图:我们可以对比图 \@ref(fig:minard)。 81 | 82 | (ref:fig-ggplot2-minard-s) 在 R 中用 **ggplot2** 包重制拿破仑远征图 83 | 84 | ```{r ggplot2-minard, fig.cap='(ref:fig-ggplot2-minard)', fig.scap='(ref:fig-ggplot2-minard-s)', fig.width=6, fig.height=3} 85 | troops <- read.table(system.file("extdata", "troops.txt", package = "MSG"), header = TRUE) 86 | cities <- read.table(system.file("extdata", "cities.txt", package = "MSG"), header = TRUE) 87 | library(ggplot2) 88 | p <- ggplot(cities, aes(x = long, y = lat)) # 框架 89 | p <- p + geom_path(aes(linewidth = survivors, colour = direction, group = group), 90 | data = troops, lineend = "round") # 军队路线 91 | p <- p + geom_point() # 城市点 92 | p <- p + geom_text(aes(label = city), hjust = 0, vjust = 1, size = 2.5) # 城市名称 93 | p <- p + scale_colour_manual(values = c("grey50", "red")) + 94 | scale_size(range = c(1, 10)) + 95 | theme(legend.position = "none") + 96 | xlim(24, 39) # 细节调整工作 97 | print(p) # 打印全图 98 | ``` 99 | 100 | ## 思考与练习 101 | 102 | 1. 大多数开源软件都会特别强调说明 ABSOLUTELY NO WARRANTY,这是否让你感到开源软件质量没有保证?CRAN 上的 R 包已经数以千计,而这些用户贡献的附加包几乎都是没有人去检查质量好坏(服务器上只做一些例行检查如是否有语法错误),我们应该以什么样的原则去使用附加包?盲目信任、完全摒弃或者自行测试? 103 | 104 | 1. 统计图形和统计计算有怎样的联系? 105 | --------------------------------------------------------------------------------