统计反思与NIX

2020-06-09 00:57:02

这篇文章描述了如何使用nixpkgs、niv和lori为可重现的R工作流设置透明的自动设置。整篇文章中使用的解释例子之一是建立反思包,并列举理查德·麦克雷思(Richard McElreath)优秀的第二版“统计反思”中的一些例子。

正如在前面的帖子1中详细描述的那样,我已经设置了NIX来处理非cran包。如果这一节的其余部分不清楚,请回过头来参考前面的帖子。

#shell.nix{pkgs?import<;nixpkgs>;{}}:with pkgs;let my-r-pkgs=rWrapper。覆盖{Packages=with rPackages;[ggplot2 tidyverse tidybayes tidybayes.。重新思考(buildRPackage{Name=";resiking";;src=fetchFromGitHub{Owner=";rmcelreath";;repo=";rev=";d0978c7f8b6329b94efa2014658d750ae12b1fa2";;sha256=";1qip6x3f6j9lmcmck6sjrj50a5azqfl6rfhp4fdj7ddabpb8n0z0";;};PropagatedBuildInput=[尾巴质量mvtnorm loo form rstan dagitty];});};在mkShell{buildInput=。[打开卷曲的git glibcLocales OpenSSL];inputsFrom=[my-r-pkgs];shellHook=';';mkdir-p";$(Pwd)/_libs";export R_Libs_user=";$(Pwd)/_libs";';';;git_SSL_CAINFO=";locale_archive=stdenv。利布。optionalString stdenv.。isLinux";${glibcLocales}/lib/locale/locale-archive";;}。

不同于npm、pipeenv、potile、conda和Friends,我的系统不会因为每次在不同项目中使用相同的包而下载和设置而变得臃肿。

然而,虽然这是从链接到RStudio和mysystem包管理器迈出的重要一步,但是这个工作流如何可重现可能还不是很明显。诚然,我已经以一种很好的功能方式定义了我的包;但是其他人可能有他们正在跟踪的不同的上行通道,因此会有不同的包。事实上,我唯一能确定的包就是我从Github构建的R包,因为这些包都绑定到一个散列上。最后,为每个项目描述的设置都非常繁琐,而且目前还不清楚如何利用像direnv这样的奇妙工具来解决这个问题。

精明的读者会注意到,我提到过R包是可重现的,因为它们与散列绑定在一起,并且可能会合理地争辩说,整个Nix生态系统首先是关于散列的。一旦我们意识到这一点,剩下的就相对简单了3。

Niv基本上跟踪所有软件包的安装通道。设置非常简单。

让Sources=import./nix/Soures.nix;pkgs=导入源。nixpkgs{};stdenv=pkgs。stdenv;my-r-pkgs=pkgs。rWrapper。用Pkgs覆盖{Packages=。rPackages;[ggplot2 tidyverse tidybayes];};单位为pkgs。mkShell{buildInput=with pkgs;[git glibcLocales OpenSSL,打开卷曲wget my-r-pkgs];shellHook=';';mkdir-p";$(Pwd)/_libs";export R_Libs_user=";$(Pwd)/_libs";';';;git_SSL_CAINFO。cacert}/etc/ssl/certs/ca-bundle.crt";;locale_archive=stdenv。利布。optionalString stdenv.。是Linux";${pkgs。glibcLocales}/lib/locale/locale-archive";;}

我们可以手动检查和编辑这些源代码,但当我们需要更新这些源代码时,简单地再次使用Niv要方便得多。

在这个阶段,我们有一组可重现的软件包可供使用。然而,这仍然是相当恼人的,不得不经历编写nx-shell的麻烦,并且在我们改变事情的时候等待它重新构建。

在过去,我对迪伦夫的钦佩是非常明确的(特别是对蟒蛇诗)。然而,尽管direnv确实允许我们在项目中包含任意的bash逻辑,但是如果有一些可以默认为nix的东西就好了。谢天谢地,TweagIO的人们开发了Lorri来挠痒。

我们可以并且应该检查Lorri希望我们使用direnv文件加载的环境:

这本身并不是太具描述性的,所以我们应该首先自己运行。

