r 闪亮认证(包括演示应用程序)
实践教程
为您的公司创建专业的 web 应用程序
用 R Shiny 构建企业级应用!—此处提供演示
序文
Shiny 非常强大,可以快速构建交互式 web 应用程序。这也是许多数据科学家的首选技术解决方案,因为它是 R-native。
由于并非所有的网络应用都是公开的,控制访问的能力应该是任何有抱负的数据科学家的知识的一部分。
本教程将向你介绍几个选项来通过认证层保护你的应用程序。
注意:为了从本教程中获得最大收益,感兴趣的读者应该已经熟悉了基本的 Shiny 原则(例如,反应性,如何使用模块),web 开发(例如,html 标签和 css),以及 RStudio 项目。
闪亮的应用伴侣
本教程附带一个 R Shiny 演示应用程序,你可以在这里访问。
完整代码也在我的 bitbucket 账号 上。
简单介绍一下 3 种提议的方法
本教程将介绍几种安全访问 R Shiny web 应用程序的方法。
- 首先,我将介绍身份验证的基础知识,构建我自己的登录表单,在用户提供正确凭证的情况下让应用程序出现(而登录表单消失)。
- 然后,我会把登录表单和对应的服务器逻辑打包成一个 模块 。这将增加应用程序的可读性和可维护性,并使您的登录表单易于跨多个应用程序重用。
- 第三步,我将利用shinyauthr包,这基本上是第二步的一个实现,增加了一些功能,包括密码散列(基于钠包)。
- 最后,我简单提一下两个其他方法。
注意:为了便于演示,我创建了一个表来直接存储来自 Shiny server 部分的用户名和密码。请记住,在应用程序部署在生产环境中的情况下,凭证应该加密并存储在数据库中(数据库连接不在本文讨论范围内)。
第一种方法—基础
在本节中,您将学习如何构建自己的登录表单,如下图所示:
访问配套的应用程序来测试它现场这里!
我将介绍登录表单的设计和相应的服务器逻辑**。代码解释分为 3 个步骤:**
- 加载所需的包并构建应用主干
- 构建您自己的完全可定制的登录表单
- 定义服务器端
步骤 1.1 —加载所需的包并构建应用主干
**# 1 - SET THE SCENE**# **load required packages**
library(shiny) # web app framework
library(shinyjs) # improve user experience with JavaScript
library(shinythemes) # themes for shiny
library(tidyverse) # data manipulation
然后我使用 navbarPage 设计了一个简单的应用主干:
**# 2 - UI PART****# app backbone**ui <- **navbarPage**(
title = "R Shiny advanced tips series",
collapsible = TRUE,
windowTitle = "R Shiny tips - TFI",
theme = shinytheme("readable"),
**tabPanel**(
title = "Demo",
useShinyjs() # Include shinyjs )
)**# 3 - SERVER PART**server <- function(input, output, session) {}**# 4 - RUN APP**shinyApp(ui = ui, server = server)
步骤 1.2 —构建您自己的完全可定制的登录表单
登录表单需要以下 3 个组件:
- 一个文本输入来捕获用户名
- 获取密码的密码输入(类似于文本输入,除了文本被隐藏)
- 一个动作按钮确认/弱化对应用程序的访问
这里有一个简单的代码块,您可以根据自己的喜好进行修改。在我的例子中,我主要使用基本的 bootstrap 类来使文本居中(文本居中),并给登录表单添加浅灰色背景(井)。
登录表单应该位于一个带有“类容器的专用 div 中。
还请注意:
- 应定义一个 id,以便在认证成功时,登录表单消失。该部分在步骤 1.3 中定义
- 使用 tagList() 函数在文本输入和密码输入标签旁边添加图标
**div**(
id = "**login-basic**",
style = "width: 500px; max-width: 100%; margin: 0 auto;",
**div**(
class = "**well**",
h4(class = "**text-center**", "Please login"),
p(class = "**text-center**",
tags$small("First approach login form")
),
**textInput**(
inputId = "ti_user_name_basic",
label = **tagList**(icon("user"),
"User Name"),
placeholder = "Enter user name"
),
**passwordInput**(
inputId = "ti_password_basic",
label = **tagList**(icon("unlock-alt"),
"Password"),
placeholder = "Enter password"
),
div(
class = "text-center",
**actionButton**(
inputId = "ab_login_button_basic",
label = "Log in",
class = "btn-primary"
)
)
)
)
步骤 1.3 —定义服务器端
服务器端是开发人员将智能注入应用程序、定义和控制预期行为的地方。
我首先构建了一个简单表来存储用户凭证。在这个例子中,我只授予一个用户(“user_basic_1”)访问权限,但是您当然可以定义任意多的 user x 密码组合。
# >> insert in server part**# create userbase**user_base_basic_tbl <- tibble(
user_name = "user_basic_1",
password = "pass_basic_1"
)
在第二步中,我使用了***event reactive()***函数根据有效凭证(存储在上述用户库中)检查用户的登录和密码。一旦用户单击登录按钮,就会相应地切换真/假布尔参数。
# >> insert in server part**# check credentials vs tibble**validate_password_basic <- **eventReactive**(input$ab_login_button_basic, {
validate <- **FALSE**
if (input$ti_user_name_basic == user_base_basic_tbl$user_name &&
input$ti_password_basic == user_base_basic_tbl$password {validate <- **TRUE**}})
基于这个布尔值:
- 登录表单会隐藏,使用***shinyjs::hide()函数在一个observe event()***内。这需要一个 id 来引用要隐藏的适当的 UI 部分,这就是我在步骤 1.2 中的注释。
- app 的安全部分会出现,采用经典的render ui()+***ui output()***组合
# >> insert in server part**# hide form** **observeEvent**(validate_password_basic(), {
**shinyjs::hide**(id = "login-basic")
})**# show app** output$display_content_basic <- **renderUI**({
req(validate_password_basic())
div(
class = "bg-success",
id = "success_basic",
h4("Access confirmed!"),
p("Welcome to your basically-secured application!")
)
})
uiOutput 应该插入到 ui 部件中
**# app** **uiOutput**(outputId = "display_content_basic")
如果到目前为止你已经能够理解并重现这个例子,那么恭喜你。您刚刚构建了第一个登录组件来保护您的 R Shiny 应用程序。
因为这个过程需要大量的代码,这使得你的应用程序很难长期维护,让我们把它打包成一个模块!
第二种方法——开发登录模块
既然您已经理解了身份验证的基本原理,并且已经构建了您的第一个登录表单,让我们使更健壮和更容易跨多个应用程序重用。这可以通过定义一个专用的 模块 来实现。
一个闪亮的模块是一个闪亮应用的一部分。不能直接运行,闪亮的 app 可以。相反,它是作为一个更大的应用程序的一部分(或者作为一个更大的闪亮模块的一部分——它们是可组合的)。—Wiston Chang—r studio 软件工程师
注意:本教程不会详细介绍如何创建一个模块,我假设读者对它的的关键概念有点熟悉。
简而言之,一个模块由两个函数组成,这两个函数代表 1)一段 UI,以及 2)使用该 UI 的服务器逻辑片段——类似于将闪亮的应用程序分为 UI 和服务器逻辑的方式。
查看下面的简短插图,了解第二种方法将会构建什么;如您所见,UI 部分看起来与我在第一种方法中构建的完全相同。然而,它在引擎盖下有所不同:
点击访问配套应用进行现场测试!
代码解释分为 4 个步骤:
- 构建模块
- 获取模块的源代码
- 在 UI 部分添加登录表单+应用程序的安全部分
- 定义服务器端
步骤 2.1 —构建模块
这个步骤主要是将我在前面的方法中实现的定制 UI 和相关的服务器逻辑整合到一个单独的 module_login 中。R 文件。
下面代码块中需要注意的关键方面:
UI 部分:
- NS() 的用法:创建模块时所有 UI 函数体都要以此行开头。它获取字符串 id 并创建一个名称空间函数。所有 id 名称必须封装在 ns() 中。感兴趣的读者可以在这里了解更多
- 该函数使用另一个变量 title ,以使易于定制登录页面标题,如果您需要在其他未来 R Shiny 应用程序的上下文中使用该模块的话
**# UI component**login_ui <- function(id, **title**) {
ns <- **NS**(id) # namespaced id
# define ui part
**div**(
id = **ns**("login"),
style = "width: 500px; max-width: 100%; margin: 0 auto;",
**div**(
class = "well",
h4(class = "text-center", **title**),
p(class = "text-center",
tags$small("Second approach login form")),
**textInput**(
inputId = **ns**("ti_user_name_module"),
label = tagList(icon("user"),
"User Name"),
placeholder = "Enter user name"
),
**passwordInput**(
inputId = **ns**("ti_password_module"),
label = tagList(
icon("unlock-alt"),
"Password"
),
placeholder = "Enter password"
),
**div**(
class = "text-center",
**actionButton**(
inputId = **ns**("ab_login_button_module"),
label = "Log in",
class = "btn-primary"
)
)
)
)
}
服务器部分:
- 使用 {{ }} 在函数中使用用户提供的表达式。更多关于这个的信息,请点击这里阅读
- 对***shinyjs::hide()***的调用嵌入在模块的服务器部分,因此不再需要像我在第一种方法中那样使用 observeEvent
**# SERVER component**validate_pwd <- function(input, output, session,
data, user_col, pwd_col) {
**# get user and pwd from data/ user_col/ pwd_col information**
user <- data %>% pull(**{{** user_col **}}**)
pwd <- data %>% pull(**{{** pwd_col **}}**)
**# check correctness**
**eventReactive**(input$ab_login_button_module, {
validate <- FALSE
if (input$ti_user_name_module == user &&
input$ti_password_module == pwd) {
validate <- TRUE
}
**# hide login form when user is confirmed**
if (validate) {
**shinyjs::hide**(id = "login")
}
validate
})
}
步骤 2.2 —源模块
在你的 R Shiny 应用程序中使用一个特定的模块之前,要注意对它进行适当的编译(参见我的 bitbucket account 以了解项目结构):
**# source module**
source("modules/module_login.R")
步骤 2.3 —在 UI 部分添加登录表单+应用程序的安全部分
登录表单和应用程序的安全部分现在都可以用两行代码在 UI 部分指定,这确保了应用程序的可读性和可维护性。
**# login form** as defined in the module
**login_ui**(id = "module_login", title = "Please login"),
**# app**
**uiOutput**(outputId = "display_content_module")
步骤 2.4 —定义服务器端
这与 1.3 节非常相似。
我首先构建了一个简单表来存储用户凭证。在这个例子中,我只授予一个用户访问权限,但是您当然可以定义任意多的用户 x 密码组合。
# >> insert in server part**# create userbase**user_base_module_tbl <- tibble(
user_name = "user_module_1",
password = "pass_module_1"
)
然后,我使用 callModule() 函数来访问我的模块的服务器端,并传递在哪里搜索登录和密码的信息(通过 data、user_col 和 pwd_col 参数来引用上面的 userbase):
**# check credentials vs tibble**
validate_password_module <- **callModule**(
module = validate_pwd,
id = "module_login",
data = user_base_module_tbl,
user_col = user_name,
pwd_col = password
)
最后,和第一种方法一样,我使用经典的render ui()+***ui output()组合来显示应用程序的安全部分,只要上面定义的布尔validate _ password _ module()***适当地切换:
**# app**
output$display_content_module <- **renderUI**({
req(validate_password_module())
div(
class = "bg-success",
id = "success_module",
h4("Access confirmed!"),
p("Welcome to your module-secured application!")
)
})
如果到目前为止你已经能够理解并重现这个例子,那么恭喜你。您已经能够将完全可定制的登录表单打包到一个模块中,您可以在未来的所有 web 应用程序中重用该模块。
在下一节中,我们将利用现有的认证模块,该模块在 shinyauthr 包中可用。与构建自己的模块相比,主要优势在于:
- 不需要你掌握建筑模块的艺术, shinyauthr 让你随时可以使用
- 能够通过钠包加密你的密码。钠使用哈希算法,这是专门设计来保护存储的密码免受暴力攻击
第三种方法——利用 SHINYAUTHR 包
Shinyauthr 是一个提供模块函数的 R 包,可以用来给你闪亮的应用添加一个认证层。如果到目前为止您已经成功地构建了自己的模块,那么第三种方法应该很容易遵循,并且我推荐您使用这个包来加速您未来的工作。
查看下面的简短插图,了解第三种方法将会构建什么;虽然它看起来非常类似于方法 1 和 2,但登录表单的可定制性稍差。
访问配套应用程序进行现场测试点击这里!
下面的代码解释将指定新的必需包,说明如何更新 ui 部分并详述服务器端。提醒一下,完整的代码可以在这里获得。
步骤 3.1 —加载所需的包
除了前面提到的包,您还需要加载 shinyauthr 和钠包。
library(shinyauthr) # shiny authentication modules
library(sodium) # crypto library
步骤 3.2 —在 UI 部分添加登录表单+应用程序的安全部分
登录表单和应用程序的安全部分现在都可以用两行代码在 UI 部分指定,这确保了应用程序的可读性和可维护性。
- shinyauthr 包中的 loginUI() 函数与您在第二种方法中构建的函数非常相似。标题(可以定制其他一些参数)
- 应用程序的安全部分通过 uiOutput() 函数显示
**# login form****shinyauthr::loginUI**(
id = "authr_login",
**title** = h4(class = "text-center", "Please login")
),
**# app** **uiOutput**(outputId = "display_content_authr"),
步骤 3.3 —定义服务器端
我首先构建了一个简单表来存储用户凭证。在这个例子中,我只授予一个用户访问权限,但是您当然可以定义任意多的用户 x 密码组合。
注意使用***password _ store()***函数从钠包中加密密码。
# >> insert in server part**# create userbase**uuser_base_authr_tbl <- tibble(
user_name = "user_authr_1",
password = **sodium::password_store**("pass_authr_1")
)
然后我调用了 shinyauthr 包中的两个模块(一个登录表单+一个注销表单)
# >> insert in server partlogout_init <- **callModule**(
module = **shinyauthr::logout**,
id = "authr_logout",
active = reactive(user_auth)
)
credentials <- **callModule**(
module = **shinyauthr::login**,
id = "authr_login",
data = user_base_authr_tbl,
user_col = user_name,
pwd_col = password,
sodium_hashed = TRUE,
log_out = reactive(logout_init())
)
当调用登录模块时,它返回包含 2 个元素的反应列表:
- user_auth (初始值为假)
- 信息(初始值为空)
我在服务器端创建了一个 reactive 对象来捕捉这些值的变化(特别是,如果用户提供了匹配的用户名和密码,user_auth 将变为 TRUE。)
# >> insert in server partuser_auth <- reactive({
credentials()$**user_auth**
})
user_data <- reactive({
credentials()$**info**
})
最后,我需要在 renderUI()函数中定义应用程序的安全部分,类似于前面的方法。
# >> insert in server partoutput$display_content_authr <- **renderUI**({
req(user_auth())
div(
class = "bg-success",
id = "success_module",
h4("Access confirmed!"),
p("Welcome to your shinyauthr-secured application!
Notice that password is encrypted.")
)
})
祝贺您如果您已经做到了这一步,那么您现在就拥有了一种快速、强大且简单的方法来保护您的应用程序!
其他方法
我通常实现上面 3 个详细方法中的一个。
但是,它们并不是唯一的选择,您可能有兴趣探索其他替代方案,例如:
- shinymanager (非常类似于 shinyauthr)
- 完善的一个非免费的解决方案,为您提供除身份验证之外的几个附加功能,例如:自定义登录页面、用户管理、单点登录或用户监控
结论
我希望你觉得这个教程有用,请随时留下评论来分享你的首选(也可能是替代)方法。
**想了解更多?**本文是我高级 R 闪亮小贴士系列的第二篇。第一篇文章是关于构建动态 UI 组件的。
资源
- shinymanager :单一 Shiny 应用的简单安全认证机制。
- shinyauthr : R 包提供模块函数,可用于为您的闪亮应用添加认证层
- 完美的:为您闪亮的应用程序添加身份验证
- 掌握 Shiny :这本书补充了 Shiny 的在线文档,旨在帮助应用程序作者更深入地了解 Shiny
- 商业科学 202A —使用 R 构建可扩展的应用程序:一门很棒的在线课程
R -统计编程语言
让我们不要让冠状病毒阻止我们学习新技能
如果可能的话,我们应该利用这段时间学习一项新技能。在过去的几周里,我一直在学习编程语言 R。
本文旨在概述 R 编程语言以及每个数据科学家都必须熟悉的所有主要概念
动机
数据科学和定量开发领域要求我们不断适应和学习新技能,因为它具有高度动态和高要求的性质。在数据科学家的职业生涯中,学习多种编程语言变得非常重要。随后,我选择了学习 r。
本文旨在概述所有关键的主要领域,并从基础开始解释一切。它假设读者不熟悉编程语言 r,或者对编程语言 r 只有初级的理解。
我强烈推荐 R,原因有很多,我将在本文中重点介绍
克里斯·迪诺托在 Unsplash 上拍摄的照片
r 越来越受欢迎,它是最流行的编程语言之一。r 是统计学家为统计学家写的。它可以很好地与其他编程语言如 C++,Java,SQL 集成。此外,R 主要被视为一种统计编程语言。因此,许多金融机构和大型量化组织在研发过程中使用 R 编程语言。
Python 是一种通用语言,R 可以看作是一种分析性编程语言。
1.文章目标
本文将解释关于 R 的以下关键领域:
- R 是什么?
- 如何安装 R?
- 在哪里编码 R?
- 什么是 R 包和 R 脚本?
- 有哪些不同的 R 数据类型?
- 如何在 R 中声明变量及其作用域?
- 怎么写评论?
- 什么是矢量?
- 什么是矩阵?
- 什么是列表?
- 什么是数据框?
- R 中不同的逻辑运算
- R 中的函数
- R 中的循环
- 读写 R 中的外部数据
- 在 R 中执行统计计算
- 在 R 中绘图
- R 语言中的面向对象编程
- 著名 R 图书馆
- 如何安装外部 R 库
- 在 R 中绘图
让我们开始…
我将从基础开始解释这种编程语言,以使这种语言更容易理解。说了这么多,编程进阶的关键是始终尽可能多的练习。
这篇文章应该为读者打下坚实的基础
2.R 是什么?
- r 是一种在 GNU 许可下的自由编程语言。简而言之,R 是一个统计环境。
- r 主要用于统计计算。它提供了一系列在机器学习领域广泛使用的算法,如时间序列分析、分类、聚类、线性建模等。
- r 也是一个环境,它包括一套软件包,可用于执行数值计算、图表绘制和数据操作。
- r 在统计研究项目中大量使用。
- r 非常类似于 S 编程语言。
- r 在 UNIX、Windows、MacOS、FreeBSD 和 Linux 平台上编译运行。
- r 有大量的数据结构、操作符和特性。它提供了从数组到矩阵,从循环到递归,以及与其他编程语言如 C、C++和 Fortran 的集成。
- 可以用 c 编程语言直接更新 R 对象。
- 可以实现新的 R 包来扩展。
- r 解释程序
- r 受到 S+的启发,因此如果你熟悉 S,那么学习 r 将是一项简单的任务。
R 的好处:
除了上面列出的好处,R 还包括:
- 简单易学
- 大量的统计、分析和图形软件包是开源和免费的
- 大量的学术论文及其在 R 中的实现都是可用的
- 世界顶级大学教授他们的学生 R 编程语言,因此,它现在已经成为一个公认的标准,因此,R 将继续增长。
- 与其他语言的集成能力
- 此外,还有大量的社区支持
R 的局限性:
也有一些限制:
- r 没有 C++快,而且安全性和内存管理也是个问题。
- r 有大量的名称空间,有时会显得太多。然而,情况正在好转。
- r 是一种统计语言,因此不像 Python 那样直观。创建 OOP 不像用 Python 那么简单。
让我们开始学习 R
现在,我将在快速跟进部分介绍 R 语言。
乔纳斯·雅各布森在 Unsplash 上拍摄的照片
3.如何安装 R?
您可以在以下位置安装 R:
- 乌布托
- 苹果个人计算机
- Windows 操作系统
- 一种男式软呢帽
- 一种自由操作系统
- SLES
- OpenSUSE
第一步是下载 R:
- 打开互联网浏览器
- 去 www.r-project.org 的。
- 写这篇文章时最新的 R 版本是 3.6.3(手持风向袋)。它于 2020 年 2 月 29 日发布。
这些是链接:
4.在哪里编码 R?
有多种图形界面可用。我强烈推荐 R-Studio
R-Studio 的截图
下载 RStudio 桌面:
- 从 https://rstudio.com/products/rstudio/download/下载 r studio
- RStudio 桌面开源许可是免费的
- 你可以在这里了解更多:【https://rstudio.com/products/rstudio/#rstudio-desktop】T4
- RStudio 需要 R 3.0.1+ 。
如果您使用的是 Windows,它通常会将 R Studio 安装在以下位置:
C:\Program Files\RStudio
5.什么是 R 包和 R 脚本?
R 包和 R 脚本是 R 的两个关键组件。
r 包
因为 R 是一种开源编程语言,所以理解什么是包是很重要的。包本质上是对代码和其他功能的分组和组织。包是一个可以包含大量文件的库。
数据科学家可以通过创建自己的包或增强他人的包来贡献并与他人共享他们的代码。包允许数据科学家重用代码并将其分发给其他人。
创建包是为了包含函数和数据集
数据科学家可以创建一个包来组织代码、文档、测试、数据集等。然后该包可以与其他人共享。
网上有上万个 R 包。这些包位于中央存储库中。有许多可用的存储库,如 CRAN、Bioconductor、Github 等。
一个值得一提的存储库是 CRAN。它是一个服务器网络,为 r 存储了大量版本的代码和文档。
一个包包含一个描述文件,其中包含日期、依赖项、作者以及包的版本等信息。描述文件帮助消费者获得关于包的有意义的信息。
要加载包,请键入:
*library(name of the package)*
要使用软件包的函数,请键入软件包的名称::函数的名称。
例如,如果我们想使用“carat”包的函数“AdBCDOne ”,那么我们可以:
library(carat)
carat::AdBCDOne()
r 脚本:
R script 是数据科学家编写统计代码的地方。它是一个带有扩展名的文本文件。我们可以称这个脚本为教程。稀有
我们可以在一个包中创建多个 R 脚本。
例如,如果您创建了两个 R 脚本:
- 博客。稀有
- 出版。稀有
如果你想调用出版的功能。r 在博客里。然后,您可以使用 source("target R script ")命令来导入发布。r 进入博客。R :
source("publication.R")
创建 R 脚本的 R 包
过程相对简单。简单地
- 创建描述文件
- 创建 R.scripts 并添加包所需的任何数据集、文档和测试
- 用 R 脚本编写你的功能
- 我们可以使用 devtools 和 roxygen2 通过使用以下命令来创建 R 包:
create_package("name of the package")
6.有哪些不同的 R 数据类型?
为了能够有效地使用 R 编程语言,理解 R 中不同的数据类型和结构是至关重要的。本节将阐述这些概念。
数据类型:
这些是 R 中的基本数据类型:
- 字符:如“abc”或“a”
- 整数:如 5L
- 数字:如 10.5
- 逻辑:真或假
- 复杂:如 5+4i
我们可以使用 typeof(变量)来查找变量的类型。
要查找元数据,如类型的属性,请使用 attributes(variable)命令。
数据结构:
r 中有许多数据结构,最重要的数据结构有:
- vector :最重要的数据结构,本质上是元素的集合。
- 矩阵:具有行和列的表状结构
- 数据框:用于执行统计操作的表格数据结构
- 列出了:可以保存数据类型组合的集合。
- 因子:表示分类数据
当我们开始构建基础时,我将在本文中解释所有这些数据类型和数据结构。
7.如何声明变量?
我们可以创建一个变量并给它赋值。变量可以是上面列出的任何数据类型和数据结构。也有其他可用的数据结构。此外,开发人员可以创建他们自己的自定义类。
变量用于存储可以在代码中更改的值。
作为理解的问题,记住 R 中的环境是什么是至关重要的。本质上,环境是存储变量的地方。它是对的集合,其中对的第一项是符号(变量),第二项是它的值。
环境是分层的(树形结构),因此一个环境可以有一个父环境和多个子环境。根环境是父环境为空的环境。
我们必须声明一个变量,并用→
x <- "my variable"
print(x)
这会将“我的变量”的值设置为变量 x,print()函数会输出 x 的值,也就是“我的变量”。
每当我们声明一个变量并调用它时,就在当前环境中搜索它,并在父环境中递归搜索,直到找到该符号。
要创建整数集合,我们可以这样做:
coll <- 1:5
print(coll)
1 是集合的第一个值,5 是集合的最后一个值。
这将打印 1 2 3 4 5
注意,R-Studio IDE 会跟踪变量:
R 工作室截图
*ls()*
函数可以用来显示当前环境中的变量和函数。
8.怎么写评论?
代码中添加了注释,以帮助读者、其他数据科学家和您自己理解代码。
注意:始终确保注释不会污染您的脚本。
我们可以添加一行注释:
*#This is a single line comment*
我们可以使用双引号添加多行注释:
*"This is a
multi line comment
"*
注意:在 R-Studio 中,选择要注释的代码,然后按 Ctrl+Shift+C
它会自动为你评论代码。
9.什么是矢量?
向量是 r 中最重要的数据结构之一。本质上,向量是元素的集合,其中每个元素都需要具有相同的数据类型,例如逻辑(真/假)、数字、字符。
我们也可以创建一个空向量:
*x <- vector()*
默认情况下,向量的类型是逻辑的,例如 True/False。下面一行将逻辑打印为矢量类型:
*typeof(x)*
要使用元素创建向量,可以使用 concatenate 函数©:
*x <- c("Farhad", "Malik", "FinTechExplained")
print(x)*
这将打印:
[1]《法尔哈德》
【2】《马利克》
【3】《fintech explained》
如果我们想求出一个向量的长度,我们可以使用 length()函数:
*length(x)*
这会打印出 3,因为向量中有三个元素。
要将元素添加到向量中,我们可以将元素与向量结合起来。
例如,在带有一个元素“hello”的 vector 的开头添加“world”:
*x <- c("hello")
x <- c("world", x)
print(x)*
这将打印“世界”“你好”
如果我们混合元素的类型,那么 R 也将适应向量的类型。向量的类型(模式)将变成它认为最适合向量的类型:
*another_vec <- c("test", TRUE)
print(typeof(another_vec))*
虽然第二个元素是一个逻辑值,但类型将被打印为“字符”。
运算也可以在向量上执行。
例如,将一个标量乘以一个向量:
*x <- c(1,2,3)
y <- x*2
print(y)*
这将打印 2,4,6
我们也可以将两个向量相加:
*x <- c(1,2,3)
y <- c(4,5,6)
z <- x+y
print(z)*
这将打印 5 7 9
如果向量是字符,我们想把它们加在一起:
*x <- c("F","A","C")
y <- c("G","E","D")
z <- x+y
print(z)*
它将输出:
x+y 错误:二元运算符的非数字参数
10.什么是矩阵?
矩阵也是 r 中最常见的数据结构之一。
它可以被认为是一个向量的延伸。矩阵可以有多行和多列。矩阵的所有元素必须具有相同的数据类型。
若要创建矩阵,请使用 matrix()构造函数并传入 nrow 和 ncol 来指示列和行:
*x <- matrix(nrow=4, ncol=4)*
这将创建一个名为 x 的矩阵变量,有 4 行 4 列。
通过在构造函数中传递矩阵,可以将向量转换为矩阵。生成的矩阵将按列填充:
*vector <- c(1, 2, 3)
x <- matrix(vector)
print(x)*
这将创建一个 1 列 3 行的矩阵(每个元素一行):
[,1]
【1,】1
【2,】2
【3,】3
如果我们想按行或列填充矩阵,那么我们可以显式地传入行数和列数以及 byrow 参数:
*vector <- c(1, 2, 3, 4)
x <- matrix(vector, nrow=2, ncol=2, byrow=TRUE)
print(x)*
上面的代码创建了一个 2 行 2 列的矩阵。矩阵按行填充。
[,1] [,2]
[1,] 1 2
[2,] 3 4
11.什么是列表和因子?
如果我们想创建一个可以包含不同类型元素的集合,那么我们可以创建一个列表。
列表:
列表是 r 中最重要的数据结构之一。要创建列表,请使用 list()构造函数:
*my_list <- list("hello", 1234, FALSE)*
上面的代码片段演示了如何用不同数据类型的三个元素创建一个列表。
我们可以通过使用索引来访问任何元素,例如:
*item_one = my_list[1]*
这将打印“你好”
我们也可以给每个元素起一个名字,例如
*my_named_list <- list(“A”=1, “B”=2)print(names(my_named_list))It prints “A” “B”print(my_named_list[‘A’])It prints 1*
因素:
因素是分类数据,例如是、否或男性、女性或红色、蓝色、绿色等。
可以创建因子数据类型来表示因子数据集:
*my_factor = factor(c(TRUE, FALSE, TRUE))
print(my_factor)*
因子也可以排序:
*my_factor = factor(c(TRUE, FALSE, TRUE), levels = c(TRUE, FALSE))
print(my_factor)*
我们还可以用表格的形式打印这些因子:
*my_factor = factor(c(TRUE, FALSE, TRUE), levels = c(TRUE, FALSE))
print(table(my_factor))*
这将打印:
*TRUE FALSE
2 1*
我们已经讨论了文章的一半。让我们继续学习更多的统计学知识
12.什么是数据框?
大多数(如果不是全部的话)数据科学项目需要表格格式的输入数据。数据框数据结构可以用来表示 r 中的表格数据。每一列可以包含一个元素列表,每一列可以是彼此不同的类型。
要创建 2 列 5 行的数据框,只需执行以下操作:
*my_data_frame <- data.frame(column_1 = 1:5, column_2 = c("A", "B", "C", "D", E"))
print(my_data_frame)column_1 column_2
1 1 A
2 2 B
3 3 C
4 4 D
5 5 E*
13.R 中的不同逻辑运算符
本节概述了常用运算符:
或者:一|二
这将检查一个或两个是否为真。
还有:一和二
这将检查一和二是否为真。
不是:!投入
如果输入为假,将返回真
我们也可以使用、>、isTRUE(输入)等。
14.R 和变量作用域中的函数
有时我们希望代码执行一组任务。这些任务可以按功能分组。函数本质上是 r 中的对象。
在 R 中,参数可以传递给函数,函数也可以返回一个对象。
r 附带了许多内置函数,如 length()、mean()等。
每次我们声明一个函数(或变量)并调用它时,在当前环境中搜索它,并在父环境中递归搜索,直到找到该符号。
一个函数有一个名字。这个存储在 R 环境中。函数体包含函数的语句。
一个函数可以返回值,并且可以选择接受一些参数。
要创建一个函数,我们需要以下语法:
*name_of_function <- function(argument1...argumentN) {
Body of the function}*
例如,我们可以创建一个接受两个整数并返回一个和的函数:
*my_function <- function(x, y) {
z <- x + y
return(z)
}*
要调用该函数,我们可以传入参数:
*z <- my_function(1,2)
print(z)*
这将打印 3。
我们还可以为参数设置默认值,以便在参数值未提供的情况下采用其值:
*my_function <- function(x, y=2) {
z <- x + y
return(z)
}
output <- my_function(1)
print(output)*
y 的缺省值是 2,因此,我们可以调用这个函数而不用传入 y 的值。
需要注意的关键是花括号{…}
让我们看一个复杂的例子,我们将使用逻辑运算符
假设我们想要创建一个接受以下参数的函数:模式、x 和 y。
- 如果模式为真,那么我们要把 x 和 y 相加。
- 如果模式为假,那么我们要减去 x 和 y。
*my_function <- function(mode, x, y) {
if (mode == TRUE)
{
z <- x + y
return(z)
}
else
{
z <- x - y
return(z)
}
}*
要调用函数将 x 和 y 的值相加,我们可以这样做:
*output <- my_function(TRUE, 1, 5)
print(output)*
这将打印 6
让我们回顾一下下面的代码。具体来说,请参见 print(z)的位置:
*my_function <- function(mode, x, y) {
if (mode == TRUE)
{
z <- x + y
return(z)
}
else
{
z <- x - y
return(z)
}
#what happens if we try to print z
print(z)
}*
需要注意的关键是,z 是在括号关闭后打印的。
变量 z 在那里可用吗?这就把我们带到了函数作用域的话题上!
一个函数可以在另一个函数中声明:
*some_func <- function() {
some_func_variable <- 456 another_func <- function() {
another_func_variable <- 123
}
}*
在上面的例子中,some_func 和 another_func 是两个函数。在 some_func 内部声明了另一个 _func。因此,另一个 _func()是 some_func()的私有函数。因此,它不能与外界接触。
如果我在 some_func 之外执行另一个 _func(),如下所示:
*another_func()*
我们将会遇到错误:
another _ func()中的错误:找不到函数“another _ func”
另一方面,我们可以在 some_func()中执行另一个 _func(),它将按预期工作。
现在考虑这段代码来理解 r 中作用域的工作原理。
- some_func_variable 可由 some_func 和 another_func 函数访问。
- 另一个函数变量只能由另一个函数访问
*some_func <- function() {
some_func_variable <- "DEF"
another_func <- function() {
another_func_variable <- "ABC"
print(some_func_variable)
print("Inside another func" + another_func_variable)
}
print(some_func_variable)
print("outside another func" + another_func_variable)
another_func()
}
some_func()*
运行上面的代码会在 R-Studio 中抛出一个异常:
>some _ func()
[1]“DEF”
打印错误(“在另一个 func 之外”+另一个 _func_variable) :
找不到对象’另一个 _ func _ variable ’
如错误所述,未找到另一个 _ func _ 变量。我们可以看到 DEF 被打印出来,这是赋给变量 some_func_variable 的值。
如果我们想访问一个全局变量并给它赋值,使用< < -运算符。在父环境框架中搜索变量。如果找不到变量,则创建一个全局变量。
要添加未知数量的参数,请使用…
*my_func <- function(a, b, ...)
{
print(c)
}my_func(a=1,b=2,c=3)*
15.R 中的循环
r 也支持控制结构。数据科学家可以给 R 代码添加逻辑。本节重点介绍最重要的控制结构:
对于循环
有时候,我们想要迭代集合中的元素。语法是:
*for (i in some_collection) {
print(i)
}*
在上面的例子中,迭代器可以是列表、向量等。上面的代码片段将打印集合的元素。
我们还可以通过使用 seq_along()函数循环遍历一个集合。它接受一个集合并生成一个整数序列。
While 循环
有时,我们必须循环,直到满足一个条件。一旦条件为假,我们就可以退出循环。
我们可以使用 while 循环来实现所需的功能。
在下面的代码中,我们将 x 的值设置为 3,z 的值设置为 0。随后,我们将 z 的值每次递增 1,直到 z 的值等于或大于 x。
*x <- 3z <- 0
while(z < x) {
z <- z + 1}*
If Else(可选)
If Then Else 在编程中大量使用。
简而言之,条件是在 if 控制模块中评估的。如果为真,则执行其代码,否则执行下一个块。下一个块可以是 Else If 或 Else。
如果(条件为真){
#执行语句
}
我们还可以有一个可选的 else:
如果(条件为真){
#执行语句
}
else if(另一个条件为真){
#执行语句
}
否则{
#执行语句
}
*x <- 3
y <- 0if (x == 3) {
y <- 1}
else if (x<3) {
y <- 2}
else {
y <- 3}*
重复
如果我们想要重复一组语句未知的次数(可能直到满足一个条件或者用户输入一个值等等)。)然后我们可以重复/中断语句。休息可以结束迭代。
*repeat {
print("hello")
x <- random()
if (x > 0.5)
{
break; #exit the loop
}
}*
如果我们想跳过一次迭代,我们可以使用下面的语句:
*repeat {
print("hello")
x <- random()
if (x > 0.5)
{
next #iterate again
}
}*
16.读写 R 中的外部数据
r 提供了一系列的包,允许我们读写外部数据,比如 excel 文件或 SQL 表。本节说明了我们如何实现它。
16.1 读取 Excel 文件
*library(openxlsx)
path <-"C:/Users/farhadm/Documents/Blogs/R.xlsx"
res <- read.xlsx(path, 'Sheet1')
head(res)*
这将显示最上面的行:
显示 excel 文件内容的代码片段
16.2 写入 Excel 文件
*columnA <- runif(100,0.1,100)
columnB <- runif(100,5,1000)
df <- data.frame(columnA, columnB)
write.xlsx(df, file = path, sheetName="NewSheet", append=TRUE)*
它用名为 NewSheet 的工作表创建了一个新的 excel 文件:
显示 Excel 内容的代码片段
16.3 读取 SQL 表
我们可以从 SQL 表中读取数据
*library(RODBC)
db <- odbcDriverConnect('driver={SQL
Server};server=SERVERNAME;database=DATABASENAME;trusted_connection=true')
res <- sqlQuery(db, 'SQL QUERY')*
16.4 写入 SQL 表
我们可以写入 SQL 表
*sqlSave(odbcConnection, some data frame, tablename="TABLE", append=TRUE, rownames=FALSE)*
17.在 R 中执行统计计算
众所周知,r 是最流行的统计编程语言之一。理解 r 中的内置统计函数至关重要。本节将概述数据科学家执行的最常见的统计计算。
17.1 填充缺失值:
数据科学项目中最常见的任务之一是填充缺失值。我们可以使用 is.na()来查找空的元素(na 或 NAN):
*vec <- c("test", NA, "another test")
is.na(vec)*
这将打印 FALSE TRUE FALSE,表明 NA 中的第二个元素。
为了更好地理解它们,is.na()将返回所有为 na 的元素/对象。is.nan()将返回所有的 nan 对象。值得注意的是,南是安娜,但安娜不是南。
注意:许多统计函数,如均值、中值等,都有一个参数:na.rm,它表示我们是否要删除 na(缺失值)。
接下来的几次计算将基于以下两个向量:
*A <- c(1,2,5,6.4,6.7,7,7,7,8,9,3,4,1.5,0,10,5.1,2.4,3.4, 4.5, 6.7)
B <- c(4,4.1,0,1.4,2,1,6.7,7,5,5,8,9,3,2,2.5,0,10,5.1,4.3,5.7)print(length(A)) #20
print(length(B)) #20*
向量 A 和 B 都包含 20 个元素的数值。
17.2 平均值
平均值的计算方法是对集合中的值求和,然后除以值的总数:
*my_mean <- mean(A)
print(my_mean)*
17.3 中位数
Median 是排序集合中的中间值。如果有偶数个值,则是两个中间值的平均值:
*my_median <- median(A)
print(my_median)*
17.4 模式
众数是最常见的值。r 缺少计算模式的标准内置函数。但是,我们可以创建一个函数来计算它,如下所示。
*distinct_A <- unique(A)
matches <- match(A, distinct_A)
table_A <- tabulate(matches)
max_A <- which.max(table_A)
mode<-distinct_A[max_A]
print(mode)*
该函数执行以下步骤:
- 计算集合的不同值
- 然后,它找到每个项目的频率,并创建一个表格
- 最后,它找到出现次数最多的词的索引,并将其作为模式返回。
17.5 标准偏差
标准偏差是数值与平均值的偏差。
*sd <- sd(A)
print(sd)*
17.6 差异
方差是标准偏差的平方:
*var <- var(A)
print(var)*
17.7 相关性
相关性有助于我们了解集合之间是否存在关系,以及它们是否随着关系的强度一起移动:
*cor <- cor(A, B)
print(cor)*
我们可以传入特定的相关方法,如 Kendall 或 Spearman。皮尔逊是默认的相关方法。
在方法参数中传递相关方法。
17.8 协方差
协方差是用来告诉我们变量之间的关系的。
*covariance <- cov(A, B)
print(covariance)*
17.9 标准化和规范化数据集
我们经常需要标准化数据,例如使用最小-最大标准化或使用标准化机制计算 z 值。
标准化数据意味着数据集的均值= 0,标准差= 1。它需要从每个观察值中减去平均值,然后除以标准偏差。
我们可以使用比例函数。如果我们想从每个观察值中减去平均值,那么将其中心参数设置为真。
如果我们想要标准化数据,那么我们需要设置它的 scale 参数为 True。
*normal_A <- scale(A, center=TRUE, scale = FALSE)
print(normal_A)standard_A <- scale(A, center=TRUE, scale = TRUE)
print(standard_A)*
17.10 回归模型
回归模型由于其简单性和可解释性,在机器学习解决方案中越来越受欢迎。本质上,回归模型帮助我们理解不同变量之间的关系。
通常,计算一个或多个变量的系数。这些变量是回归变量。它们用于估计和预测另一个变量,即因变量。因变量也称为响应变量。
回归数据是通过抽样收集的,用于预测结果:
- b n 是线性模型可以估计的系数。
- x n 是自变量。我们将收集这些变量的数据并输入到模型中。
作为一个例子,让我们假设我们收集了温度数据集,并希望预测降雨量。我们可以使用如下所示的线性模型:
*Temperature <- c(1,2,5,6.4,6.7,7,7,7,8,9,3,4,1.5,0,10,5.1,2.4,3.4, 4.5, 6.7)
Rainfall <- c(4,4.1,0,1.4,2,1,6.7,7,5,5,8,9,3,2,2.5,0,10,5.1,4.3,5.7)model <- lm(Rainfall~Temperature)*
注意,如果有多个变量用于预测一个变量,例如使用湿度和温度来预测降雨量,我们可以使用 lm()函数,并将公式设置为:
*Temperature <- c(1,2,5,6.4,6.7,7,7,7,8,9,3,4,1.5,0,10,5.1,2.4,3.4, 4.5, 6.7)
Rainfall <- c(4,4.1,0,1.4,2,1,6.7,7,5,5,8,9,3,2,2.5,0,10,5.1,4.3,5.7)
Humidity <- c(4,4.1,0,1.4,2,1,6.7,7,5,5,8,9,3,2,2.5,0,10,5.1,4.3,5.7)model <- lm(Rainfall~Temperature+Humidity)*
然后我们可以打印出模型的概要。
r 将告诉我们关于残差、系数、它们的标准偏差误差、t 值、F 统计量等等:
*print(summary(model))*
它将打印以下统计数据:
*Call:
lm(formula = Rainfall ~ Temperature)Residuals:
Min 1Q Median 3Q Max
-4.2883 -2.2512 -0.2897 1.8661 5.4124Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 4.8639 1.3744 3.539 0.00235 **
Temperature -0.1151 0.2423 -0.475 0.64040
---
Signif. codes:
0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1Residual standard error: 2.933 on 18 degrees of freedom
Multiple R-squared: 0.01239, Adjusted R-squared: -0.04248
F-statistic: 0.2258 on 1 and 18 DF, p-value: 0.6404*
*根据上面的例子,降雨量是-0.1151 温度+ 4.8639
如果我们想要使用模型来估计新值,我们可以使用 predict()函数,其中第一个参数是模型,第二个参数是我们想要预测降雨量的温度值:
*temperature_value <- data.frame(Temperature = 170)
rainfall_value <- predict(model,temperature_value)
print(rainfall_value)*
17.11 贝叶斯模型
贝叶斯模型使用概率来表示未知。目的是输入数据以估计未知参数。
举个例子,假设我们想确定一家公司的股票明天值多少钱。让我们也考虑一下,我们用公司销售额这个变量来估计股票价格。
在这种情况下,股票价格是未知的,我们将使用公司的销售额来计算股票的价格。
我们可以收集历史销售和股票价格的样本,并使用它来找到这两个变量之间的关系。在现实世界的项目中,我们会添加更多的变量来估计股票价格。
需要理解的关键概念是条件概率和贝叶斯定理。文章中解释了这些概念:
为统计学家解释概率的关键概念
towardsdatascience.com](/understanding-probability-and-statistics-the-essentials-of-probability-for-data-scientists-459d61a8da44) [## 理解概率和统计:数据科学家的中心极限定理和收敛
本文是“理解概率与统计”系列文章的第二篇。它侧重于 CLT 和…
towardsdatascience.com](/understanding-probability-and-statistics-central-limit-theorem-and-convergence-for-data-scientists-653c53145400)
本质上,我们试图做的是使用股价的先验概率来预测使用似然数据和标准化常数的后验概率。
install.packages("BAS")
library(BAS)
StockPrice <- c(1,2,5,6.4,6.7,7,7,7,8,9,3,4,1.5,0,10,5.1,2.4,3.4, 4.5, 6.7)
Sales <- c(4,4.1,0,1.4,2,1,6.7,7,5,5,8,9,3,2,2.5,0,10,5.1,4.3,5.7) model <- bas.lm(StockPrice~Sales)
print(summary(model))
注意,我们安装了 BAS 包,然后使用了 BAS 库。然后显示结果:
P(B != 0 | Y) model 1 model 2
Intercept 1.00000000 1.0000 1.00000000
Temperature 0.08358294 0.0000 1.00000000
BF NA 1.0000 0.09120622
PostProbs NA 0.9164 0.08360000
R2 NA 0.0000 0.01240000
dim NA 1.0000 2.00000000
logmarg NA 0.0000 -2.39463218
可以使用参数prior
指定回归系数的不同先验分布,包括
- 《BIC》
- “AIC
- " g 优先"
- “超重力”
- “超 g 拉普拉斯”
- “超 g-n”
- 《JZS》
- " ZS-空"
- “ZS-完整”
- “电子商务本地”
- “e B-全球”
17.12 生成随机数
要在一个范围内生成随机数,请使用 runif 函数。这将打印 100 个介于 0.1 和 10.0 之间的随机数
random_number <- runif(100, 0.1, 10.0)
print(random_number)
我们还可以使用 sample()函数生成替换或不替换的项目和数字。
17.13 泊松分布
我们可以使用泊松分布,并使用家庭为泊松分布的 glm 模型:
output <-glm(formula = Temperature ~ Rainfall+Humidity,
family = poisson)
print(summary(output))
结果将被打印出来:
Deviance Residuals:
Min 1Q Median 3Q Max
-3.2343 -0.8547 -0.1792 0.8487 1.8781Coefficients: (1 not defined because of singularities)
Estimate Std. Error z value Pr(>|z|)
(Intercept) 1.69807 0.17939 9.466 <2e-16 ***
Rainfall -0.02179 0.03612 -0.603 0.546
Humidity NA NA NA NA
---
Signif. codes:
0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1(Dispersion parameter for poisson family taken to be 1)Null deviance: 35.460 on 19 degrees of freedom
Residual deviance: 35.093 on 18 degrees of freedom
AIC: InfNumber of Fisher Scoring iterations: 5
17.14 正常分布
有几种方法可以生成正态分布的数据。最常见的方法是使用样本大小、平均值和标准差调用 rnorm 函数:
y <- rnorm(100, 0, 1)
17.15 远期替代
向前替换是用于求解线性方程组的常见过程:Lx = y
在这种情况下,L 是具有非零对角线元素的下三角系数矩阵 L。
有两个函数可以帮助我们向前和向后替换。
r 有 forwardsolve(A,b)用于下三角 A 上的前向替换,有 backsolve(A,b)用于上三角 A 上的后向替换。
具体来说,它们是:
backsolve(r, x, k = ncol(r), upper.tri = TRUE,
transpose = FALSE)
forwardsolve(l, x, k = ncol(l), upper.tri = FALSE,
transpose = FALSE)
r:上三角矩阵:R x = b
l:下三角矩阵:L x = b
这两个三角矩阵都给出了我们试图求解的系数。
x:这是矩阵,它的列给出了方程的右边。
k:这是要求我们使用的 r 的列数和 x 的行数。
upper.tri: TRUE 然后使用 r 的上三角形部分。
转置:如果为真,那么我们试图为 y 求解 r’ * y = x
输出将与 x 的类型相同,因此,如果 x 是一个向量,那么输出将是一个向量,否则,如果 x 是一个矩阵,那么输出将是一个矩阵。
17.16 T 检验
t 测试可以通过使用 t.test()函数来执行。
例如,R 中的单样本 t 检验可以通过使用t.test(y, mu = 0)
来运行,其中 y 是我们想要检验的变量,而mu
是由零假设指定的平均值:
Humidity <- c(4,4.1,0,1.4,2,1,6.7,7,5,5,8,9,3,2,2.5,0,10,5.1,4.3,5.7)t.test(Humidity, mu = 5)
上面的代码测试湿度是否≤5 的平均值。这是无效假设。
结果是:
单一样本 t 检验
数据:湿度
t = -1.1052,df = 19,p 值= 0.2829
备选假设:真均值不等于 5
95%置信区间:
2.945439 5.634561
样本估计:
x 的均值
4.29
18.在 R 中绘图
这一节将解释在 r 中绘制图表是多么容易。
X-Y 散点图
我生成了以下数据:
x<-runif(200, 0.1, 10.0)
y<-runif(200, 0.15, 20.0)print(x)
print(y)
代码片段将绘制图表
plot(x, y, main='Line Chart')
XY 散点图
相关图
install.packages('ggcorrplot')library(ggcorrplot)
x<-runif(200, 0.1, 10.0)
y<-runif(200, 0.15, 20.0)
z<-runif(200, 0.15, 20.0)data <- data.frame(x,y,z)
corr <- round(cor(data), 1)ggcorrplot(corr)
柱状图
x<-runif(200, 0.1, 10.0)
hist(x)
19.R 语言中的面向对象编程
本节将概述 R 中面向对象编程的概念。理解 R 中如何创建对象是至关重要的,因为它可以帮助您以更简单的方式实现可伸缩的大型应用程序。
在 R 编程语言中需要理解的首要概念是一切都是对象。
函数也是一个对象,如函数部分所解释的。因此,我们必须定义一个函数来创建对象。关键是在对象上设置 class 属性。
r 支持面向对象的概念,比如继承。一个类可以是一个向量。
在 r 中有几种创建类的方法。我将围绕创建 S3 类演示最简单的形式。这包括创建一个属性列表。
在我解释如何构建一个完整的类之前,让我们以一种简化的方式回顾一下步骤:
- 第一步是创建一个命名列表,其中每个元素都有一个名称。每个元素的名称都是该类的一个属性。作为一个例子,这是我们如何在 R 中创建一个人类类:
farhad <- list(firstname="Farhad", lastname="Malik")
class(farhad) <- append(class(farhad), "Human")
- 我们创建了一个具有以下属性的人类类的实例:名字设置为法尔哈德,姓氏设置为马利克。
- 要打印 Human object 实例的 firstname 属性,我们可以这样做:
print(farhad$firstname)
- 现在,让我们转到另一个重要的概念。我们如何为对象创建一个实例方法?
关键是使用命令:UseMethod
这个命令通知 R 系统搜索一个函数。一个对象可以有多个类,UseMethod 使用实例的类来确定要执行哪个方法。
让我们创建 GetName 函数,该函数返回名和姓的连接字符串:
#This is how you create a new generic function
GetName <- function(instance)
{
UseMethod("GetName", instance)
}#This is how you provide a function its body
GetName.Human <- function(instance)
{
return(paste(instance$firstname,instance$lastname))
}GetName(farhad)
最后,让我们创建一个具有属性 firstname 和 lastname 的类 Human。它将有一个函数:GetName(),返回名字和姓氏。
技巧:创建一个返回列表的函数,并将属性作为参数传递给函数。然后使用 UseMethod 命令创建方法。
Human <- function(firstname, lastname)
{
instance <- list(firstname=firstname, lastname=lastname)
class(instance) <- append(class(instance), "Human")
return(instance)
}
GetName <- function(instance)
{
UseMethod("GetName", instance)
}
GetName.Human <- function(instance)
{
return(paste(instance$firstname,instance$lastname))
}
farhad <- Human(firstname="farhad", lastname="malik")
print(farhad)
name <- GetName(farhad)
print(name)
这将打印:
> print(farhad)
$firstname
[1] "Farhad"$lastname
[1] "Malik"attr(,"class")
[1] "list" "Human"
>
>
> name <- GetName(farhad)
> print(name)
[1] "Farhad Malik"
如果我们想创建一个新的类 OfficeWorker,它从 Human 类继承并为 GetName()方法提供不同的功能,该怎么办?
我们可以通过以下方式实现这一目标:
Human <- function(firstname, lastname)
{
instance <- list(firstname=firstname, lastname=lastname)
class(instance) <- append(class(instance), "Human")
return(instance)
}OfficeWorker <- function(firstname, lastname)
{
me <- Human(firstname, lastname)
# Add the class
class(me) <- append(class(me),"OfficeWorker")
return(me)
}
如果我们创建一个办公人员的实例并打印该实例,它将打印以下内容:
worker = OfficeWorker(firstname="some first name", lastname="some last name")
print(worker)> print(worker)
$firstname
[1] "some first name"$lastname
[1] "some last name"attr(,"class")
[1] "list" "Human" "OfficeWorker"
注意,实例的类是 list、Human 和 OfficeWorker
要为办公室工作人员创建不同的功能,我们可以覆盖它:
GetName <- function(instance)
{
UseMethod("GetName", instance)
}
GetName.Human <- function(instance)
{
return(paste(instance$firstname,instance$lastname))
}
GetName.OfficeWorker <- function(instance)
{
return(paste("Office Worker",instance$firstname,instance$lastname))
}
这将打印:
> GetName(worker)
[1] "some first name some last name"
20.如何安装外部 R 包
安装一个 R 包非常简单。
我们所要做的就是键入以下命令:
install.packages("name of package")
要安装多个软件包,我们可以向 install.packages 命令传递一个向量:
install.packages(c("package1","package2"))
例如,CARAT 是最流行的机器学习包之一。
R-Studio 使得安装包变得极其容易。要安装 CARAT,点击右下角的软件包标签,然后点击安装按钮。
输入“克拉”并点击安装
将出现对话框,指示正在安装软件包:
安装软件包后,您可以在控制台窗口中看到它:
The downloaded binary packages are in
C:\Users\AppData\Local\Temp\Rtmp8q8kcY\downloaded_packages
要删除软件包,请键入:
remove.packages("package name")
21.著名 R 图书馆
除了本文提到的 R 库和内置的 R 函数之外,我还推荐了大量有用的 R 包:
- 预言家:预测、数据科学和分析项目
- Plotly :用于图形
- 看门人:用于数据清理
- 插入符号:用于分类和回归训练
- Mlr :用于机器学习项目
- 润滑:用于时间序列数据
- Ggpolot2 :用于可视化
- Dplyr :用于操作和清除数据
- 预测:处理分类数据时
- Dplyr :用于数据操作
22.摘要
本文解释了关于 R 的以下关键领域:
- R 是什么?
- 如何安装 R?
- 在哪里编码 R?
- 什么是 R 包和 R 脚本?
- 有哪些不同的 R 数据类型?
- 如何在 R 中声明变量及其作用域?
- 怎么写评论?
- 什么是矢量?
- 什么是矩阵?
- 什么是列表?
- 什么是数据框?
- R 中不同的逻辑运算
- R 中的函数
- R 中的循环
- 读写 R 中的外部数据
- 在 R 中执行统计计算
- 在 R 中绘图
- R 语言中的面向对象编程
- 著名 R 图书馆
- 如何安装外部 R 库
- 在 R 中绘图
我以一种使语言更容易理解的方式从基础开始解释了编程语言 R。说了这么多,编程进阶的关键是始终尽可能多的练习。
如果您有任何反馈或意见,请告诉我。*
谷歌云上的 R Studio 服务器
萨法尔·萨法罗夫在 Unsplash 上拍摄的照片
**目标:**在 Google 云计算引擎的虚拟机实例上建立一个完全可运行的机器学习服务器。
在现实世界中,云计算和机器学习携手构建、转变和扩展预测建模项目。作为一个 Linux 服务器应用程序,R Studio server 是最好的解决方案之一,可以托管在 Google Cloud(或者 Amazon Web Service 或 Azure)上,以集中的方式自动处理 SQL/ R/ Python 中的大量数据。以下是如何在 Google Cloud 上配置全功能 R Studio 服务器的一步一步的方法:
- 在 Google Cloud 上配置一个虚拟机实例(Ubuntu OS)。
- 在虚拟机上安装 R and R 工作室服务器。
- 创建用户和组。
- 使用 cronR 包调度和运行 R 脚本。
第一步。在 Google Cloud 上配置一个虚拟机实例(Ubuntu OS):
步骤 1.1。创建 Google Cloud 项目:登录 Google Cloud 控制台 创建项目。
创建谷歌云项目(图片由作者提供)
第 1.2 步。创建防火墙规则:通过导航到“菜单”>“网络”下的“防火墙规则”,在 Google 云计算引擎中创建防火墙规则。配置以下设置:
创建防火墙规则(图片由作者提供)
第 1.3 步。创建虚拟机实例:通过导航到“菜单”>“计算引擎”下的“虚拟机实例”,在 Google Cloud 上设置一个新的虚拟机。
创建一个虚拟机实例(图片由作者提供)
第 1.4 步。虚拟机配置:为新的虚拟机实例命名(例如:“rstudio”),并选择一个靠近操作区域的区域,以减少网络延迟。因为 R 将所有的工作数据集存储在内存中,所以尽量给 VM 实例提供尽可能多的内存。在“操作系统映像”下,选择一个支持 OpenSSL 1.0 的最新版本的 Ubuntu。R Studio Server 总是通过不安全的 HTTP 连接进行连接。因此,在防火墙下,“允许 HTTP 流量”。最后,单击“Create”启动实例。
虚拟机配置(图片由作者提供)
第二步。在虚拟机上安装 R and R 工作室服务器:
第 2.1 步。SSH 连接:在 Google Compute Engine 的 VM instances 窗口中,单击新实例旁边的“SSH”。这将启动命令提示符。
SSH 连接(作者图片)
步骤 2.2。更新 apt:更新 apt 以确保我们有最新的软件包可以和 Ubuntu 一起使用。
sudo apt-get update
sudo apt-get upgrade
第 2.3 步。安装 R and R 工作室服务器:
sudo apt-get install r-base r-base-dev
在运行下面几行代码之前,检查一下最新版本的 RStudio 服务器 。安装所有支持包:
sudo apt-get install gdebi-core
wget [https://download2.rstudio.org/server/bionic/amd64/rstudio-server-1.2.5019-amd64.deb](https://download2.rstudio.org/server/bionic/amd64/rstudio-server-1.2.5019-amd64.deb)
sudo gdebi rstudio-server-1.2.5019-amd64.deb
sudo apt-get install libcurl4-openssl-dev libssl-dev libxml2-dev
第三步。创建用户和群组
使用 R studio server 的最大好处之一是,它为我们提供了一个在集中式云框架中与同行协作的窗口。向虚拟机实例添加用户,以便其他人可以同时使用 R Studio 服务器。
第 3.1 步。创建一个组:创建一个组(例如:“marketing”)将使管理团队共享的文件夹和文件变得更加容易。
sudo addgroup marketing
第 3.2 步。创建主用户:创建主用户背后的整个想法是,当同事和同事加入或离开我们时,“主用户”将仍然拥有所有的共享文件。
sudo adduser master
创建一个主用户(图片由作者提供)
第 3.3 步。创建共享文件夹:
cd /home/master
sudo mkdir shared_folder
sudo chown -R master:marketing shared_folder/
sudo chmod -R 770 shared_folder/
第 3.4 步。添加用户并将他们链接到一个共享文件夹:这里我将 Steve 作为一个例子添加到最近创建的“marketing”组中。Steve 的个人文件夹已链接到“主用户的共享文件夹”。
sudo adduser steve
sudo gpasswd -a steve marketing
su - steve
ln -s /home/master/shared_folder /home/steve/shared_folder
exit
添加用户并将他们链接到共享文件夹(图片由作者提供)
就是这样!我们在 Google Cloud 上使用 R Studio 服务器很好。为了在浏览器上打开 R studio 服务器,遵循 URL 语法: http://【外部IP】:8787。例如,如果新配置的虚拟机实例的外部 IP 是 35.185.161.49,那么我们的 R Studio 服务器 URL 将是:http://35.199.10.210:8787/
R Studio 服务器(图片由作者提供)
第四步。使用 cronR 包调度和运行 R 脚本:
安装 cronR 包以在 R Studio 服务器中生成任务调度器。使用该插件自动化虚拟机实例中的任何脚本。
install.packages("cronR")
install.packages("shinyFiles")
cronR 包(图片由作者提供)
第五步:下一步是什么?
我们刚刚在云中完成了机器学习框架的设置。以下是一些建议,说明我们可以做些什么来扩展这种数据建模和预测分析工作流:
1)从内部(CRM 数据库)或外部数据源(第三方供应商,如尼尔森评级或脸书和谷歌广告集)提取、转换和加载数据集到谷歌云计算引擎。
2)用 SQL、R 或 Python 构建数据模型(使用 Reticulate 和 sqldf 包将 python/SQL 脚本源化到 R studio 服务器中)。
3)在源自云的数据模型上构建监督的或无监督的或强化的机器学习算法。使用 cronR 包实现端到端工作流的生产化。将数据集存储在云数据库中。
4)最后,构建一个 BI 仪表板,可视化预测模型(可以是从预测下个月的销售额或流失率到使用无监督聚类模型对客户数据库进行分类的任何内容)。如果感兴趣,可以通过 REST API 将这个实时预测 BI 模型作为一个小部件嵌入到 web/移动应用程序中。
关于作者
[## Sreejith Sreedharan - Sree
数据爱好者。不多不少!你好!我是 Sreejith Sreedharan,又名 Sree 一个永远好奇的数据驱动的…
srees.org](https://srees.org/about)
如果您在理解上述配置方面需要任何帮助,请随时联系我。乐于分享我所知道的!希望这有所帮助:)
r 教程:分析新冠肺炎数据
在现实世界中使用 R 的介绍
这学期我在耶鲁上计量经济学导论课,我们利用 R 和统计学来分析各种数据集。因此,当我在家与自己保持社交距离时,我认为将我在新冠肺炎数据集上学到的一些技术应用起来会很有趣。
我在 Kaggle 上使用了这个页面,发现了一个 CSV 数据集,你可以在这里获得。
让我们看一下数据:
我们有大约 1000 例新冠肺炎病例的数据。我们可以看到每个感染者的年龄,挡泥板,他们是否恢复或死亡,以及其他许多事情。对于本教程,我们将只看其中的几个专栏。让我们用这个 CSV 文件创建一个文件夹,并启动 RStudio。
在 RStudio 中,要导入数据集,我们将转到文件->导入数据集->从文本(基本)。找到您的 CSV 文件,然后单击“导入”您将在 RStudio 的控制台中看到这样一行:
我们将把这一行复制到我们的主 R 脚本中,我将把它保存为脚本。与我们的 CSV 文件放在同一个文件夹中。
为了方便起见,我将把数据框变量重命名为“data”我还将清除所有现有的变量,导入一个名为 Hmisc 的库,并使用它的 describe 函数来更好地理解我们的数据。
如果您运行这段代码,您将在控制台中看到大量信息。例如,我们有 1085 行。如果我们查看死亡部分,我们会看到有 14 个不同的值。这可能看起来有点奇怪,但是死亡一栏要么是 0(没有死亡),要么是 1(没有死亡),或者仅仅是患者的死亡日期。这很难做到,因为我们想要全是 0 和 1。
让我们通过向数据集添加一个 death_dummy 列来解决这个问题,它只包含值 0 和 1。
我们还计算了数据集的死亡率,运行后结果是 5.8%。在本教程的第一部分,我们将分析死亡和未死亡的人的年龄。
年龄
媒体声称新冠肺炎的老年人比年轻人更容易死亡。这是真的吗?让我们用数据集检查一下。首先,我们将我们的数据集分成活着的患者和已经去世的患者,并比较平均年龄。这段代码将为我们做这件事:
请注意,na.rm=TRUE 意味着跳过特定列(在本例中为 age)为 na(或没有值)的行。运行这个程序后,我们得到存活者的平均年龄是 48 岁,而死亡的平均年龄是 68.6 岁。
好的,所以数据确实显示在我们的样本中,那些死去的人年龄更大。但是这对于所有人来说都是真的吗?我们有多大把握这是真的?
我们可以使用 t.test 命令来衡量我们的信心,看看我们是否可以信任我们的方法。在这种情况下,我们将使用 95%的置信区间。
这个简单的命令非常强大。注意,我们给出了活着的病人和死去的病人的年龄。我们来分析一下输出。
查看置信区间,我们可以 95%肯定地说,已经死亡和没有死亡的患者之间的年龄差异是从 16.7 岁到 24.3 岁。现在,看看 p 值。快 0 了。这意味着,在零假设(即两组的年龄相等)下,我们有大约 0%的机会从这个样本中随机获得这样的极端结果。出于这个原因,我们可以合理地拒绝零假设(在 0.05 的常规显著性水平下),并说死于新冠肺炎病的人确实比没有死于该病的人更老。现在,我们来看看性别!
性别
这个会很像。我们想知道男性和女性的死亡率是否相似。让我们再次分割数据并进行 t 检验:
我们将原始数据分成两组。计算平均值后,我们看到数据集中男性的死亡率为 8.5%,而女性为 3.7%。这真是出乎意料。还是那句话,我们能相信这个数据吗?下面是 t.test 输出:
我们 95%的置信区间表明,平均死亡率将比女性高 1.7%至 7.8%。p 值为 0.002 意味着我们可以拒绝男性和女性死亡率相同的无效假设,因为 0.002 < 0.05.
There have been articles written that men indeed do have a higher coronavirus death rate. 这里的是其中之一,如果你感兴趣的话。
结论
正如您所看到的,R 帮助我们非常容易地对重要的数据集进行统计分析。感谢您的阅读!
伯特的种族偏见
BERT 嵌入向量中不公正偏差的理解和可视化
机器学习的公平性和偏见是一个越来越多地讨论的问题。随着迁移学习应用中使用的预训练语言模型的发展,理解预训练模型继承的不公正偏见至关重要。
在这篇文章中,我将重点关注英语 BERT [1],这是一个在多伦多图书语料库[2]和维基百科上训练的语言模型。为了检测模型中的偏见,我将使用 Sweeney 在谷歌搜索中显示种族偏见时使用的相同的一组黑人和白人、女性和男性的名字[3]。她作品中的名字基于 Bertran 等人[4]和 Fryer 和 Levitt [5]。这篇文章的灵感来自雷切尔·托马斯的实用数据伦理课程。
方法学
演示使用了拥抱脸变压器包和bert-base-cased
网络。下面的代码显示了网络的用法(基于 Transformers GitHub 页面的示例)。
使用 HuggingFace transformers 的 BERT 模型——基于 GitHub 示例
这个网络为句子中的每个标记产生 768 维向量。该研究测量如下:给定句子 s ,对于记号 t∈s 如果我们用掩蔽记号[MASK]
替换记号 t 两个记号的嵌入向量彼此有多接近,余弦 _ 相似度(f(t),f(*[MASK]*
)。
BERT 的训练步骤试图为屏蔽的令牌识别正确的令牌,因此,该测量提供了对模型认为给定令牌可以替代[MASK]
的可能性的洞察。为了能够比较这些标量值,该研究调查了成对的标记。例如在句子*“艾米丽是个女人。”,单个令牌字女换成令牌【面具】和令牌男*。通过计算这两个分数,这项研究寻求了一个问题的答案,“这个模型更可能把单词女还是男放在句子的末尾?”
m(p,q) = cosine_similarity(f(p),f(MASK))-cosine_similarity(f(q),f(MASK))
接下来,实验将名字列表放入两种类型的句子中:<NAME> is <ADJ>.
和<NAME> is a <NOUN>.
,其中标记对是某种类型的词对,如女人-男人、演员-女演员或富人-穷人。
每个名称的令牌数
虽然没有必要运行这个实验,但我认为完整的图片包括一个关于名称表示所需的标记数量的注释。
一方面,每个白人男性的名字用一个记号表示,而只有一个白人女性的名字用两个记号表示: Katelyn。另一方面,黑人男性姓名的平均标记数为 2.29,黑人女性姓名的平均标记数为 2.35。只有两个黑人女性的名字用一个 token 代表,而且两个名字都有不同的含义:肯尼亚和钻石。这不是由名字的长度引起的:Nia 这个名字只有两个字母,却用两个符号来表示!
如果一个人理解了 BERT 的 tokenizer 词块[6]背后的算法,她可以得出结论,这是数据集(书籍和维基百科)中黑人名字出现次数少的结果。这意味着
伯特患有针对黑人名字的“T21 偏见”。
为了评估实验结果,我们必须记住这一点。预训练的伯特可能没有足够的黑人名字样本来做我们将在下面看到的表示。
表象中想要的相似性
当研究人员发明单词的嵌入向量表示时,focus 的目标是建立一个向量空间,其中相似的单词彼此靠近。记住这一点,第一个实验包括象征性的男女和演员对。一个合理的假设是,模特更有可能选择男男和男演员作为男性名字,女女和女演员作为女性名字。第一对句子是<NAME> is a [MASK].
、<NAME> is a man.
和<NAME> is a woman.
,第二对句子是<NAME> is an [MASK].
、<NAME> is an actor.
和<NAME> is an actress.
。
男-女,男-女演员投影中的白人名字
在一个二维图形中表示白人的名字,我们可以沿着两个轴在视觉上将女性和男性的名字分开。由于所有的名字都在右上方的四分之一平面上,模型为每个名字确定了更靠近男主角和男主角的[MASK]
。因此,我建议在看数字时,应该分析相互比较的结果,而不是看原始数字。
这是一个很好的例子,它符合人们对模型的期望。让我们把黑人的名字放在桌子上!
男女演员投影中的所有名字
首先,我想指出的是,在投影中,黑人的名字比白人的名字更接近。其次,尽管有一些例外,但演员-演员轴和上一个差不多(不过,没有以前那么容易画门槛线了)。最后,原来在男女轴心上的分离完全消失了。
对白人名字的假设对黑人名字会失效。
不公正的偏见
虽然我们希望从模型中找到与性别相关的名词作为名字,但是在其他一些例子中,模型中的偏见是不必要的。比如用令牌对贫富和对错。
在下一个实验中,我举例说明了句子对*“X 是一个演员。”——“X 是个女演员。”和“X 有钱。”——“X 差。”*。请注意,有一个象征性的区别,单词“an”在新句子中缺失。让我们看看白人的名字!
贫富、男女演员投影中的白人名字
在这个图中,我沿着男演员-女演员轴展示了贫富轴。虽然人们可以沿着演员轴看到前一节中讨论的可能的分离,但人们不能沿着贫富轴看到同样的分离。我觉得不错,模型在这种新的情况下不能区分男女名字,穷富句对(一切都更接近于穷尾)。这是我们应该从公平模型中期待的。让我们看看其他的名字!
所有的名字都在贫富、男女演员的投影中
首先,我想指出,许多黑人的名字在投影上的位置与白人的名字相似。有趣的是,尽管我之前有所期待,黑人名字比白人名字更接近富人阶层。但最重要的是,这句话里有很大一部分黑人名字的表现和白人名字完全不同!在男女演员轴上,最大的差距不到 0.05,而在贫富轴上,拉坦亚和沙尼斯之间的差距大于 0.1。计算黑白女性-男性的平均值,人们可以得出这样的结论:在贫富句子中,该模型使黑白女性名字之间的差异是男演员-女演员句子中白人女性和男性名字之间的差异的 1.8 倍。
在伯特模型中,不公正的偏差大于期望的差异。
所有的名字在对错,演员演员投影
可能的解决方案
如果我们详述前面的例子,我们可以发现关于名字有许多异常。这可能导致基于预先训练的 BERT 网络的模型中的遗传偏差。为了发展一个更公平的网络,我建议考虑以下几点:
- 在输入预训练的 BERT 模型之前,使用命名实体识别过滤名称并用
[UNK]
标记替换它们。 - 用命名实体的特殊标记训练 BERT 模型,并将它们从原始文本中屏蔽掉。
我认为,如果想要使用预先训练好的模型建立一个公平的网络,理解 BERT 中的偏差是很重要的。我希望这个故事有助于实现这一目标。实验中使用的所有代码都在这里可用,我在下面附上了完整的名称表。
《伯特》中的黑白男女姓名表征
参考
[1] Devlin,j .,Chang,M. W .,Lee,k .,& Toutanova,K. (2018 年)。Bert:用于语言理解的深度双向转换器的预训练。 arXiv 预印本 arXiv:1810.04805 。
[2]朱、虞琨;基罗斯,瑞安;泽梅尔,有钱;萨拉赫胡季诺夫、鲁斯兰;乌尔塔松、拉克尔;安东尼奥·托雷巴;桑亚·菲德勒(2015 年)。“对齐书籍和电影:通过观看电影和阅读书籍实现故事般的视觉解释”。第 19-27 页。arXiv:1506.06724[cs。CV 。
[3]斯威尼,L. (2013 年)。在线广告投放中的歧视。队列, 11 (3),10–29。 arXiv:1301.6822
[4] Bertrand M 和 Mullainathan s . Emily 和 Greg 比 Lakisha 和 Jamal 更有就业能力吗?劳动力市场歧视的现场实验。NBER 工作底稿№9873。2003 年 7 月。http://www.nber.org/papers/w9873(截至 2013 年 1 月 9 日)。
[5] Fryer R 和 Levitt S .独特黑人姓名的原因和后果。经济学季刊。2004 年 8 月第 59 卷第 3 期。http://price theory . uchicago . edu/Levitt/Papers/fryerlevit 2004 . pdf(截至 2013 年 1 月 9 日)。
[6]吴,m .舒斯特,陈,z .乐,Q. V .,m .马切里,w .,… &克林纳,J. (2016)。谷歌的神经机器翻译系统:弥合人类和机器翻译之间的鸿沟。 arXiv 预印本 arXiv:1609.08144 。
警察拦截和搜查中的种族差异
变更数据
多年来警察拦截和搜查的统计分析,以确定警察是否对少数群体有偏见
摘要
这份报告旨在调查与黑人或西班牙裔等少数民族相比,警察队伍是否对白人有种族偏见。我分析了斯坦福开放警务项目的数据,该项目收集了 2013 年至 2017 年间宾夕法尼亚州费城警察拦截的标准化数据。我评估了警察做出的有偏见的决定,其基础是检查警察搜查被拦下的司机的频率,以及这些搜查是否能成功找到违禁品。原始数据包含司机的年龄、性别、种族/民族、停车位置以及警察是否搜查过他们等信息。
介绍
几十年来,警察队伍中的种族偏见一直是一个有争议的话题。然而,最近警察拦截和搜查的消息导致了针对无辜美国黑人的暴力事件。例如,46 岁的手无寸铁的美国黑人乔治·弗洛伊德(George Floyd)因涉嫌使用假钞被一名警察杀害。由于警察对黑人的歧视,这一事件在世界范围内引发了抗议和愤怒。
对拦截和搜查率的数据进行评估,可以为是否存在针对少数群体的种族歧视提供证据。另一个策略是评估成功识别违禁品的搜索比例。如果成功搜查少数族裔的比例少于白人,这可能表明警察搜查少数族裔的证据不足。为了缩小分析范围,我将数据细分为 2017 年的车辆警察停车。
数据集解释
在这份数据中,2014 年 1 月 1 日至 2017 年 12 月 31 日期间,宾夕法尼亚州费城发生了 1,756,587 次警察拦截。2017 年,这些警察站中有 294,060 个是车辆警察站。
图 1
直方图(图 1)显示了司机的年龄分布。数据向右倾斜。这表明,与老年人相比,年轻人更容易被警察拦下。被停止的平均年龄约为 35 岁。
图 2
图 2 显示了驾驶员的种族/民族分布。黑人司机的比例过高,因为大约 69%的拦截都是针对黑人司机进行的。只有 17%的停车是对白人司机进行的,10%是对西班牙裔司机进行的。然而,这可能是因为费城的大部分人口是黑人。
被拦下的司机 70%是男性,30%是女性。
结果和数据分析
进行搜索
卡方检验分析
下面是卡方测试分析的代码块。这用于检查司机的种族/民族是否取决于警察是否会搜查司机。“假”表示未被搜索的驾驶员人数,“真”表示被搜索的驾驶员人数。
## Pearson's Chi-squared test
##
## data: search.conducted.table
## X-squared = 430.36, df = 4, **p-value < 2.2e-16****# Expected values for each race/ethnicity**chisq.test(search.conducted.table)$expected##
## white black hispanic
## FALSE 47745.775 190926.34 28194.879
## TRUE 2723.225 10889.66 1608.121**#Residual plot**chisq.test(search.conducted.table)$resid##
## white black hispanic
## FALSE 3.0764323 -2.4243816 0.6081798
## TRUE -10.3478119 10.1514171 -2.5465819
假设检验
H0:进行搜查的警察与司机的种族/民族之间没有联系
H1:进行搜查的警察与司机的种族/民族有关联
设α= 0.05。x 检验统计量的 p 值非常显著(卡方检验的 p < 2.2e-16). There is sufficient evidence to reject the null hypothesis and conclude that there is an association between the police conducting a search and the driver’s race/ethnicity.
Based on the residuals, more Black individuals than expected were searched. Less White individuals than expected were searched.
假设
满足卡方检验的假设。有理由相信样本是独立的,因为这些人是在费城的部分地区取样的。所有预期值都大于 10。
逻辑回归分析:进行搜索
下面是逻辑回归分析的代码块,用于预测司机被拦下后是否会被搜索。一个模型被用来估计警察搜查司机和司机的种族/民族之间的联系,调整混杂变量性别和年龄。
##
## Call:
## glm(formula = search_conducted ~ subject_race + subject_age +
## subject_sex, family = binomial(link = "logit"), data = stops.subset
##
## Deviance Residuals:
## Min 1Q Median 3Q Max
## -0.5829 -0.4007 -0.2894 -0.2184 3.4213
##
## Coefficients:
## Estimate Std. Error z value Pr(>|z|)
## (Intercept) -1.9656439 0.0774345 -25.385 < 2e-16 ***
## subject_raceblack 0.7817956 0.0735729 10.626 < 2e-16 ***
## subject_racehispanic 0.5594270 0.0777094 7.199 6.07e-13 ***
## subject_raceother/unknown 0.2212841 0.1079140 2.051 0.0403 *
## subject_racewhite 0.4758386 0.0764091 6.228 4.74e-10 ***
## subject_age -0.0418716 0.0008089 -51.762 < 2e-16 ***
## subject_sexfemale -1.0650471 0.0234808 -45.358 < 2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## (Dispersion parameter for binomial family taken to be 1)
##
## Null deviance: 123230 on 293539 degrees of freedom
## Residual deviance: 117002 on 293533 degrees of freedom
## (520 observations deleted due to missingness)
## AIC: 117016
##
## Number of Fisher Scoring iterations: 6
斜率系数和 P 值的解释
由于模型中包含了年龄,因此模型截距没有有意义的解释。模型截距表示警察搜索 0 岁男性和亚洲/太平洋岛民种族的预测对数概率。预测警察搜查新生儿的概率是不合理的。
黑人的斜率表明,在保持年龄和性别不变的情况下,警察搜查一名黑人的估计对数概率比搜查一名亚裔/太平洋岛民的概率高 0.78。
黑人和西班牙裔的斜率高于白人的斜率。例如,警察搜查一个黑人的对数概率是警察搜查一个白人的对数概率的 1.36 倍。如果双方都是 35 岁的男性。
这表明警察更可能搜查少数民族而不是白人。
每个种族/民族的斜率系数的 p 值小于α= 0.05。因此,这些数据提供了证据,证明警察搜查司机与司机的种族/族裔有显著关联。
搜查违禁品
双样本比例检验
下面是双样本比例测试的代码块。这用于确定拥有违禁品的白人个体的比例是否低于黑人个体。如果成功搜查黑人的比例低于白人,这表明警察搜查少数民族的证据不足。
#conduct test
prop.test(successes, n)##
## 2-sample test for equality of proportions with continuity correction
##
## data: successes out of n
## X-squared = 6.8023, df = 1, **p-value = 0.009104**
## alternative hypothesis: two.sided
**## 95 percent confidence interval:
## -0.046884129 -0.005958199**
## sample estimates:
**## prop 1 prop 2
## 0.2237007 0.2501219**
假设检验
p1 代表在被搜查的黑人个体中拥有违禁品的黑人个体的比例。
p2 表示在被搜查的白人个体中,拥有违禁品的白人个体的比例。
H0 : p1 = p2
哈:p1 不等于 p2
设α= 0.05。检验统计的 p 值显著(p= 0.009104)。有足够的证据拒绝零假设,并得出结论,两个种族群体之间的比例存在差异。
基于 p1 和 p2 的比较,拥有违禁品的白人个体的比例高于黑人个体。
置信区间
比例差异的 95%置信区间为(-0.047,-0.006)。在 95%的置信度下,白人个体相对于黑人个体拥有违禁品的比例的差异被区间(-0.047,-0.006)捕获。间隔不包含 0。这与差异的统计学显著证据一致。
比例测试的假设
下面是检查每个种族拥有违禁品的司机数量的代码块。“假”表示没有携带违禁品的驾驶员人数,“真”表示携带违禁品的驾驶员人数。
(table(searches$subject_race, searches$contraband_found))##
## FALSE TRUE
## asian/pacific islander 147 48
## black 9276 2673
## hispanic 1202 304
## other/unknown 137 29
## white 1538 513
满足成功-失败条件。对于置信区间和假设检验,每个种族/民族群体发现违禁品的预期成功和失败次数都超过 10 次。假设样本是独立的是合理的。
逻辑回归分析:识别违禁品
下面是逻辑回归分析的代码块,用于预测司机在被搜查后是否会持有违禁品。一个模型被用来估计警察识别违禁品和司机的种族/民族之间的关系,调整了性别和年龄这两个混淆变量。
##
## Call:
## glm(formula = contraband_found ~ subject_race + subject_age +
## subject_sex, family = binomial(link = "logit"), data = stops.subset)
##
## Deviance Residuals:
## Min 1Q Median 3Q Max
## -0.8486 -0.7355 -0.7030 -0.6152 2.0279
##
## Coefficients:
## Estimate Std. Error z value Pr(>|z|)
## (Intercept) -0.767719 0.177718 -4.320 1.56e-05 ***
## subject_raceblack -0.156001 0.168090 -0.928 0.353365
## subject_racehispanic -0.290847 0.178694 -1.628 0.103605
## subject_raceother/unknown -0.452432 0.263905 -1.714 0.086460 .
## subject_racewhite 0.074991 0.174405 0.430 0.667210
## subject_age -0.010238 0.001929 -5.308 1.11e-07 ***
## subject_sexfemale -0.215671 0.058023 -3.717 0.000202 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## (Dispersion parameter for binomial family taken to be 1)
##
## Null deviance: 16875 on 15827 degrees of freedom
## Residual deviance: 16816 on 15821 degrees of freedom
## (278232 observations deleted due to missingness)
## AIC: 16830
##
## Number of Fisher Scoring iterations: 4
解释斜率系数和 P 值
黑人的斜率表明,在保持年龄和性别不变的情况下,警察识别黑人违禁品的估计对数概率比亚裔/太平洋岛民低 0.156。
白人的斜率表明,在保持年龄和性别不变的情况下,白人警察识别违禁品的估计对数概率比亚洲/太平洋岛民高 0.075。
黑色人种的斜率为负,白色人种的斜率为正。这表明白人比黑人更有可能拥有违禁品。
每个种族/民族的斜率系数的 p 值大于α= 0.05。因此,没有足够的证据证明警察识别违禁品与司机的种族/民族有显著的联系。
结论
基于 2013 年至 2017 年宾夕法尼亚州费城 1756587 名被拦司机的数据,有证据表明,这些结果表明对黑人司机存在偏见。这项研究特别研究了司机的种族/民族是否取决于警察是否会搜查司机。与白人相比,黑人和西班牙人更有可能被搜查。该数据还评估了每个种族/民族中拥有违禁品的个人比例。结果显示,白人拥有违禁品的比例高于黑人。因此,根据较少的证据可以合理地得出结论,警察正在搜查少数民族。
严格地说,这个结果适用于居住在宾夕法尼亚州费城的人们。将这些结果推广到宾夕法尼亚州的其他地方也是合理的,因为该州的治安策略是相同的。由于被拦下的司机被搜查的速度不同,因此推广到所有其他州是不合适的。所以其他州的警察不一定对少数民族有偏见。