├── .Rbuildignore
├── .github
├── .gitignore
├── move.yml
└── workflows
│ └── quarto.yaml
├── .gitignore
├── DESCRIPTION
├── LICENSE.md
├── README.md
├── _output.yml
├── _quarto.yml
├── documentation.qmd
├── errors.qmd
├── files.qmd
├── functions.qmd
├── ga_script.html
├── ggplot2.qmd
├── git.qmd
├── index.qmd
├── news.qmd
├── package-files.qmd
├── pipes.qmd
├── plausible.html
├── style.Rproj
├── styler-addin.png
├── styles.scss
├── syntax.qmd
└── tests.qmd
/.Rbuildignore:
--------------------------------------------------------------------------------
1 | ^\.travis\.yml$
2 | ^.*\.Rproj$
3 | ^\.Rproj\.user$
4 | ^\.github$
5 |
--------------------------------------------------------------------------------
/.github/.gitignore:
--------------------------------------------------------------------------------
1 | *.html
2 |
--------------------------------------------------------------------------------
/.github/move.yml:
--------------------------------------------------------------------------------
1 | # Configuration for move-issues bot - https://github.com/dessant/move-issues
2 |
3 | # Delete the command comment when it contains no other content
4 | deleteCommand: true
5 |
6 | # Close the source issue after moving
7 | closeSourceIssue: true
8 |
9 | # Lock the source issue after moving
10 | lockSourceIssue: false
11 |
12 | # Mention issue and comment authors
13 | mentionAuthors: true
14 |
15 | # Preserve mentions in the issue content
16 | keepContentMentions: true
17 |
18 | # Set custom aliases for targets
19 | # aliases:
20 | # r: repo
21 | # or: owner/repo
22 |
--------------------------------------------------------------------------------
/.github/workflows/quarto.yaml:
--------------------------------------------------------------------------------
1 | on:
2 | push:
3 | branches: [main, master]
4 | pull_request:
5 | workflow_dispatch:
6 |
7 | name: quarto.yml
8 |
9 | jobs:
10 | quarto:
11 | runs-on: ubuntu-latest
12 | env:
13 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }}
14 | steps:
15 | - uses: actions/checkout@v2
16 |
17 | - uses: r-lib/actions/setup-r@v2
18 | with:
19 | use-public-rspm: true
20 |
21 | - uses: r-lib/actions/setup-r-dependencies@v2
22 |
23 | - name: Install Quarto
24 | uses: quarto-dev/quarto-actions/install-quarto@v1
25 |
26 | - name: Render book
27 | run: quarto render
28 |
29 | - name: Deploy to GitHub pages 🚀
30 | if: github.event_name != 'pull_request'
31 | uses: JamesIves/github-pages-deploy-action@4.1.4
32 | with:
33 | branch: gh-pages
34 | folder: _book
35 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .Rproj.user
2 | .Rhistory
3 | .RData
4 | .Ruserdata
5 | _main.*
6 | _book
7 |
8 | /.quarto/
9 |
--------------------------------------------------------------------------------
/DESCRIPTION:
--------------------------------------------------------------------------------
1 | Package: tidyversestyle
2 | Title: Tidyverse style
3 | Version: 1.0.0
4 | Authors@R: c(
5 | person("Hadley", "Wickham", , "hadley@rstudio.com", c("aut", "cre"))
6 | )
7 | URL: https://github.com/hadley/adv-r
8 | Depends:
9 | R (>= 3.1.0)
10 | Imports:
11 | bookdown,
12 | rmarkdown,
13 | png,
14 | bslib,
15 | downlit,
16 | xml2
17 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | # Creative Commons Legal Code
2 |
3 | ## Attribution-ShareAlike 3.0 Unported
4 |
5 | CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE LEGAL SERVICES. DISTRIBUTION OF THIS LICENSE DOES NOT CREATE AN ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE INFORMATION PROVIDED, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM ITS USE.
6 |
7 | ### License
8 |
9 | THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED.
10 |
11 | BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS LICENSE MAY BE CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND CONDITIONS.
12 |
13 | #### 1. Definitions
14 |
15 | - **a.** **"Adaptation"** means a work based upon the Work, or upon the Work and other pre-existing works, such as a translation, adaptation, derivative work, arrangement of music or other alterations of a literary or artistic work, or phonogram or performance and includes cinematographic adaptations or any other form in which the Work may be recast, transformed, or adapted including in any form recognizably derived from the original, except that a work that constitutes a Collection will not be considered an Adaptation for the purpose of this License. For the avoidance of doubt, where the Work is a musical work, performance or phonogram, the synchronization of the Work in timed-relation with a moving image ("synching") will be considered an Adaptation for the purpose of this License.
16 |
17 | - **b.** **"Collection"** means a collection of literary or artistic works, such as encyclopedias and anthologies, or performances, phonograms or broadcasts, or other works or subject matter other than works listed in Section 1(f) below, which, by reason of the selection and arrangement of their contents, constitute intellectual creations, in which the Work is included in its entirety in unmodified form along with one or more other contributions, each constituting separate and independent works in themselves, which together are assembled into a collective whole. A work that constitutes a Collection will not be considered an Adaptation (as defined below) for the purposes of this License.
18 |
19 | - **c.** **"Creative Commons Compatible License"** means a license that is listed at http://creativecommons.org/compatiblelicenses that has been approved by Creative Commons as being essentially equivalent to this License, including, at a minimum, because that license: (i) contains terms that have the same purpose, meaning and effect as the License Elements of this License; and, (ii) explicitly permits the relicensing of adaptations of works made available under that license under this License or a Creative Commons jurisdiction license with the same License Elements as this License.
20 |
21 | - **d.** **"Distribute"** means to make available to the public the original and copies of the Work or Adaptation, as appropriate, through sale or other transfer of ownership.
22 |
23 | - **e.** **"License Elements"** means the following high-level license attributes as selected by Licensor and indicated in the title of this License: Attribution, ShareAlike.
24 |
25 | - **f.** **"Licensor"** means the individual, individuals, entity or entities that offer(s) the Work under the terms of this License.
26 |
27 | - **g.** **"Original Author"** means, in the case of a literary or artistic work, the individual, individuals, entity or entities who created the Work or if no individual or entity can be identified, the publisher; and in addition (i) in the case of a performance the actors, singers, musicians, dancers, and other persons who act, sing, deliver, declaim, play in, interpret or otherwise perform literary or artistic works or expressions of folklore; (ii) in the case of a phonogram the producer being the person or legal entity who first fixes the sounds of a performance or other sounds; and, (iii) in the case of broadcasts, the organization that transmits the broadcast.
28 |
29 | - **h.** **"Work"** means the literary and/or artistic work offered under the terms of this License including without limitation any production in the literary, scientific and artistic domain, whatever may be the mode or form of its expression including digital form, such as a book, pamphlet and other writing; a lecture, address, sermon or other work of the same nature; a dramatic or dramatico-musical work; a choreographic work or entertainment in dumb show; a musical composition with or without words; a cinematographic work to which are assimilated works expressed by a process analogous to cinematography; a work of drawing, painting, architecture, sculpture, engraving or lithography; a photographic work to which are assimilated works expressed by a process analogous to photography; a work of applied art; an illustration, map, plan, sketch or three-dimensional work relative to geography, topography, architecture or science; a performance; a broadcast; a phonogram; a compilation of data to the extent it is protected as a copyrightable work; or a work performed by a variety or circus performer to the extent it is not otherwise considered a literary or artistic work.
30 |
31 | - **i.** **"You"** means an individual or entity exercising rights under this License who has not previously violated the terms of this License with respect to the Work, or who has received express permission from the Licensor to exercise rights under this License despite a previous violation.
32 |
33 | - **j.** **"Publicly Perform"** means to perform public recitations of the Work and to communicate to the public those public recitations, by any means or process, including by wire or wireless means or public digital performances; to make available to the public Works in such a way that members of the public may access these Works from a place and at a place individually chosen by them; to perform the Work to the public by any means or process and the communication to the public of the performances of the Work, including by public digital performance; to broadcast and rebroadcast the Work by any means including signs, sounds or images.
34 |
35 | - **k.** **"Reproduce"** means to make copies of the Work by any means including without limitation by sound or visual recordings and the right of fixation and reproducing fixations of the Work, including storage of a protected performance or phonogram in digital form or other electronic medium.
36 |
37 | #### 2. Fair Dealing Rights
38 |
39 | Nothing in this License is intended to reduce, limit, or restrict any uses free from copyright or rights arising from limitations or exceptions that are provided for in connection with the copyright protection under copyright law or other applicable laws.
40 |
41 | #### 3. License Grant
42 |
43 | Subject to the terms and conditions of this License, Licensor hereby grants You a worldwide, royalty-free, non-exclusive, perpetual (for the duration of the applicable copyright) license to exercise the rights in the Work as stated below:
44 |
45 | - **a.** to Reproduce the Work, to incorporate the Work into one or more Collections, and to Reproduce the Work as incorporated in the Collections;
46 |
47 | - **b.** to create and Reproduce Adaptations provided that any such Adaptation, including any translation in any medium, takes reasonable steps to clearly label, demarcate or otherwise identify that changes were made to the original Work. For example, a translation could be marked "The original work was translated from English to Spanish," or a modification could indicate "The original work has been modified.";
48 |
49 | - **c.** to Distribute and Publicly Perform the Work including as incorporated in Collections; and,
50 |
51 | - **d.** to Distribute and Publicly Perform Adaptations.
52 |
53 | - **e.** For the avoidance of doubt:
54 |
55 | - **i.** **Non-waivable Compulsory License Schemes.** In those jurisdictions in which the right to collect royalties through any statutory or compulsory licensing scheme cannot be waived, the Licensor reserves the exclusive right to collect such royalties for any exercise by You of the rights granted under this License;
56 |
57 | - **ii.** **Waivable Compulsory License Schemes.** In those jurisdictions in which the right to collect royalties through any statutory or compulsory licensing scheme can be waived, the Licensor waives the exclusive right to collect such royalties for any exercise by You of the rights granted under this License; and,
58 |
59 | - **iii.** **Voluntary License Schemes.** The Licensor waives the right to collect royalties, whether individually or, in the event that the Licensor is a member of a collecting society that administers voluntary licensing schemes, via that society, from any exercise by You of the rights granted under this License.
60 |
61 | The above rights may be exercised in all media and formats whether now known or hereafter devised. The above rights include the right to make such modifications as are technically necessary to exercise the rights in other media and formats. Subject to Section 8(f), all rights not expressly granted by Licensor are hereby reserved.
62 |
63 | #### 4. Fair Dealing Rights
64 |
65 | The license granted in Section 3 above is expressly made subject to and limited by the following restrictions:
66 |
67 | - **a.** You may Distribute or Publicly Perform the Work only under the terms of this License. You must include a copy of, or the Uniform Resource Identifier (URI) for, this License with every copy of the Work You Distribute or Publicly Perform. You may not offer or impose any terms on the Work that restrict the terms of this License or the ability of the recipient of the Work to exercise the rights granted to that recipient under the terms of the License. You may not sublicense the Work. You must keep intact all notices that refer to this License and to the disclaimer of warranties with every copy of the Work You Distribute or Publicly Perform. When You Distribute or Publicly Perform the Work, You may not impose any effective technological measures on the Work that restrict the ability of a recipient of the Work from You to exercise the rights granted to that recipient under the terms of the License. This Section 4(a) applies to the Work as incorporated in a Collection, but this does not require the Collection apart from the Work itself to be made subject to the terms of this License. If You create a Collection, upon notice from any Licensor You must, to the extent practicable, remove from the Collection any credit as required by Section 4(c), as requested. If You create an Adaptation, upon notice from any Licensor You must, to the extent practicable, remove from the Adaptation any credit as required by Section 4(c), as requested.
68 |
69 | - **b.** You may Distribute or Publicly Perform an Adaptation only under the terms of: (i) this License; (ii) a later version of this License with the same License Elements as this License; (iii) a Creative Commons jurisdiction license (either this or a later license version) that contains the same License Elements as this License (e.g., Attribution-ShareAlike 3.0 US)); (iv) a Creative Commons Compatible License. If you license the Adaptation under one of the licenses mentioned in (iv), you must comply with the terms of that license. If you license the Adaptation under the terms of any of the licenses mentioned in (i), (ii) or (iii) (the "Applicable License"), you must comply with the terms of the Applicable License generally and the following provisions: (I) You must include a copy of, or the URI for, the Applicable License with every copy of each Adaptation You Distribute or Publicly Perform; (II) You may not offer or impose any terms on the Adaptation that restrict the terms of the Applicable License or the ability of the recipient of the Adaptation to exercise the rights granted to that recipient under the terms of the Applicable License; (III) You must keep intact all notices that refer to the Applicable License and to the disclaimer of warranties with every copy of the Work as included in the Adaptation You Distribute or Publicly Perform; (IV) when You Distribute or Publicly Perform the Adaptation, You may not impose any effective technological measures on the Adaptation that restrict the ability of a recipient of the Adaptation from You to exercise the rights granted to that recipient under the terms of the Applicable License. This Section 4(b) applies to the Adaptation as incorporated in a Collection, but this does not require the Collection apart from the Adaptation itself to be made subject to the terms of the Applicable License.
70 |
71 | - **c.** If You Distribute, or Publicly Perform the Work or any Adaptations or Collections, You must, unless a request has been made pursuant to Section 6(a), keep intact all copyright notices for the Work and provide, reasonable to the medium or means You are utilizing: (i) the name of the Original Author (or pseudonym, if applicable) if supplied, and/or if the Original Author and/or Licensor designate another party or parties (e.g., a sponsor institute, publishing entity, journal) for attribution ("Attribution Parties") in Licensor's copyright notice, terms of service or by other reasonable means, the name of such party or parties; (ii) the title of the Work if supplied; (iii) to the extent reasonably practicable, the URI, if any, that Licensor specifies to be associated with the Work, unless such URI does not refer to the copyright notice or licensing information for the Work; and (iv) , consistent with Ssection 3(b), in the case of an Adaptation, a credit identifying the use of the Work in the Adaptation (e.g., "French translation of the Work by Original Author," or "Screenplay based on original Work by Original Author"). The credit required by this Section 4(c) may be implemented in any reasonable manner; provided, however, that in the case of a Adaptation or Collection, at a minimum such credit will appear, if a credit for all contributing authors of the Adaptation or Collection appears, then as part of these credits and in a manner at least as prominent as the credits for the other contributing authors. For the avoidance of doubt, You may only use the credit required by this Section for the purpose of attribution in the manner set out above and, by exercising Your rights under this License, You may not implicitly or explicitly assert or imply any connection with, sponsorship or endorsement by the Original Author, Licensor and/or Attribution Parties, as appropriate, of You or Your use of the Work, without the separate, express prior written permission of the Original Author, Licensor and/or Attribution Parties.
72 |
73 | - **d.** Except as otherwise agreed in writing by the Licensor or as may be otherwise permitted by applicable law, if You Reproduce, Distribute or Publicly Perform the Work either by itself or as part of any Adaptations or Collections, You must not distort, mutilate, modify or take other derogatory action in relation to the Work which would be prejudicial to the Original Author's honor or reputation. Licensor agrees that in those jurisdictions (e.g. Japan), in which any exercise of the right granted in Section 3(b) of this License (the right to make Adaptations) would be deemed to be a distortion, mutilation, modification or other derogatory action prejudicial to the Original Author's honor and reputation, the Licensor will waive or not assert, as appropriate, this Section, to the fullest extent permitted by the applicable national law, to enable You to reasonably exercise Your right under Section 3(b) of this License (right to make Adaptations) but not otherwise.
74 |
75 | #### 5. Representations, Warranties and Disclaimer
76 |
77 | UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING, LICENSOR OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU.
78 |
79 | #### 6. Limitation on Liability
80 |
81 | EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
82 |
83 | #### 7. Termination
84 |
85 | - **a.** This License and the rights granted hereunder will terminate automatically upon any breach by You of the terms of this License. Individuals or entities who have received Adaptations or Collections from You under this License, however, will not have their licenses terminated provided such individuals or entities remain in full compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will survive any termination of this License.
86 |
87 | - **b.** Subject to the above terms and conditions, the license granted here is perpetual (for the duration of the applicable copyright in the Work). Notwithstanding the above, Licensor reserves the right to release the Work under different license terms or to stop distributing the Work at any time; provided, however that any such election will not serve to withdraw this License (or any other license that has been, or is required to be, granted under the terms of this License), and this License will continue in full force and effect unless terminated as stated above.
88 |
89 | #### 8. Miscellaneous
90 |
91 | - **a.** Each time You Distribute or Publicly Perform the Work or a Collection, the Licensor offers to the recipient a license to the Work on the same terms and conditions as the license granted to You under this License.
92 |
93 | - **b.** Each time You Distribute or Publicly Perform an Adaptation, Licensor offers to the recipient a license to the original Work on the same terms and conditions as the license granted to You under this License.
94 |
95 | - **c.** If any provision of this License is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this License, and without further action by the parties to this agreement, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable.
96 |
97 | - **d.** No term or provision of this License shall be deemed waived and no breach consented to unless such waiver or consent shall be in writing and signed by the party to be charged with such waiver or consent.
98 |
99 | - **e.** This License constitutes the entire agreement between the parties with respect to the Work licensed here. There are no understandings, agreements or representations with respect to the Work not specified here. Licensor shall not be bound by any additional provisions that may appear in any communication from You. This License may not be modified without the mutual written agreement of the Licensor and You.
100 |
101 | - **f.** The rights granted under, and the subject matter referenced, in this License were drafted utilizing the terminology of the Berne Convention for the Protection of Literary and Artistic Works (as amended on September 28, 1979), the Rome Convention of 1961, the WIPO Copyright Treaty of 1996, the WIPO Performances and Phonograms Treaty of 1996 and the Universal Copyright Convention (as revised on July 24, 1971). These rights and subject matter take effect in the relevant jurisdiction in which the License terms are sought to be enforced according to the corresponding provisions of the implementation of those treaty provisions in the applicable national law. If the standard suite of rights granted under applicable copyright law includes additional rights not granted under this License, such additional rights are deemed to be included in the License; this License is not intended to restrict the license of any rights under applicable law.
102 |
103 | ### Creative Commons Notice
104 |
105 | Creative Commons is not a party to this License, and makes no warranty whatsoever in connection with the Work. Creative Commons will not be liable to You or any party on any legal theory for any damages whatsoever, including without limitation any general, special, incidental or consequential damages arising in connection to this license. Notwithstanding the foregoing two (2) sentences, if Creative Commons has expressly identified itself as the Licensor hereunder, it shall have all rights and obligations of Licensor.
106 |
107 | Except for the limited purpose of indicating to the public that the Work is licensed under the CCPL, Creative Commons does not authorize the use by either party of the trademark "Creative Commons" or any related trademark or logo of Creative Commons without the prior written consent of Creative Commons. Any permitted use will be in compliance with Creative Commons' then-current trademark usage guidelines, as may be published on its website or otherwise made available upon request from time to time. For the avoidance of doubt, this trademark restriction does not form part of the License.
108 |
109 | Creative Commons may be contacted at .
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 | [](https://github.com/tidyverse/style/actions/workflows/bookdown.yaml)
3 |
4 |
5 | An R Style Guide written in **bookdown** (https://github.com/rstudio/bookdown).
6 |
--------------------------------------------------------------------------------
/_output.yml:
--------------------------------------------------------------------------------
1 | bookdown::bs4_book:
2 | includes:
3 | in_header: [ga_script.html]
4 | repo: https://github.com/tidyverse/style
5 |
6 | bookdown::pdf_book:
7 | latex_engine: xelatex
8 | citation_package: natbib
9 | keep_tex: yes
10 |
11 | bookdown::epub_book: default
--------------------------------------------------------------------------------
/_quarto.yml:
--------------------------------------------------------------------------------
1 | project:
2 | type: book
3 |
4 | book:
5 | title: Tidyverse style guide
6 | author: The tidyverse team
7 |
8 | site-url: https://style.tidyverse.org
9 | repo-url: https://github.com/tidyverse/style/
10 | repo-branch: main
11 | repo-actions: [edit, issue]
12 |
13 | chapters:
14 | - index.qmd
15 |
16 | - part: Analyses
17 | chapters:
18 | - files.qmd
19 | - syntax.qmd
20 | - functions.qmd
21 | - pipes.qmd
22 | - ggplot2.qmd
23 |
24 | - part: Packages
25 | chapters:
26 | - package-files.qmd
27 | - documentation.qmd
28 | - tests.qmd
29 | - errors.qmd
30 | - news.qmd
31 |
32 | - part: Other
33 | chapters:
34 | - git.qmd
35 |
36 | format:
37 | html:
38 | include-in-header: "plausible.html"
39 | callout-appearance: simple
40 | theme:
41 | - styles.scss
42 |
43 | knitr:
44 | opts_chunk:
45 | eval: false
46 |
--------------------------------------------------------------------------------
/documentation.qmd:
--------------------------------------------------------------------------------
1 | # Documentation {#sec-documentation}
2 |
3 | ## Introduction
4 |
5 | Documentation of code is essential, even if the only person using your code
6 | is future-you. Use [roxygen2](https://github.com/klutometis/roxygen) with
7 | [markdown](https://github.com/klutometis/roxygen/blob/master/vignettes/markdown.Rmd)
8 | support enabled to keep your documentation close to the code.
9 |
10 | ## Title and description
11 |
12 | Use the first line of your function documentation to provide a concise title that describes the function, dataset, or class. Titles should use sentence case
13 | but not end with a full stop (`.`).
14 |
15 | ```{r}
16 | #' Combine values into a vector or list
17 | #'
18 | #' This is a generic function which combines its arguments.
19 | #'
20 | ```
21 |
22 | There is no need to use the explicit `@title` or `@description` tags, except
23 | in the case of the description if it is multiple paragraphs or includes
24 | more complex formatting like a bulleted list.
25 |
26 | ```{r}
27 | #' Apply a function to each element of a vector
28 | #'
29 | #' @description
30 | #' The map function transform the input, returning a vector the same length
31 | #' as the input.
32 | #'
33 | #' * `map()` returns a list or a data frame
34 | #' * `map_lgl()`, `map_int()`, `map_dbl()` and `map_chr()` return
35 | #' vectors of the corresponding type (or die trying);
36 | #' * `map_dfr()` and `map_dfc()` return data frames created by row-binding
37 | #' and column-binding respectively. They require dplyr to be installed.
38 | ```
39 |
40 | ## Indents and line breaks
41 |
42 | Always indent with one space after `#'`. If any description corresponding to a
43 | `roxygen` tag spans over multiple lines, add another two spaces of extra
44 | indention.
45 |
46 | ```{r}
47 | #' @param key The bare (unquoted) name of the column whose values will be used
48 | #' as column headings.
49 | ```
50 |
51 | Alternatively, tags that span over multiple lines (like `@description`, `@examples` and `@section`) can have the corresponding tag on its own line and then subsequent lines don't need to be indented.
52 |
53 | ```{r}
54 | #' @examples
55 | #' 1 + 1
56 | #' sin(pi)
57 | ```
58 |
59 | Use line breaks before/after sections where needed:
60 |
61 | ```{r}
62 | #' @section Tidy data:
63 | #' When applied to a data frame, row names are silently dropped. To preserve,
64 | #' convert to an explicit variable with [tibble::rownames_to_column()].
65 | #'
66 | #' @section Scoped filtering:
67 | #' The three [scoped] variants ([filter_all()], [filter_if()] and
68 | #' [filter_at()]) make it easy to apply a filtering condition to a
69 | #' selection of variables.
70 | ```
71 |
72 | ## Documenting parameters
73 |
74 | For most tags, like `@param`, `@seealso` and `@return`, the text should be a
75 | sentence, starting with a capital letter and ending with a full stop.
76 |
77 | ```{r}
78 | #' @param key The bare (unquoted) name of the column whose values will be used
79 | #' as column headings.
80 | ```
81 |
82 | If some functions share parameters, you can use `@inheritParams` to avoid
83 | duplication of content in multiple places.
84 |
85 | ```{r}
86 | #' @inheritParams function_to_inherit_from
87 | ```
88 |
89 | ## Capitalization and full stops
90 |
91 | For all bullets, enumerations, argument descriptions and the like, use sentence
92 | case and put a period at the end of each text element, even if it is only a few
93 | words. However, avoid capitalization of function names or packages since R is
94 | case sensitive. Use a colon before enumerations or bulleted lists.
95 |
96 | ```{r}
97 | #' @details
98 | #' In the following, we present the bullets of the list:
99 | #' * Four cats are few animals.
100 | #' * forcats is a package.
101 | ```
102 |
103 | ## Cross-linking
104 |
105 | Cross-referencing is encouraged, both within R's help file system as well as to
106 | external resources.
107 |
108 | List closely related functions in `@seealso`. A single related function can be written as a sentence:
109 |
110 | ```{r}
111 | #' @seealso [fct_lump()] to automatically convert the rarest (or most common)
112 | #' levels to "other".
113 | ```
114 |
115 | More recommendations should be organised in a bulleted list:
116 |
117 | ```{r}
118 | #' @seealso
119 | #' * [tibble()] constructs from individual columns.
120 | #' * [enframe()] converts a named vector into a two-column tibble (names and
121 | #' values).
122 | #' * [name-repair] documents the details of name repair.
123 | ```
124 |
125 |
126 | You can link to functions in other packages by using the fully qualified function name, i.e. `[pkg::function()]`.
127 |
128 | If you have a family of related functions, you can use the `@family` tag to
129 | automatically add appropriate lists and interlinks to the `@seealso` section.
130 | Family names are plural. In dplyr, the verbs `arrange()`, `filter()`,
131 | `mutate()`, `slice()`, `summarize()` form the family of single table verbs.
132 |
133 | ```{r}
134 | #' @family single table verbs
135 | ```
136 |
137 | When linking to external resources either include the full url inline with `<>`, or the surrounding prose and link text should make it extremely clear where the hyperlink goes. Avoid text like ["click here"](https://www.smashingmagazine.com/2012/06/links-should-never-say-click-here/).
138 |
139 | ## R code
140 |
141 | Text that contains valid R code should be marked as such using backticks. This includes:
142 |
143 | * Function arguments, e.g. `na.rm`.
144 | * Values, e.g. `TRUE`, `FALSE`, `NA`, `NaN`, `...`, `NULL`
145 | * Literal R code, e.g. `mean(x, na.rm = TRUE)`
146 | * Class names, e.g. "a tibble will have class `tbl_df` ..."
147 |
148 | You can use code syntax for function names, like `` `tibble()` ``, but consider if it would be better to use a cross-link instead, like `[tibble()]`. If you refer to a function multiple times in one topic, you only need to link the first reference.
149 |
150 | ## Package names
151 |
152 | Don't use code font for package names. If the package name might be misinterpreted as an ordinary word, disambiguate by following it with "package" or by wrapping the package name in `{}` (but not both).
153 |
154 | ```
155 | # Good
156 | Use the glue package to flexibly interpolate values into strings.
157 | Use {glue} to flexibly interpolate values into strings.
158 |
159 | # Bad
160 | Use glue to flexibly interpolate values into strings.
161 | Use `glue` to flexibly interpolate values into strings.
162 | Use the {glue} package to flexibly interpolate values into strings.
163 | ```
164 |
165 | If a package name comes at the start of a sentence, treat it like a proper name and don't capitalize it:
166 |
167 | ```
168 | # Good
169 | dplyr provides a grammar of data manipulation, providing a consistent set of verbs that help you solve the most common data manipulation challenges.
170 |
171 | # Bad
172 | Dplyr provides a grammar of data manipulation, providing a consistent set of verbs that help you solve the most common data manipulation challenges
173 | ```
174 |
175 | ## Internal functions
176 |
177 | Internal functions should be documented with `#'` comments as per usual.
178 | Use the `@noRd` tag to prevent `.Rd` files from being generated.
179 |
180 | ```{r}
181 | #' Drop last
182 | #'
183 | #' Drops the last element from a vector.
184 | #'
185 | #' @param x A vector object to be trimmed.
186 | #'
187 | #' @noRd
188 | ```
189 |
--------------------------------------------------------------------------------
/errors.qmd:
--------------------------------------------------------------------------------
1 | # Error messages
2 |
3 | An error message should start with a general statement of the problem then give a concise description of what went wrong.
4 | Consistent use of punctuation and formatting makes errors easier to parse.
5 |
6 | This guide assumes that you're using `cli::cli_abort()`.
7 | We are transitioning to use this function in the tidyverse because it:
8 |
9 | - Makes it easy to generate bulleted lists.
10 | - Uses glue style interpolation to insert data into the error.
11 | - Supports a wide range of [inline markup](https://cli.r-lib.org/reference/inline-markup.html).
12 | - Provides convenient tools to [chain errors together](https://rlang.r-lib.org/reference/topic-error-chaining.html).
13 | - Can control the [name of the function](https://rlang.r-lib.org/reference/topic-error-call.html) shown in the error.
14 |
15 | Much of the advice in this guide still applies if you're using `stop()`, but it will be be much more work to generate the message.
16 |
17 |
18 |
19 | ## Problem statement
20 |
21 | Every error message should start with a general statement of the problem.
22 | It should be concise, but informative (This is hard!).
23 | The problem statement should use sentence case and end with a full stop.
24 |
25 | - If the cause of the problem is clear (e.g. an incorrect type or size), use "**must**":
26 |
27 | ```{r}
28 | dplyr::nth(1:10, "x")
29 | #> Error:
30 | #> ! `n` must be a numeric vector, not a character vector.
31 |
32 | dplyr::nth(1:10, 1:2)
33 | #> Error:
34 | #> ! `n` must have length 1, not length 2.
35 | ```
36 |
37 | Do your best to tell the user both what is expected ("a numeric vector") and what they actually provided ("a character vector").
38 |
39 | - If you cannot state what was expected, use "**can't**":
40 |
41 | ```{r}
42 | mtcars |> pull(b)
43 | #> Error:
44 | #> ! Can't find column `b` in `.data`.
45 |
46 | as_vector(environment())
47 | #> Error:
48 | #> ! Can't coerce `.x` to a vector.
49 |
50 | purrr::modify_depth(list(list(x = 1)), 3, ~ . + 1)
51 | #> Error:
52 | #> ! Can't find specified `.depth` in `.x`.
53 | ```
54 |
55 |
56 |
57 | ## Error location
58 |
59 | Ideally the error message should mention the failing function call.
60 |
61 |
62 |
63 | See for more about how to pass calls through error helpers.
64 |
65 | ## Error details
66 |
67 | After the problem statement, use a bulleted list to provide further information.
68 | Use cross bullets (`x`) to let the user know what the problem is, then use info bullets (`i`) to provide contextual information.
69 | These are easy to create with `cli_abort()`:
70 |
71 |
72 |
73 | Try to keep the sentences short and sweet:
74 |
75 | ```{r}
76 | # Good
77 | vec_slice(letters, 100)
78 | #> ! Can't subset elements past the end.
79 | #> ℹ Location 100 doesn't exist.
80 | #> ℹ There are only 26 elements.
81 |
82 | # Bad
83 | vec_slice(letters, 100)
84 | #> ! Must index an existing element.
85 | #> There are 26 elements and you've tried to subset element 100.
86 | ```
87 |
88 | Do your best to reveal the location, name, and/or content of the troublesome component of the input.
89 | The goal is to make it as easy as possible for the user to find and fix the problem.
90 |
91 | ```{r}
92 | # Good
93 | map_int(1:5, ~ "x")
94 | #> Error:
95 | #> ! Each result must be a single integer.
96 | #> ✖ Result 1 is a character vector.
97 |
98 | # Bad
99 | map_int(1:5, ~ "x")
100 | #> Error:
101 | #> ! Each result must be a single integer
102 | ```
103 |
104 | (It is often not easy to identify the exact problem; it may require passing around extra arguments so that error messages generated at a lower-level can know the original source. For frequently used functions, the effort is typically worth it.)
105 |
106 | If the source of the error is unclear, avoid pointing the user in the wrong direction by giving an opinion about the source of the error:
107 |
108 | ```{r}
109 | # Good
110 | pull(mtcars, b)
111 | #> Error:
112 | #> ! Can't find column `b` in `.data`.
113 |
114 | tibble(x = 1:2, y = 1:3, z = 1)
115 | #> Error:
116 | #> ! Tibble columns must have compatible sizes.
117 | #> • Size 2: Existing data.
118 | #> • Size 3: Column `y`.
119 | #> ℹ Only values of size one are recycled.
120 |
121 | # Bad: implies one argument at fault
122 | pull(mtcars, b)
123 | #> Error:
124 | #> ! Column `b` must exist in `.data`.
125 |
126 | pull(mtcars, b)
127 | #> Error:
128 | #> ! `.data` must contain column `b`.
129 |
130 | tibble(x = 1:2, y = 1:3, z = 1)
131 | #> Error:
132 | #> ! Column `x` must be length 1 or 3, not 2.
133 | ```
134 |
135 | If there are multiple issues, or an inconsistency revealed across several arguments or items, prefer a bulleted list:
136 |
137 | ```{r}
138 | # Good
139 | purrr::reduce2(1:4, 1:2, `+`)
140 | #> Error:
141 | #> ! `.x` and `.y` must have compatible lengths:
142 | #> ✖ `.x` has length 4
143 | #> ✖ `.y` has length 2
144 |
145 | # Bad: harder to scan
146 | purrr::reduce2(1:4, 1:2, `+`)
147 | #> Error:
148 | #> ! `.x` and `.y` must have compatible lengths: `.x` has length 4 and
149 | #> `.y` has length 2
150 | ```
151 |
152 | If the list of issues might be long, make sure to truncate to only show the first few:
153 |
154 | ```{r}
155 | # Good
156 | #> Error: NAs found at 1,000,000 locations: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, ...
157 | ```
158 |
159 | If you want to correctly pluralise the error message, consider using `ngettext()`.
160 | See the notes in `?ngettext()` for some challenges related to correct translation to other languages.
161 |
162 | ## Hints
163 |
164 | If the source of the error is clear and common, you may want to provide a hint as to how to fix it.
165 | The hint should be the last bullet, use an info bullet (`i`), and end in a question mark.
166 |
167 | ```{r}
168 | dplyr::filter(iris, Species = "setosa")
169 | #> Error:
170 | #> ! Filter specifications must be named.
171 | #> ℹ Did you mean `Species == "setosa"`?
172 |
173 | ggplot2::ggplot(ggplot2::aes())
174 | #> Error:
175 | #> ! Can't plot data with class "uneval".
176 | #> ℹ Did you accidentally provide the results of aes() to the `data` argument?
177 |
178 | mtcars |> ggplot() |> geom_point()
179 | #> Error in `validate_mapping()`:
180 | #> ! `mapping` must be created by `aes()`.
181 | #> ℹ Did you use `%>%` or `|>` instead of `+`?
182 | ```
183 |
184 | Hints are particularly important if the source of the error is far away from the root cause:
185 |
186 | ```{r}
187 | # Bad
188 | mean[[1]]
189 | #> Error:
190 | #> ! object of type 'closure' is not subsettable
191 |
192 | # BETTER
193 | mean[[1]]
194 | #> Error:
195 | #> ! Can't subset a function.
196 |
197 | # BEST
198 | mean[[1]]
199 | #> Error:
200 | #> ! Can't subset a function.
201 | #> ℹ Have you forgotten to define a variable named `mean`?
202 | ```
203 |
204 | Good hints are difficult to write because you want to avoid steering users in the wrong direction.
205 | Generally, we avoid writing a hint unless the problem is common, and you can easily find a common pattern of incorrect usage (e.g. by searching StackOverflow).
206 |
207 | ## Punctuation
208 |
209 |
210 |
211 | - Errors should be written in sentence case, and should end in a full stop.
212 | Bullets should be formatted similarly; make sure to capitalise the first word (unless it's an argument or column name).
213 |
214 | - Prefer the singular in problem statements:
215 |
216 | ```{r}
217 | # Good
218 | map_int(1:2, ~ "a")
219 | #> Error:
220 | #> ! Each result must be coercible to a single integer.
221 | #> ✖ Result 1 is a character vector.
222 |
223 | # Bad
224 | map_int(1:2, ~ "a")
225 | #> Error:
226 | #> ! Results must be coercible to single integers.
227 | #> ✖ Result 1 is a character vector.
228 | ```
229 |
230 | - If you can detect multiple problems, list up to five.
231 | This allows the user to fix multiple problems in a single pass without being overwhelmed by many errors that may have the same source.
232 |
233 |
234 |
235 | ```{r}
236 | # BETTER
237 | map_int(1:10, ~ "a")
238 | #> Error:
239 | #> ! Each result must be coercible to a single integer.
240 | #> ✖ Result 1 is a character vector
241 | #> ✖ Result 2 is a character vector
242 | #> ✖ Result 3 is a character vector
243 | #> ✖ Result 4 is a character vector
244 | #> ✖ Result 5 is a character vector
245 | #> ... and 5 more problems
246 | ```
247 |
248 | - Pick a natural connector between problem statement and error location: this may be ", not", ";", or ":" depending on the context.
249 |
250 | - Surround the names of arguments in backticks, e.g. `` `x` ``.
251 | Use "column" to disambiguate columns and arguments: `` Column `x` ``.
252 | Avoid "variable", because it is ambiguous.
253 |
254 |
255 | - Ideally, each component of the error message should be less than 80 characters wide.
256 | Do not add manual line breaks to long error messages; they will not look correct if the console is narrower (or much wider) than expected.
257 | Instead, use bullets to break up the error into shorter logical components.
258 | In case you do need longer sentences, let cli perform paragraph wrapping automatically.
259 | It inserts newlines automatically depending on the width of the console.
260 |
261 | ## Before and after
262 |
263 | More examples gathered from around the tidyverse.
264 |
265 | ```{r}
266 | dplyr::filter(mtcars, cyl)
267 | #> BEFORE:
268 | #> ! Argument 2 filter condition does not evaluate to a logical vector.
269 |
270 | #> AFTER:
271 | #> ! Each argument must be a logical vector.
272 | #> * Argument 2 (`cyl`) is an integer vector.
273 |
274 | tibble::tribble("x", "y")
275 | #> BEFORE: ! Expected at least one column name; e.g. `~name`
276 | #> AFTER: ! Must supply at least one column name, e.g. `~name`.
277 |
278 | ggplot2::ggplot(data = diamonds) + ggplot2::geom_line(ggplot2::aes(x = cut))
279 | #> BEFORE: ! geom_line requires the following missing aesthetics: y
280 | #> AFTER: ! `geom_line()` must have the following aesthetics: `y`.
281 |
282 | dplyr::rename(mtcars, cyl = xxx)
283 | #> BEFORE: ! `xxx` contains unknown variables
284 | #> AFTER: ! Can't find column `xxx` in `.data`.
285 |
286 | dplyr::arrange(mtcars, xxx)
287 | #> BEFORE: ! Evaluation error: object 'xxx' not found.
288 | #> AFTER: ! Can't find column `xxx` in `.data`.
289 | ```
290 |
291 | ## Localisation
292 |
293 | It is encouraged to be as informative as possible, but each sentence should be very simple to make localisation and translation possible.
294 | [A Localization Horror Story: It Could Happen To You](https://metacpan.org/pod/distribution/Locale-Maketext/lib/Locale/Maketext/TPJ13.pod) is a Good summary of the challenges of localising error messages.
295 | You might not support localised messages right now but you should make it as easy as possible to do it in the future.
296 |
--------------------------------------------------------------------------------
/files.qmd:
--------------------------------------------------------------------------------
1 | # Files {#sec-files}
2 |
3 | ## Names
4 |
5 | 1. File names should be **machine readable**: avoid spaces, symbols, and special characters. Prefer file names that are all lower case, and never
6 | have names that differ only in their capitalization. Delimit words with `-` or `_`. Use `.R` as the extension of R files.
7 |
8 | ```
9 | # Good
10 | fit_models.R
11 | utility_functions.R
12 | exploratory-data-analysis.R
13 |
14 | # Bad
15 | fit models.R
16 | foo.r
17 | ExploratoryDataAnaylsis.r
18 | ```
19 |
20 | 2. File names should be **human readable**: use file names to describe what's in the file.
21 |
22 | ```
23 | # good
24 | report-draft-notes.txt
25 |
26 | # bad
27 | temp.r
28 | ```
29 |
30 | Use the same structure for closely related files:
31 |
32 | ```
33 | # good
34 | fig-eda.png
35 | fig-model-3.png
36 |
37 | # bad
38 | figure eda.PNG
39 | fig model three.png
40 | ```
41 |
42 | 3. File names should play well with default ordering. If your file names contain dates, use yyyy-mm-dd (ISO8601) format so they sort in chronological order. If your file names include numbers, make sure to pad them with the appropriate number of zeros so that (e.g.) 11 doesn't get sorted before 2. If files should be used in a specific order, put the number at the start, not the end.
43 |
44 | ```
45 | # good
46 | 01-load-data.R
47 | 02-exploratory-analysis.R
48 | 03-model-approach-1.R
49 | 04-model-approach-2.R
50 | 2025-01-01-report.Rmd
51 | 2025-02-01.report.Rmd
52 |
53 | # bad
54 | alternative model.R
55 | code for exploratory analysis.r
56 | feb 01 report.Rmd
57 | jan 01 report.Rmd
58 | model_first_try.R
59 | run-first.r
60 | ```
61 |
62 | If you later realise that you've missed some steps, it's tempting to use `02a`,
63 | `02b`, etc. However, I think it's generally better to bite the bullet and
64 | rename all files.
65 |
66 | 4. Don't [tempt fate](https://phdcomics.com/comics.php?f=1531) by using "final" or similar words in file names. Instead either rely on Git to track changes over time, or failing that, put the date in the file name.
67 |
68 | ```
69 | # good
70 | report-2022-03-20.qmd
71 | report-2022-04-02.qmd
72 |
73 | # bad
74 | finalreport.qmd
75 | FinalReport-2.qmd
76 | ```
77 |
78 | ## Organisation
79 |
80 | It's hard to describe exactly how you should organise your code across multiple files. I think the best rule of thumb is that if you can give a file a concise name that still evokes its contents, you've arrived at a good organisation. But getting to that point is hard.
81 |
82 | ## Internal structure
83 |
84 | Use commented lines of `-` and `=` to break up your file into easily readable
85 | chunks.
86 |
87 | ```{r}
88 | # Load data ---------------------------
89 |
90 | # Plot data ---------------------------
91 | ```
92 |
93 | If your script uses add-on packages, load them all at once at the very
94 | beginning of the file. This is more transparent than sprinkling `library()`
95 | calls throughout your code or having hidden dependencies that are loaded in a
96 | startup file, such as `.Rprofile`.
97 |
--------------------------------------------------------------------------------
/functions.qmd:
--------------------------------------------------------------------------------
1 | # Functions
2 |
3 | ## Naming
4 |
5 | As well as following the general advice for object names in @sec-objectnames, strive to use verbs for function names:
6 |
7 | ```{r}
8 | # Good
9 | add_row()
10 | permute()
11 |
12 | # Bad
13 | row_adder()
14 | permutation()
15 | ```
16 |
17 | ## Anonymous functions
18 |
19 | Use the new lambda syntax: `\(x) x + 1` when writing short anonymous functions (i.e. when you define a function in an argument without giving it an explicit name).
20 |
21 | ```R
22 | # Good
23 | map(xs, \(x) mean((x + 5)^2))
24 | map(xs, function(x) mean((x + 5)^2))
25 |
26 | # Bad
27 | map(xs, ~ mean((.x + 5)^2))
28 | ```
29 |
30 | Don't use `\()` for multi-line functions:
31 |
32 | ```R
33 | # Good
34 | map(xs, function(x) {
35 | mean((x + 5)^2)
36 | })
37 |
38 | # Bad
39 | map(xs, \(x) {
40 | mean((x + 5)^2)
41 | })
42 | ```
43 |
44 | Or when creating named functions:
45 |
46 | ```R
47 | # Good
48 | cv <- function(x) {
49 | sd(x) / mean(x)
50 | }
51 |
52 | # Bad
53 | cv <- \(x) sd(x) / mean(x)
54 | ```
55 |
56 | Avoid using `\()` in a pipe, and remember to use informative argument names.
57 |
58 | ## Multi-line function definitions
59 |
60 | There are two options if the function name and definition can't fit on a single line. In both cases, each argument goes on its own line; the difference is how deep you indent it and where you put `)` and `{`:
61 |
62 | * **Single-indent**: indent the argument name with a single indent (i.e. two spaces).
63 | The trailing `)` and leading `{` go on a new line.
64 |
65 | ```{r}
66 | # Good
67 | long_function_name <- function(
68 | a = "a long argument",
69 | b = "another argument",
70 | c = "another long argument"
71 | ) {
72 | # As usual code is indented by two spaces.
73 | }
74 | ```
75 |
76 | * **Hanging-indent**: indent the argument name to match the opening `(` of `function`.
77 | The trailing `)` and leading `{` go on the same line as the last argument.
78 |
79 | ```{r}
80 | # Good
81 | long_function_name <- function(a = "a long argument",
82 | b = "another argument",
83 | c = "another long argument") {
84 | # As usual code is indented by two spaces.
85 | }
86 | ```
87 |
88 | These styles are designed to clearly separate the function definition from its body.
89 |
90 | ```{r}
91 | # Bad
92 | long_function_name <- function(a = "a long argument",
93 | b = "another argument",
94 | c = "another long argument") {
95 | # Here it's hard to spot where the definition ends and the
96 | # code begins, and to see all three function arguments
97 | }
98 | ```
99 |
100 | If a function argument can't fit on a single line, this is a sign you should rework the argument to keep it [short and sweet](https://design.tidyverse.org/defaults-short-and-sweet.html).
101 |
102 | ## S7
103 |
104 | In S7, the method definition can be long because the function name is replaced by a method call that specifies the generic and dispatch classes. In this case we recommend the single-indent style.
105 |
106 | ```{r}
107 | method(from_provider, list(openai_provider, class_any)) <- function(
108 | provider,
109 | x,
110 | ...,
111 | error_call = caller_env()
112 | ) {
113 | ...
114 | }
115 | ```
116 |
117 | If the method definition is too long to fit on one line, use the usual rules to
118 | spread the method arguments across multiple lines:
119 |
120 | ```{r}
121 | method(
122 | from_provider,
123 | list(openai_provider, class_any, a_very_long_class_name)
124 | ) <- function(
125 | provider,
126 | x,
127 | ...,
128 | error_call = caller_env()
129 | ) {
130 | ...
131 | }
132 | ```
133 |
134 | ## `return()`
135 |
136 | Only use `return()` for early returns. Otherwise, rely on R to return the result
137 | of the last evaluated expression.
138 |
139 | ```{r}
140 | # Good
141 | find_abs <- function(x) {
142 | if (x > 0) {
143 | return(x)
144 | }
145 | x * -1
146 | }
147 | add_two <- function(x, y) {
148 | x + y
149 | }
150 |
151 | # Bad
152 | add_two <- function(x, y) {
153 | return(x + y)
154 | }
155 | ```
156 |
157 | Return statements should always be on their own line because they have important effects on the control flow. See also [control flow modifiers](#control-flow-modifiers).
158 |
159 | ```{r}
160 | # Good
161 | find_abs <- function(x) {
162 | if (x > 0) {
163 | return(x)
164 | }
165 | x * -1
166 | }
167 |
168 | # Bad
169 | find_abs <- function(x) {
170 | if (x > 0) return(x)
171 | x * -1
172 | }
173 | ```
174 |
175 | If your function is called primarily for its side-effects (like printing,
176 | plotting, or saving to disk), it should return the first argument invisibly.
177 | This makes it possible to use the function as part of a pipe. `print` methods
178 | should usually do this, like this example from [httr](http://httr.r-lib.org/):
179 |
180 | ```{r}
181 | print.url <- function(x, ...) {
182 | cat("Url: ", build_url(x), "\n", sep = "")
183 | invisible(x)
184 | }
185 | ```
186 |
187 | ## Comments
188 |
189 | In code, use comments to explain the "why" not the "what" or "how". Each line
190 | of a comment should begin with the comment symbol and a single space: `# `.
191 |
192 | ```{r}
193 | # Good
194 |
195 | # Objects like data frames are treated as leaves
196 | x <- map_if(x, is_bare_list, recurse)
197 |
198 |
199 | # Bad
200 |
201 | # Recurse only with bare lists
202 | x <- map_if(x, is_bare_list, recurse)
203 | ```
204 |
205 | Comments should be in sentence case, and only end with a full stop if they
206 | contain at least two sentences:
207 |
208 | ```{r}
209 | # Good
210 |
211 | # Objects like data frames are treated as leaves
212 | x <- map_if(x, is_bare_list, recurse)
213 |
214 | # Do not use `is.list()`. Objects like data frames must be treated
215 | # as leaves.
216 | x <- map_if(x, is_bare_list, recurse)
217 |
218 |
219 | # Bad
220 |
221 | # objects like data frames are treated as leaves
222 | x <- map_if(x, is_bare_list, recurse)
223 |
224 | # Objects like data frames are treated as leaves.
225 | x <- map_if(x, is_bare_list, recurse)
226 | ```
227 |
--------------------------------------------------------------------------------
/ga_script.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
--------------------------------------------------------------------------------
/ggplot2.qmd:
--------------------------------------------------------------------------------
1 | # ggplot2
2 |
3 |
4 | ## Introduction
5 |
6 | Styling suggestions for `+` used to separate ggplot2 layers are very similar to those for `|>` in pipelines.
7 |
8 |
9 | ## Whitespace
10 |
11 | `+` should always have a space before it, and should be followed by a new line. This is true even if your plot has only two layers. After the first step, each line should be indented by two spaces.
12 |
13 | If you are creating a ggplot off of a dplyr pipeline, there should only be one level of indentation.
14 |
15 | ```{r}
16 | # Good
17 | iris |>
18 | filter(Species == "setosa") |>
19 | ggplot(aes(x = Sepal.Width, y = Sepal.Length)) +
20 | geom_point()
21 |
22 | # Bad
23 | iris |>
24 | filter(Species == "setosa") |>
25 | ggplot(aes(x = Sepal.Width, y = Sepal.Length)) +
26 | geom_point()
27 |
28 | # Bad
29 | iris |>
30 | filter(Species == "setosa") |>
31 | ggplot(aes(x = Sepal.Width, y = Sepal.Length)) + geom_point()
32 | ```
33 |
34 |
35 | ## Long lines
36 |
37 | If the arguments to a ggplot2 layer don't all fit on one line, put each argument on its own line and indent:
38 |
39 | ```{r}
40 | # Good
41 | iris |>
42 | ggplot(aes(x = Sepal.Width, y = Sepal.Length, color = Species)) +
43 | geom_point() +
44 | labs(
45 | x = "Sepal width, in cm",
46 | y = "Sepal length, in cm",
47 | title = "Sepal length vs. width of irises"
48 | )
49 |
50 | # Bad
51 | iris |>
52 | ggplot(aes(x = Sepal.Width, y = Sepal.Length, color = Species)) +
53 | geom_point() +
54 | labs(x = "Sepal width, in cm", y = "Sepal length, in cm", title = "Sepal length vs. width of irises")
55 | ```
56 |
57 | ggplot2 allows you to do data manipulation, such as filtering or slicing, within the `data` argument. Avoid this, and instead do the data manipulation in a pipeline before starting plotting.
58 |
59 | ```{r}
60 | # Good
61 | iris |>
62 | filter(Species == "setosa") |>
63 | ggplot(aes(x = Sepal.Width, y = Sepal.Length)) +
64 | geom_point()
65 |
66 | # Bad
67 | ggplot(filter(iris, Species == "setosa"), aes(x = Sepal.Width, y = Sepal.Length)) +
68 | geom_point()
69 | ```
70 |
--------------------------------------------------------------------------------
/git.qmd:
--------------------------------------------------------------------------------
1 | # Git/GitHub
2 |
3 | ## Commit messages
4 |
5 | Follow [standard git commit message advice](https://chris.beams.io/posts/git-commit/). In brief:
6 |
7 | * The first line is the subject, and should summarise the changes in the commit
8 | in under 50 characters.
9 | Use sentence case, but no period at the end.
10 |
11 | * If additional details are required, add a blank line, and then provide
12 | explanation and context in paragraph format.
13 |
14 | * If the commit fixes a GitHub issue include `Fixes #`.
15 | This will ensure that the issue is automatically closed when the commit
16 | is merged into main.
17 |
18 | ## Pull requests
19 |
20 | The title of a pull request should briefly describe the changes made. The title should be standalone and should not include the related issue number (i.e. don't write `Fixes #10`).
21 |
22 | For very simple changes, you can leave the description blank as there's no need to describe what will be obvious from looking at the diff. For more complex changes, you should give an overview of the changes. If the PR fixes an issue, make sure to include `Fixes #` in the description.
23 |
--------------------------------------------------------------------------------
/index.qmd:
--------------------------------------------------------------------------------
1 | # Welcome {-}
2 |
3 | Good coding style is like correct punctuation: you can manage without it, butitsuremakesthingseasiertoread. This site describes the style used throughout the [tidyverse](http://tidyverse.org). It was derived from Google's original R Style Guide - but
4 | Google's [current guide](https://google.github.io/styleguide/Rguide.html) is derived from the tidyverse style guide.
5 |
6 | All style guides are fundamentally opinionated. Some decisions genuinely do make code easier to use (especially matching indenting to programming structure), but many decisions are arbitrary. The most important thing about a style guide is that it provides consistency, making code easier to write because you need to make fewer decisions.
7 |
8 | Two R packages support this style guide:
9 |
10 | * [styler](http://styler.r-lib.org) allows you to interactively restyle
11 | selected text, files, or entire projects. It includes an RStudio add-in,
12 | the easiest way to re-style existing code.
13 |
14 | ```{r}
15 | #| eval: true
16 | #| echo: false
17 | #| fig-align: center
18 | knitr::include_graphics("styler-addin.png", dpi = 220)
19 | ```
20 |
21 |
22 | * [lintr](https://github.com/r-lib/lintr) performs automated checks
23 | to confirm that you conform to the style guide.
24 |
--------------------------------------------------------------------------------
/news.qmd:
--------------------------------------------------------------------------------
1 | # News
2 |
3 | Each user-facing change to a package should be accompanied by a bullet in `NEWS.md`. Minor changes to documentation don't need to be documented, but it's worthwhile to draw attention to sweeping changes and to new vignettes.
4 |
5 | ## In-development
6 |
7 | The goal of the bullet is to briefly describe the change so users of the packages can understand what's changed. This can be similar to the commit message, but written with a user (not developer) in mind. It's worth emphasizing this point --- the reader of your NEWS entries is likely unfamiliar with the day-to-day development work or internals of your package. Think carefully about how to concisely but clearly summarize what's changed and why it matters for them. If it doesn't matter (i.e. it's a purely internal change), you don't need a bullet.
8 |
9 | New bullets should be added to the top of the file (immediately under the first heading) and should be a single line. Organisation and wrapping will happen later, during the release process.
10 |
11 | ```
12 | # haven (development version)
13 |
14 | * Second update.
15 | * First update.
16 | ```
17 |
18 | If the bullet is related to an issue, include the issue number. If the
19 | contribution is a PR, and the author is not a package author, include the
20 | GitHub user name. Both items should be wrapped in parentheses and will generally
21 | come before the final period.
22 |
23 | ```
24 | # Good
25 | * `ggsave()` now uses full argument names to avoid partial match warnings (@wch, #2355).
26 |
27 | # Bad
28 | * `ggsave()` now uses full argument names to avoid partial match warnings.
29 |
30 | * `ggsave()` now uses full argument names to avoid partial match warnings. (@wch, #2355)
31 | ```
32 |
33 | ## Pre-release {#news-release}
34 |
35 | Prior to release, the NEWS file needs to be thoroughly proofread, groomed, and organised into sections.
36 |
37 | ### General style
38 |
39 | Strive to place the name of the function as close to the beginning of the bullet
40 | as possible. A consistent location makes the bullets easier to scan, and easier
41 | to organise prior to release.
42 |
43 | ```
44 | # Good
45 | * `ggsave()` now uses full argument names to avoid partial match warning (#2355).
46 |
47 | # Bad
48 | * Fixed partial argument matches in `ggsave()` (#2355).
49 | ```
50 |
51 | Lines should be wrapped to 80 characters, and each bullet should end in a full
52 | stop.
53 |
54 | Frame bullets positively (i.e. what now happens, not what used to happen), and
55 | use the present tense.
56 |
57 | ```
58 | # Good
59 | * `ggsave()` now uses full argument names to avoid partial match warnings (#2355).
60 |
61 | # Bad
62 | * `ggsave()` no longer partially matches argument names (#2355).
63 | ```
64 |
65 | Many news bullets will be a single sentence. This is typically adequate when describing a bug fix or minor improvement, but you may need more detail when describing a new feature. For more complex features, include longer examples in fenced code blocks (```` ``` ````). These will be useful inspiration when you later write the blog post.
66 |
67 | ````
68 | # Good
69 | * In `stat_bin()`, `binwidth` now also takes functions.
70 |
71 | # Better
72 | * In `stat_bin()`, `binwidth` now also takes functions. The function is
73 | called with the scaled `x` values, and should return a single number.
74 | This makes it possible to use classical binwidth computations with ggplot2.
75 |
76 | # Best
77 | * In `stat_bin()`, `binwidth` now also takes functions. The function is
78 | called with the scaled `x` values, and should return a single number.
79 | With a little work, this makes it possible to use classical bin size
80 | computations with ggplot2.
81 |
82 | ```R
83 | sturges <- function(x) {
84 | rng <- range(x)
85 | bins <- nclass.Sturges(x)
86 |
87 | (rng[2] - rng[1]) / bins
88 | }
89 | ggplot(diamonds, aes(price)) +
90 | geom_histogram(binwidth = sturges) +
91 | facet_wrap(~cut)
92 | ```
93 | ````
94 |
95 | ### Code style
96 |
97 | Functions, arguments, and file names should be wrapped in backticks. Function names should include parentheses; omit "the argument" or "the function"
98 |
99 | ```
100 | # Good
101 | * In `stat_bin()`, `binwidth` now also takes functions.
102 |
103 | # Bad
104 | * In the stat_bin function, "binwidth" now also takes functions.
105 | ```
106 |
107 | ### Headings
108 |
109 | Each release should have a level 1 heading (`#`) containing the package name
110 | and version number. For smaller packages or patch releases this amount of
111 | organisation may be sufficient. For example, here is the NEWS for modelr 0.1.2:
112 |
113 | ```
114 | # modelr 0.1.2
115 |
116 | * `data_grid()` no longer fails with modern tidyr (#58).
117 |
118 | * New `mape()` and `rsae()` model quality statistics (@paulponcet, #33).
119 |
120 | * `rsquare()` use more robust calculation 1 - SS_res / SS_tot rather
121 | than SS_reg / SS_tot (#37).
122 |
123 | * `typical()` gains `ordered` and `integer` methods (@jrnold, #44),
124 | and `...` argument (@jrnold, #42).
125 | ```
126 |
127 | If there are many bullets, the version heading should be followed by issues
128 | grouped into related areas with level 2 headings (`##`). Three commonly
129 | used sections are shown below:
130 |
131 | ```
132 | # package 1.1.0
133 |
134 | ## Breaking changes
135 |
136 | ## New features
137 |
138 | ## Minor improvements and fixes
139 | ```
140 |
141 | It is fine to deviate from these headings if another organisation makes sense.
142 | Indeed, larger packages will often require a finer break down. For example,
143 | ggplot2 2.3.0
144 | included these headings:
145 |
146 | ```
147 | # ggplot 2.3.0
148 | ## Breaking changes
149 | ## New features
150 | ### Tidy evaluation
151 | ### sf
152 | ### Layers: geoms, stats, and position adjustments
153 | ### Scales and guides
154 | ### Margins
155 | ## Extension points
156 | ## Minor bug fixes and improvements
157 | ### Facetting
158 | ### Scales
159 | ### Layers
160 | ### Coords
161 | ### Themes
162 | ### Guides
163 | ### Other
164 | ```
165 |
166 | It is not worthwhile to organise bullets into headings during development, as
167 | it's not typically obvious what the groups will be in advance.
168 |
169 | Within a section, bullets should be ordered alphabetically by the first function
170 | mentioned. If no function is mentioned, place the bullet at the top of the section.
171 |
172 | ### Breaking changes
173 |
174 | API breaking changes should also appear in their own section at the top.
175 | Each bullet should include a description of the symptoms of the change, and what
176 | is needed to fix it. The bullet should also be repeated in the appropriate section.
177 |
178 | ```
179 | ## Breaking changes
180 |
181 | * `separate()` now correctly uses -1 to refer to the far right position,
182 | instead of -2. If you depended on this behaviour, you'll need to condition
183 | on `packageVersion("tidyr") > "0.7.2"`.
184 | ```
185 |
186 | ### Common patterns
187 |
188 | The following excerpts from tidyverse news entries provide helpful templates to
189 | follow.
190 |
191 | * New family of functions:
192 |
193 | ```
194 | * Support for ordered factors is improved. Ordered factors throw a warning
195 | when mapped to shape (unordered factors do not), and do not throw warnings
196 | when mapped to size or alpha (unordered factors do). Viridis is used as
197 | default colour and fill scale for ordered factors (@karawoo, #1526).
198 |
199 | * `possibly()`, `safely()` and friends no longer capture interrupts: this
200 | means that you can now terminate a mapper using one of these with
201 | Escape or Ctrl + C (#314).
202 | ```
203 |
204 | * New function:
205 |
206 | ```
207 | * New `position_dodge2()` provides enhanced dogding for boxplots...
208 |
209 | * New `stat_qq_line()` makes it easy to add a simple line to a Q-Q plot.
210 | This line makes it easier to judge the fit of the theoretical distribution
211 | (@nicksolomon).
212 | ```
213 |
214 | * New argument to existing function:
215 |
216 | ```
217 | * `geom_segment()` gains a `linejoin` parameter.
218 | ```
219 |
220 | * Function argument changes behaviour:
221 |
222 | ```
223 | * In `separate()`, `col = -1` now refers to the far right position.
224 | Previously, and incorrectly, `col = -2` referred to the far-right
225 | position.
226 | ```
227 |
228 | * Function changes behaviour:
229 |
230 | ```
231 | * `map()` and `modify()` now work with calls and pairlists (#412).
232 |
233 | * `flatten_dfr()` and `flatten_dfc()` now aborts with informative
234 | message if dplyr is not installed (#454).
235 |
236 | * `reduce()` now throws an error if `.x` is empty and `.init` is not
237 | supplied.
238 | ```
239 |
240 | ## Blog post
241 |
242 | For all major and minor releases, the latest news should be turned into a blog
243 | post. The blog post should highlight major user-facing changes, and point to
244 | the release notes for more details. Generally, you should focus on new features
245 | and major improvements, including examples showing the new features in action.
246 | You don't need to describe minor improvements and bug fixes, as the motivated
247 | reader can find these in the release notes.
248 |
--------------------------------------------------------------------------------
/package-files.qmd:
--------------------------------------------------------------------------------
1 | # Files {#package-files}
2 |
3 | The majority of advice in @sec-files also applies to files in packages. Important differences are described below.
4 |
5 | ## Names
6 |
7 | * If a file contains a single function, give the file the same name as the
8 | function.
9 |
10 | * If a file contains multiple related functions, give it a concise,
11 | but evocative name.
12 |
13 | * Deprecated functions should live in a file with `deprec-` prefix.
14 |
15 | ## Organisation
16 |
17 | In a file that contains multiple functions, public functions and their
18 | documentation should appear first, with private functions appearing after all
19 | documented functions. If multiple public functions share the same documentation,
20 | they should all immediately follow the documentation block.
21 |
22 | See @sec-documentation for more thorough guidance on documenting functions
23 | in packages.
24 |
25 | ```{r}
26 | # Bad
27 | help_compute <- function() {
28 | # ... Lots of code ...
29 | }
30 |
31 | #' My public function
32 | #'
33 | #' This is where the documentation of my function begins.
34 | #' ...
35 | #' @export
36 | do_something_cool <- function() {
37 | # ... even more code ...
38 | help_compute()
39 | }
40 | ```
41 |
42 | ```{r}
43 | # Good
44 | #' Lots of functions for doing something cool
45 | #'
46 | #' ... Complete documentation ...
47 | #' @name something-cool
48 | NULL
49 |
50 | #' @describeIn something-cool Get the mean
51 | #' @export
52 | get_cool_mean <- function(x) {
53 | # ...
54 | }
55 |
56 | #' @describeIn something-cool Get the sum
57 | #' @export
58 | get_cool_sum <- function(x) {
59 | # ...
60 | }
61 | ```
62 |
--------------------------------------------------------------------------------
/pipes.qmd:
--------------------------------------------------------------------------------
1 | # Pipes
2 |
3 | ## Introduction
4 |
5 | Use `|>` to emphasise a sequence of actions, rather than the object that the actions are being performed on.
6 |
7 | The tidyverse has been designed to work particularly well with the pipe, but you can use it with any code, particularly in conjunction with the `_` placeholder.
8 |
9 | ```{r}
10 | strings |>
11 | str_replace("a", "b") |>
12 | str_replace("x", "y")
13 |
14 | strings |>
15 | gsub("a", "b", x = _) |>
16 | gsub("x", "y", x = _)
17 | ```
18 |
19 | Avoid using the pipe when:
20 |
21 | * You need to manipulate more than one object at a time. Reserve pipes for a
22 | sequence of steps applied to one primary object.
23 |
24 | * There are meaningful intermediate objects that could be given
25 | informative names.
26 |
27 | ## Whitespace
28 |
29 | `|>` should always have a space before it, and should usually be followed by a new line. After the first step, each line should be indented by two spaces. This structure makes it easier to add new steps (or rearrange existing steps) and harder to overlook a step.
30 |
31 | ```{r}
32 | # Good
33 | iris |>
34 | summarize(across(where(is.numeric), mean), .by = Species) |>
35 | pivot_longer(!Species, names_to = "measure", values_to = "value") |>
36 | arrange(value)
37 |
38 | # Bad
39 | iris |> summarize(across(where(is.numeric), mean), .by = Species) |>
40 | pivot_longer(!Species, names_to = "measure", values_to = "value") |>
41 | arrange(value)
42 | ```
43 |
44 | ## Long lines
45 |
46 | If the arguments to a function don't all fit on one line, put each argument on
47 | its own line and indent:
48 |
49 | ```{r}
50 | # Good
51 | iris |>
52 | summarise(
53 | Sepal.Length = mean(Sepal.Length),
54 | Sepal.Width = mean(Sepal.Width),
55 | .by = Species
56 | )
57 |
58 | # Bad
59 | iris |>
60 | summarise(Sepal.Length = mean(Sepal.Length), Sepal.Width = mean(Sepal.Width), .by = Species)
61 | ```
62 |
63 | For data analysis, we recommend using the pipe whenever a function needs to span multiple lines, even if it's only a single step.
64 |
65 | ```{r}
66 | # Bad
67 | summarise(
68 | iris,
69 | Sepal.Length = mean(Sepal.Length),
70 | Sepal.Width = mean(Sepal.Width),
71 | .by = Species
72 | )
73 | ```
74 |
75 | ## Short pipes
76 |
77 | It's ok to write a short pipe on a single line:
78 |
79 | ```{r}
80 | # Ok
81 | iris |> subset(Species == "virginica") |> _$Sepal.Length
82 | iris |> summarise(width = Sepal.Width, .by = Species) |> arrange(width)
83 | ```
84 |
85 | But because short pipes often become longer pipes, we recommend that you generally stick to one function per line:
86 |
87 | ```{r}
88 | # Better
89 | iris |>
90 | subset(Species == "virginica") |>
91 | _$Sepal.Length
92 |
93 | iris |>
94 | summarise(width = Sepal.Width, .by = Species) |>
95 | arrange(width)
96 | ```
97 |
98 | Sometimes it's useful to include a short pipe as an argument to a function in a
99 | longer pipe. Carefully consider whether the code is more readable with a short
100 | inline pipe (which doesn't require a lookup elsewhere) or if it's better to move
101 | the code outside the pipe and give it an evocative name.
102 |
103 | ```{r}
104 | # Good
105 | x |>
106 | semi_join(y |> filter(is_valid))
107 |
108 | # Ok
109 | x |>
110 | select(a, b, w) |>
111 | left_join(y |> select(a, b, v), join_by(a, b))
112 |
113 | # Better
114 | x_join <- x |> select(a, b, w)
115 | y_join <- y |> select(a, b, v)
116 | left_join(x_join, y_join, join_by(a, b))
117 | ```
118 |
119 | ## Assignment
120 |
121 | There are three acceptable forms of assignment:
122 |
123 | * Variable name and assignment on separate lines:
124 |
125 | ```{r}
126 | iris_long <-
127 | iris |>
128 | gather(measure, value, -Species) |>
129 | arrange(-value)
130 | ```
131 |
132 | * Variable name and assignment on the same line:
133 |
134 | ```{r}
135 | iris_long <- iris |>
136 | gather(measure, value, -Species) |>
137 | arrange(-value)
138 | ```
139 |
140 | * Assignment at the end of the pipe with `->`:
141 |
142 | ```{r}
143 | iris |>
144 | gather(measure, value, -Species) |>
145 | arrange(-value) ->
146 | iris_long
147 | ```
148 |
149 | I think that the third is the most natural to write, but makes reading a little
150 | harder: when the name comes first, it can act as a heading to remind
151 | you of the purpose of the pipe.
152 |
153 | ## magrittr
154 |
155 | We recommend you use the base `|>` pipe instead of magrittr's `%>%`.
156 |
157 | ```{r}
158 | # Good
159 | iris |>
160 | summarise(width = Sepal.Width, .by = Species) |>
161 | arrange(width)
162 |
163 | # Bad
164 | iris %>%
165 | summarise(width = Sepal.Width, .by = Species) %>%
166 | arrange(width)
167 | ```
168 |
169 | As of R 4.3.0, the base pipe provides all the features from magrittr that we recommend using.
170 |
--------------------------------------------------------------------------------
/plausible.html:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/style.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 | BuildType: Website
16 |
--------------------------------------------------------------------------------
/styler-addin.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tidyverse/style/8d69afc7c8a09b6ab415820b869ca1ef7099ec6a/styler-addin.png
--------------------------------------------------------------------------------
/styles.scss:
--------------------------------------------------------------------------------
1 | /*-- scss:rules --*/
2 |
3 | .sidebar-title {
4 | color: #301445;
5 | }
6 |
7 | div.sidebar-item-container .active {
8 | font-weight: bold;
9 | }
10 |
11 | .sidebar nav[role=doc-toc] ul>li>a.active, .sidebar nav[role=doc-toc] ul>li>ul>li>a.active{
12 | font-weight: bold;
13 | }
14 |
15 | img.quarto-cover-image {
16 | box-shadow: 0 .5rem 1rem rgba(0,0,0,.15);
17 | }
18 |
19 | /* Headings ------------------------------------------------------ */
20 |
21 | #title-block-header.quarto-title-block.default .quarto-title h1.title {
22 | margin-bottom: 0.5rem;
23 | }
24 |
25 | h2 {
26 | margin-top: 2rem;
27 | margin-bottom: 1rem;
28 | font-size: 1.4rem;
29 | font-weight: 600;
30 | }
31 | h3 { margin-top: 1.5em; font-size: 1.2rem; font-weight: 500; }
32 | h4 { margin-top: 1.5em; font-size: 1.1rem; }
33 | h5 { margin-top: 1.5em; font-size: 1rem; }
34 |
35 | .quarto-section-identifier {
36 | color: #6C6C6C;
37 | font-weight: normal;
38 | }
39 |
40 | /* Code ------------------------------------------------ */
41 |
42 | code {
43 | color: #3a373c;
44 | }
45 |
46 | code a:any-link {
47 | text-decoration: underline;
48 | text-decoration-color: #ccc;
49 | }
50 |
51 | pre {
52 | background-image: linear-gradient(160deg,#f8f8f8 0,#f1f1f1 100%);
53 | }
54 |
--------------------------------------------------------------------------------
/syntax.qmd:
--------------------------------------------------------------------------------
1 | # Syntax
2 |
3 | ## Object names {#sec-objectnames}
4 |
5 | > "There are only two hard things in Computer Science: cache invalidation and
6 | > naming things."
7 | >
8 | > --- Phil Karlton
9 |
10 | Variable and function names should use only lowercase letters, numbers, and `_`.
11 | Use underscores (`_`) (so called snake case) to separate words within a name.
12 |
13 | ```{r}
14 | # Good
15 | day_one
16 | day_1
17 |
18 | # Bad
19 | DayOne
20 | dayone
21 | ```
22 |
23 | Base R uses dots in function names (`contrib.url()`) and class names
24 | (`data.frame`), but it's better to reserve dots exclusively for the S3 object
25 | system. In S3, methods are given the name `function.class`; if you also use
26 | `.` in function and class names, you end up with confusing methods like
27 | `as.data.frame.data.frame()`.
28 |
29 | If you find yourself attempting to cram data into variable names (e.g. `model_2018`, `model_2019`, `model_2020`), consider using a list or data frame instead.
30 |
31 | Generally, variable names should be nouns and function names should be verbs.
32 | Strive for names that are concise and meaningful (this is not easy!).
33 |
34 | ```{r}
35 | # Good
36 | day_one
37 |
38 | # Bad
39 | first_day_of_the_month
40 | djm1
41 | ```
42 |
43 | Where possible, avoid re-using names of common functions and variables. This
44 | will cause confusion for the readers of your code.
45 |
46 | ```{r}
47 | # Bad
48 | T <- FALSE
49 | c <- 10
50 | mean <- function(x) sum(x)
51 | ```
52 |
53 | ## Spacing
54 |
55 | ### Commas
56 |
57 | Always put a space after a comma, never before, just like in regular English.
58 |
59 | ```{r}
60 | # Good
61 | x[, 1]
62 |
63 | # Bad
64 | x[,1]
65 | x[ ,1]
66 | x[ , 1]
67 | ```
68 |
69 | ### Parentheses
70 |
71 | Do not put spaces inside or outside parentheses for regular function calls.
72 |
73 | ```{r}
74 | # Good
75 | mean(x, na.rm = TRUE)
76 |
77 | # Bad
78 | mean (x, na.rm = TRUE)
79 | mean( x, na.rm = TRUE )
80 | ```
81 |
82 | Place a space before and after `()` when used with `if`, `for`, or `while`.
83 |
84 | ```{r}
85 | # Good
86 | if (debug) {
87 | show(x)
88 | }
89 |
90 | # Bad
91 | if(debug){
92 | show(x)
93 | }
94 | ```
95 |
96 | Place a space after `()` used for function arguments:
97 |
98 | ```{r}
99 | # Good
100 | function(x) {}
101 |
102 | # Bad
103 | function (x) {}
104 | function(x){}
105 | ```
106 |
107 | ### Embracing
108 |
109 | The embracing operator, `{{ }}`, should always have inner spaces to help emphasise its special behaviour:
110 |
111 | ```{r}
112 | # Good
113 | max_by <- function(data, var, by) {
114 | data |>
115 | group_by({{ by }}) |>
116 | summarise(maximum = max({{ var }}, na.rm = TRUE))
117 | }
118 |
119 | # Bad
120 | max_by <- function(data, var, by) {
121 | data |>
122 | group_by({{by}}) |>
123 | summarise(maximum = max({{var}}, na.rm = TRUE))
124 | }
125 | ```
126 |
127 |
128 | ### Infix operators
129 |
130 | Most infix operators (`==`, `+`, `-`, `<-`, etc.) should always be surrounded by
131 | spaces:
132 |
133 | ```{r}
134 | # Good
135 | height <- (feet * 12) + inches
136 | mean(x, na.rm = TRUE)
137 |
138 | # Bad
139 | height<-feet*12+inches
140 | mean(x, na.rm=TRUE)
141 | ```
142 |
143 | There are a few exceptions, which should never be surrounded by spaces:
144 |
145 | * The operators with [high precedence][syntax]: `::`, `:::`, `$`, `@`, `[`,
146 | `[[`, `^`, unary `-`, unary `+`, and `:`.
147 |
148 | ```{r}
149 | # Good
150 | sqrt(x^2 + y^2)
151 | df$z
152 | x <- 1:10
153 |
154 | # Bad
155 | sqrt(x ^ 2 + y ^ 2)
156 | df $ z
157 | x <- 1 : 10
158 | ```
159 |
160 | * Single-sided formulas when the right-hand side is a single identifier.
161 |
162 | ```{r}
163 | # Good
164 | ~foo
165 | tribble(
166 | ~col1, ~col2,
167 | "a", "b"
168 | )
169 |
170 | # Bad
171 | ~ foo
172 | tribble(
173 | ~ col1, ~ col2,
174 | "a", "b"
175 | )
176 | ```
177 |
178 | Note that single-sided formulas with a complex right-hand side do need a space.
179 |
180 | ```{r}
181 | # Good
182 | ~ .x + .y
183 |
184 | # Bad
185 | ~.x + .y
186 | ```
187 |
188 | * When used in tidy evaluation `!!` (bang-bang) and `!!!` (bang-bang-bang)
189 | (because they have precedence equivalent to unary `-`/`+`).
190 |
191 | ```{r}
192 | # Good
193 | call(!!xyz)
194 |
195 | # Bad
196 | call(!! xyz)
197 | call( !! xyz)
198 | call(! !xyz)
199 | ```
200 |
201 | * The help operator.
202 |
203 | ```{r}
204 | # Good
205 | package?stats
206 | ?mean
207 |
208 | # Bad
209 | package ? stats
210 | ? mean
211 | ```
212 |
213 | ### Extra spaces
214 |
215 | Adding extra spaces is ok if it improves alignment of `=` or `<-`.
216 |
217 | ```{r}
218 | # Good
219 | list(
220 | total = a + b + c,
221 | mean = (a + b + c) / n
222 | )
223 |
224 | # Also fine
225 | list(
226 | total = a + b + c,
227 | mean = (a + b + c) / n
228 | )
229 | ```
230 |
231 | Do not add extra spaces to places where space is not usually allowed.
232 |
233 | ## Vertical space
234 |
235 | Use vertical whitespace sparingly, and primarily to separate your "thoughts" in code, much like paragraph breaks in prose.
236 |
237 | * Avoid empty lines at the start or end of functions.
238 | * Only use a single empty line when needed to separate functions or pipes.
239 | * It often makes sense to put an empty line before a comment block, to help visually connect the explanation with the code that it applies to.
240 |
241 | ## Function calls
242 |
243 | ### Named arguments {#argument-names}
244 |
245 | A function's arguments typically fall into two broad categories: one supplies
246 | the __data__ to compute on; the other controls the __details__ of computation.
247 | When you call a function, you typically omit the names of data arguments,
248 | because they are used so commonly. If you override the default value of an
249 | argument, use the full name:
250 |
251 | ```{r}
252 | # Good
253 | mean(1:10, na.rm = TRUE)
254 |
255 | # Bad
256 | mean(x = 1:10, , FALSE)
257 | mean(, TRUE, x = c(1:10, NA))
258 | ```
259 |
260 | Avoid partial matching, where you supply a unique prefix of a function argument.
261 |
262 | ```{r}
263 | # Good
264 | rep(1:2, times = 3)
265 | cut(1:10, breaks = c(0, 4, 11))
266 |
267 | # Bad
268 | rep(1:2, t = 3)
269 | cut(1:10, br = c(0, 4, 11))
270 | ```
271 |
272 | ### Assignment
273 |
274 | Avoid assignment in function calls:
275 |
276 | ```{r}
277 | # Good
278 | x <- complicated_function()
279 | if (nzchar(x) < 1) {
280 | # do something
281 | }
282 |
283 | # Bad
284 | if (nzchar(x <- complicated_function()) < 1) {
285 | # do something
286 | }
287 | ```
288 |
289 | The only exception is in functions that capture side-effects:
290 |
291 | ```{r}
292 | output <- capture.output(x <- f())
293 | ```
294 |
295 | ### Long function calls
296 |
297 | Strive to limit your code to 80 characters per line. This fits comfortably on a
298 | printed page with a reasonably sized font. If you find yourself running out of
299 | room, this is a good indication that you should encapsulate some of the work in
300 | a separate function or use early returns to reduce the nesting in your code.
301 |
302 | If a function call is too long to fit on a single line, use one line each for
303 | the function name, each argument, and the closing `)`.
304 | This makes the code easier to read and to change later.
305 |
306 | ```{r}
307 | # Good
308 | do_something_very_complicated(
309 | something = "that",
310 | requires = many,
311 | arguments = "some of which may be long"
312 | )
313 |
314 | # Bad
315 | do_something_very_complicated("that", requires, many, arguments,
316 | "some of which may be long"
317 | )
318 | ```
319 |
320 | As described under [Named arguments](#argument-names), you can omit the argument names
321 | for very common arguments (i.e. for arguments that are used in almost every
322 | invocation of the function). If this introduces a large disparity between the line lengths, you may want to supply names anyway:
323 |
324 | ```{r}
325 | # Good
326 | my_function(
327 | x,
328 | long_argument_name,
329 | extra_argument_a = 10,
330 | extra_argument_b = c(1, 43, 390, 210209)
331 | )
332 |
333 | # Also good
334 | my_function(
335 | x = x,
336 | y = long_argument_name,
337 | extra_argument_a = 10,
338 | extra_argument_b = c(1, 43, 390, 210209)
339 | )
340 | ```
341 |
342 | You may place multiple unnamed arguments on the same line if they are closely
343 | related to each other. A common example of this is creating strings
344 | with `paste()`. In such cases, it's often beneficial to match one line of code
345 | to one line of output.
346 |
347 | ```{r}
348 | # Good
349 | paste0(
350 | "Requirement: ", requires, "\n",
351 | "Result: ", result, "\n"
352 | )
353 |
354 | # Bad
355 | paste0(
356 | "Requirement: ", requires,
357 | "\n", "Result: ",
358 | result, "\n")
359 | ```
360 |
361 | ## Braced expressions {#braced-expressions}
362 |
363 | Braced expressions, `{}`, define the most important hierarchy of R code, allowing you to group multiple R expressions together into a single expression. The most common places to use braced expressions are in function definitions, control flow, and in certain function calls (e.g. `tryCatch()` and `test_that()`).
364 |
365 | To make this hierarchy easy to see:
366 |
367 | * `{` should be the last character on the line.
368 | Related code (e.g., an `if` clause, a function declaration, a trailing comma, ...) must be on the same line as the opening brace.
369 |
370 | * The contents should be indented by two spaces.
371 |
372 | * `}` should be the first character on the line.
373 |
374 | ```{r}
375 | # Good
376 | if (y < 0 && debug) {
377 | message("y is negative")
378 | }
379 |
380 | if (y == 0) {
381 | if (x > 0) {
382 | log(x)
383 | } else {
384 | message("x is negative or zero")
385 | }
386 | } else {
387 | y^x
388 | }
389 |
390 | test_that("call1 returns an ordered factor", {
391 | expect_s3_class(call1(x, y), c("factor", "ordered"))
392 | })
393 |
394 | tryCatch(
395 | {
396 | x <- scan()
397 | cat("Total: ", sum(x), "\n", sep = "")
398 | },
399 | interrupt = function(e) {
400 | message("Aborted by user")
401 | }
402 | )
403 |
404 | # Bad
405 | if (y < 0 && debug) {
406 | message("Y is negative")
407 | }
408 |
409 | if (y == 0)
410 | {
411 | if (x > 0) {
412 | log(x)
413 | } else {
414 | message("x is negative or zero")
415 | }
416 | } else { y ^ x }
417 | ```
418 |
419 | It is occasionally useful to have empty braced expressions, in which case it should be written `{}`, with no intervening space.
420 |
421 | ```{r}
422 | # Good
423 | function(...) {}
424 |
425 | # Bad
426 | function(...) { }
427 | function(...) {
428 |
429 | }
430 | ```
431 |
432 | ## Control flow
433 |
434 | ### Loops
435 |
436 | R defines three types of looping constructs: `for`, `while`, and `repeat` loops.
437 |
438 | * The body of a loop must be a braced expression.
439 |
440 | ```{r}
441 | # Good
442 | for (i in seq) {
443 | x[i] <- x[i] + 1
444 | }
445 |
446 | while (waiting_for_something()) {
447 | cat("Still waiting...")
448 | }
449 |
450 | # Bad
451 | for (i in seq) x[i] <- x[i] + 1
452 |
453 | while (waiting_for_something()) cat("Still waiting...")
454 | ```
455 |
456 | * It is occasionally useful to use a `while` loop with an empty braced expression body to wait. As mentioned in [Braced expressions](#braced-expressions), there should be no space within the `{}`.
457 |
458 | ### If statements
459 |
460 | * A single line if statement must never contain braced expressions. You can use
461 | single line if statements for very simple statements that don't have
462 | side-effects and don't modify the control flow.
463 |
464 | ```{r}
465 | # Good
466 | message <- if (x > 10) "big" else "small"
467 |
468 | # Bad
469 | message <- if (x > 10) { "big" } else { "small" }
470 |
471 | if (x > 0) message <- "big" else message <- "small"
472 |
473 | if (x > 0) return(x)
474 | ```
475 |
476 | * A multiline if statement must contain braced expressions.
477 |
478 | ```{r}
479 | # Good
480 | if (x > 10) {
481 | x * 2
482 | }
483 |
484 | if (x > 10) {
485 | x * 2
486 | } else {
487 | x * 3
488 | }
489 |
490 | # Bad
491 | if (x > 10)
492 | x * 2
493 |
494 | # In particular, this if statement will only parse when wrapped in a braced
495 | # expression or call
496 | {
497 | if (x > 10)
498 | x * 2
499 | else
500 | x * 3
501 | }
502 | ```
503 |
504 | * When present, `else` should be on the same line as `}`.
505 |
506 | * Avoid implicit type coercion (e.g. from numeric to logical) in the condition of an if statement:
507 |
508 | ```{r}
509 | # Good
510 | if (length(x) > 0) {
511 | # do something
512 | }
513 |
514 | # Bad
515 | if (length(x)) {
516 | # do something
517 | }
518 | ```
519 |
520 | ::: {.callout-note}
521 | `&` and `|` should never be used inside of an `if` clause because they can return vectors. Always use `&&` and `||` instead.
522 | :::
523 |
524 | ::: {.callout-note}
525 | `ifelse(x, a, b)` is not a drop-in replacement for `if (x) a else b`. `ifelse()` is vectorised (i.e. if `length(x) > 1`, then `a` and `b` will be recycled to match) and it is eager (i.e. both `a` and `b` will always be evaluated).
526 | :::
527 |
528 | ### Control flow modifiers {#control-flow-modifiers}
529 |
530 | Syntax that affects control flow (like `return()`, `stop()`, `break`, or `next`) should always go in their own `{}` block:
531 |
532 | ```{r}
533 | # Good
534 | if (y < 0) {
535 | stop("Y is negative")
536 | }
537 |
538 | find_abs <- function(x) {
539 | if (x > 0) {
540 | return(x)
541 | }
542 | x * -1
543 | }
544 |
545 | for (x in xs) {
546 | if (is_done(x)) {
547 | break
548 | }
549 | }
550 |
551 | # Bad
552 | if (y < 0) stop("Y is negative")
553 |
554 | find_abs <- function(x) {
555 | if (x > 0) return(x)
556 | x * -1
557 | }
558 |
559 | for (x in xs) {
560 | if (is_done(x)) break
561 | }
562 | ```
563 |
564 | ### Switch statements
565 |
566 | * Avoid position-based `switch()` statements (i.e. prefer names).
567 | * Each element should go on its own line unless all element can fit on one line.
568 | * Elements that fall through to the following element should have a space after `=`.
569 | * Provide a fall-through error unless you have previously validated the input.
570 |
571 | ```{r}
572 | # Good
573 | switch(x,
574 | a = ,
575 | b = 1,
576 | c = 2,
577 | stop("Unknown `x`", call. = FALSE)
578 | )
579 |
580 | # Bad
581 | switch(x,
582 | a =,
583 | b = 1,
584 | c = 2
585 | )
586 | switch(x,
587 | a = long_function_name1(), b = long_function_name2(),
588 | c = long_function_name2()
589 | )
590 | switch(y, 1, 2, 3)
591 | ```
592 |
593 | ## Semicolons
594 |
595 | Semicolons are never recommended.
596 | In particular, don't put `;` at the end of a line, and don't use `;` to put multiple commands on one line.
597 |
598 | ```{r}
599 | # Good
600 | my_helper()
601 | my_other_helper()
602 |
603 | # Bad
604 | my_helper();
605 | my_other_helper();
606 |
607 | { my_helper(); my_other_helper() }
608 | ```
609 |
610 | ## Assignment
611 |
612 | Use `<-`, not `=`, for assignment.
613 |
614 | ```{r}
615 | # Good
616 | x <- 5
617 |
618 | # Bad
619 | x = 5
620 | ```
621 |
622 | ## Data
623 |
624 | ### Character vectors
625 |
626 | Use `"`, not `'`, for quoting text. The only exception is when the text already
627 | contains double quotes and no single quotes.
628 |
629 | ```{r}
630 | # Good
631 | "Text"
632 | 'Text with "quotes"'
633 | 'A link'
634 |
635 | # Bad
636 | 'Text'
637 | 'Text with "double" and \'single\' quotes'
638 | ```
639 |
640 | ### Logical vectors
641 |
642 | Prefer `TRUE` and `FALSE` over `T` and `F`.
643 |
644 | ## Comments
645 |
646 | Each line of a comment should begin with the comment symbol and a single
647 | space: `# `
648 |
649 | In data analysis code, use comments to record important findings and analysis
650 | decisions. If you need comments to explain what your code is doing, consider
651 | rewriting your code to be clearer. If you discover that you have more comments
652 | than code, consider switching to [R Markdown][rmd].
653 |
654 | [syntax]: https://rdrr.io/r/base/Syntax.html
655 | [rmd]: https://rmarkdown.rstudio.com/
656 |
--------------------------------------------------------------------------------
/tests.qmd:
--------------------------------------------------------------------------------
1 | # Tests
2 |
3 | ## Organisation
4 |
5 | The organisation of test files should match the organisation of `R/` files: if a function lives in `R/foofy.R`, then its tests should live in `tests/testthat/test-foofy.R`.
6 |
7 | Use `usethis::use_test()` to automatically create a file with the correct name.
8 |
9 | The file name will be displayed in output in order to get context.
10 |
--------------------------------------------------------------------------------