EVALUATION_ROOT=";$HOME/.cache/lorri/gc_roots/407bd4df60fbda6e3a656c39f81c03c2/gc_root/shell_gc_root";watch_file";/run/user/1000/lorri/daemon.socket";watch_file";$EVALUATION_ROOT";#!/usr/bin/env bash#^shebang未使用,因为此文件是源文件,但存在于编辑器#集成中。注意:Direnv保证它*将*使用bash进行解析。Function Punt(){:}#Move";OrigPreHook";";preHook";";$@";;Move(){srcvarname=$1#示例:varname可能包含字符串";OrigPATH";#删除源变量名称Shift destvarname=$1#示例:destvarname可能包含字符串";path";#删除目标变量名称Shift#。.SOME-VALUE.";export";${@?}";;#将$Original设置为变量$srcvarname的内容#表示eval";$destvarname=\";${!srcvarname}\";";#将目标变量名称标记为已导出,以便direnv拾取它#(shellcheck:我们确实要导出destvarname!的内容)#shellcheck Disable=SC2163。#从上面移除导出,即:export OrigPATH.。unset";$srcvarname";}函数pretiend(){varname=$1#示例:varname可能包含字符串";path";#删除varname移位分隔符=$1#示例:分隔符通常是字符串";:";#删除分隔符参数,因此剩余的参数#是要导出Shift的参数#将$Original设置为变量$varname#引用Original=";$。#有效接受新变量的内容导出(#34;${@?}";;#将$varname的变量重新设置为varname#引用的内容,以及当前(在导出时更新的)内容。#但是,除非${Original}以值eval";$varname=${!varname}${Original:+${Separator}${Original}}";}函数append(){varname=$1#示例:varname可能包含字符串";path";#删除varname移位分隔符=$1#示例:分隔符通常是字符串";:";#删除分隔符参数,因此剩余的参数#是要导出Shift的参数#将$Original设置为变量$varname的内容#引用原始=";${!varname:-}";#有效接受新变量的内容export";${@?}";;#将$varname的变量重新设置为varname#引用的内容,加上当前(导出时更新的)内容。#但是,除非${Original}以值eval";$varname=${Original:+${Original}${Separator}}${varname}";}varmap(){if[-f";$EVALUATION_ROOT/varmap-v1";];THEN#捕获正在设置的变量的名称IFS=";=";Read-r-a cur_varname<;&,否则请排除${parator};THEN#捕获正在设置的变量的名称IFS=";=";READ-r-a cur_varname<;&。";$1";#在IFS=';';和`read`分隔符为';';的情况下,我们实现了#拆分\0字节,同时还保留了前导#空格:##bash-3.2$printf';<;-前导空格\0bar\0baz\0';\#|(While IFS=';';read-d$';\0';";;完成)#>;<;-前导空格<;#>;bar<;#>;baz<;`而IFS=';';read-r-d&&;&;IFS=';&;&;IFS=';&39;map_Variable\&;&;IFS=';如果[";$MAP_VARIABLE";==";${CUR_varname[0]}";];则如果[";$MAP_INSTRUCTION";==";APPEND";];则附加";$MAP_VARIABLE";";$MAP_SECTOR";";$@";返回FI完成<;";$EVALUATION_ROOT/varmap-v1";FI EXPORT";${@?}";}函数DECLARE(){IF[";$1";==";-x";];然后SHIFT;FI#某些变量需要特殊处理。##-Punt:根本不要设置变量#-Prepend

正如承诺的那样,我们将首先测试设置以确保一切正常。现在也是尝试tidybayes的好时机。重新思考套餐。为了使用它,我们需要以某种方式定义重新思考包,以便我们可以将其传递给buildInput以用于tidybay.reink。我们将修改新的shell.nix,如下所示:

#shell.nix let Sources=import./nix/Soures.nix;pkgs=导入源。nixpkgs{};stdenv=pkgs。重新思考=用pkgs。rPackages;buildRPackage{name=";resiking";;src=pkgs。fetchFromGitHub{Owner=";rmcelreath";;repo=";;rev=";d0978c7f8b6329b94efa2014658d750ae12b1fa2";;sha256=";1qip6x3f6j9lmcmck6sjrj50a5azqfl6rfhp4fdj7ddabpb8n0z0";;};pagatedBuildInput=[尾部质量mvtnorm loo form rstan dagitty];};tidybayes_reink=with pkgs。rPackages;buildRPackage{name=";tidybayes.reessing";;src=pkgs。fetchFromGitHub{Owner=";mjskay";;repo=";tidybayes.resiking";;rev=";df903c88f4f4320795a47c616eef24a690b433a4";;sha256=";1jl3189zdddmwm07z1mk58hcahirqrwx211ms0i1rzbx5y4zak0c";;};pagatedBuildInput=[dplyr Tibble rlang mass tidybayes reessing rstan];};rEnv=pkgs。rWrapper。用Pkgs覆盖{Packages=。rPackages;[ggplot2 tidyverse tidybayes DevTools model r curlot ggrepel RColorBrewer purrr forcats rstan reessing tidybayes_reink];};以pkgs为单位。mkShell{buildInput=with pkgs;[git glibcLocales其中];inputsFrom=[rEnv];shellHook=';';mkdir-p";$(Pwd)/_libs";export R_Libs_user=";$(Pwd)/_libs";';';git_SSL_CAINFO="。cacert}/etc/ssl/certs/ca-bundle.crt";;locale_archive=stdenv。利布。optionalString stdenv.。是Linux";${pkgs。glibcLocales}/lib/locale/locale-archive";;}。

这里需要注意的主要内容是,我们需要派生WeCreate的输出,也就是说,我们需要使用inputsFrom而不是buildInput作为rEnv。

库(Magrittr)库(Dplyr)库(Purrr)库(Forcats)库(Tidyr)库(Delyr)库(Tidybayes)库(tidybayes.resiking)库(Ggplot2)库(Ventlot)库(Rstan)库(Reink)库(Ggrepel)库(RColorBrewer)heme_set(heme_tidybayes())rstan_options(auto_write=true)选项(mc.co.。,";B";,";C";,";D";,";E";),n),响应=rNorm(n*5,c(0,1,2,1,-1),0.5))mtcar_lean=mtcars%>;变异百分比(cyl=factor(Cyl))m_cyl=ulam(ist(cyl~dordlogit(phi,cutpoint),phi<;-b_mpg*mpg,b_mpg~Student_t(3,0,10),cutpoint~Student_t(3,0,10)),data=mtars_lean,Chains=4,Cores=Parallel::DetectCores(),ITER=2000)。%Swing_Draw(cutpoint[cyl])#定义最后一个切割点LAST_Cutpoint=Tibble(.raw=1:max(切割点$.raw),cyl=";8";,cutpoint=inf)切割点=BIND_ROWS(切割点,LAST_Cutpoint)%>;%#定义上一个切割点(cutpoint_{j-1})group_by(.raw)%>;%arrange(Cyl)%>;%mudiate(prev_cutpoint=LAG(cutpoint,默认值=-inf))fitted_cyl_pros=mtars_clee%>;%data_grid(mpg=seq_range(mpg,n=101))%>;%add_fit_tracts(M_Cyl)%>;%INNER_JOIN(cutpoints,by=";.raw";)%>;%mugiate(`P(cyl|mpg)`=#此部分为logit^-1(cutpoint_j-beta*x)-logit^-1(cutpoint_{j-1}-beta*x)plogis(cutpoint-.value)-plogis(prev_cutpoint-.value))data_lot=mtcar_cle%>;%gglot(aes(x=mpg,y=cyl,color=cyl))+geom。,name=";cyl";)fit_lot=fit_cyl_pros%>;%gglot(aes(x=mpg,y=`P(cyl|mpg)`,color=cyl))+stat_lineribbon(AES(Fill=cyl),alpha=1/5)+scale_color_brewer(调色板=";深色2&34;)+scale_ill_brewer(调色板=&。../image/rethinking.png";)PLOT_GRID(nol=1,Align=";v";,Data_Plot,Fit_Plot)dev.off。

这篇文章实际上更多的是对前一篇文章的探索性跟进,并不是真正孤立地工作。话又说回来,在这一点上,一切似乎都进行得很好。R和NIX最终成为了一种真正可行的组合,可以在阳光下进行任何分析。工作流程的某些部分仍然有点不稳定,但随着时间的推移可能会自行解决。

我的动机被列出来了,我