现在,让我们把目光转向“语言”,这个在社会科学中占据重要地位的研究对象。我们不必深究语言的起源、演化或分类,但需要理解语言的功能及其对人类社会发展的重要作用。
语言,本质上是人类用于交流、表达思想和传递信息的符号系统,涵盖口头语言、书面语言乃至手势等非语言符号。作为一种沟通工具,语言的核心功能在于实现信息交换与意义共享。正是凭借语言,人类得以传递知识、协调行动、维系社会认同,并建立起日益复杂的社会协作体系。可以说,语言不仅塑造了我们的认知方式,也显著推动了人类文明的进步。
人与动物的交流,就很难通过口头表达或者书面语言进行了。因此,在长期相处中,人类与动物之间逐渐形成了某种基于声音、动作甚至默契的交互方式。我们无法确定猫、狗等动物是否真正“理解”人类的语言,但它们确实能够对人类指令做出反应。
而与机器的“交流”,则需要我们开发另一套专门的语言,计算机语言。准确地说,这种交流并非完全对等,更多体现为一种单向的指令传递过程。计算机语言的核心目标,是以机器可识别和执行的方式,表达人类的逻辑与意图。从某种意义上说,学习计算机语言,与训练动物理解指令确有相似之处:都是通过一套特定符号实现跨“物种”沟通。
计算机语言具有从底层到高级的多种形态。最基础的是由0和1构成的机器语言,直接对应计算机硬件的电路状态;其上是汇编语言,使用简短的助记符替代二进制指令;再往上则是高级语言,如 C、Java、Python、R 等,它们已具备类似人类语言的语法结构。越高级的语言,越接近自然语言的表达方式,抽象程度也越高。某种程度上,计算机语言就像一套具有严格语法规则的“咒语”,通过精准指令驱动机器完成既定任务。
从执行方式来看,计算机语言可分为编译型与解释型。编译型语言(如 C、C++、Go)需整体翻译为机器指令后执行,通常执行效率较高,但调试相对复杂;解释型语言(如 Python、R、JavaScript)则逐行翻译执行,灵活性更强,尤其适合快速开发和交互式应用。
那么,像 ChatGPT 这样的模型,究竟使用的是哪种语言?从本质上讲,它是在自然语言处理(NLP)技术基础上,通过海量文本训练所形成的、能够“理解”并生成人类自然语言的人工智能系统。尽管其交互表现已高度接近人类的日常沟通方式,但其底层机制仍然完全由程序代码所驱动:核心高性能计算部分由 C++ 实现,而整体架构的设计、训练流程的控制以及系统部署,则主要由 Python 主导完成,并融合了包括 CUDA 在内的多种技术语言协同构建。
需明确的是,模型本身并非语言,而是一种基于数学和统计的计算架构。当前,计算机语言与人类自然语言之间仍存在根本差异:前者强调精确性、无歧义与高度结构化,后者则充满灵活性、歧义性与语境依赖性。为此,计算机科学家、语言学家与统计学家正从两条路径推进:一是优化计算机语言本身,使其更贴近人类思维;二是通过更先进的模型与算法提升机器对模糊语义和复杂语境的处理能力,从而逐步弥合人机交互的鸿沟,构建更自然、更智能的沟通机制。
然而,对社会科学研究者而言,计算机语言本质上是一种与机器沟通的工具。它让机器“听懂”人类的指令。学习计算机语言不必视为畏途,它就像掌握一门新的外语,只不过要求更清晰、更具逻辑性,尽量减少模糊与语境依赖。不妨将其看作学习哈利·波特的咒语,或茅山道士的“急急如律令”,不仅毫不枯燥,反而是一项充满挑战且足够酷炫的技能。
正如维特根斯坦所言:“语言的界限就是世界的界限。”在数字化日益深入的今天,社会科学研究者有必要通过计算机语言,将社会科学扩展至数字世界。毕竟,数字空间,也已是我们人类社会不可分割的一部分,不是吗?
现在我们已经知道,计算机的物理基础是建立在二进制开关(0和1)的状态变化之上了。在早期编程阶段,程序员需要直接输入机器码来控制硬件,这一过程不仅繁琐,而且极易出错。随后,凯瑟琳·布斯(Kathleen Booth) 等先驱者设计并提出了汇编语言的概念,以应对直接编写机器码的挑战。作为机器码的符号化表示,汇编语言采用助记符(如MOV、ADD)替代二进制操作码,并引入符号标签取代直接内存地址,使编程效率有所提升。尽管如此,汇编语言仍要求程序员密切关注 CPU 寄存器、内存分配等底层细节,其核心思想是“直接控制硬件”。
到了 20 世纪 70 年代,丹尼斯·里奇(Dennis Ritchie) 设计了 C 语言。这标志着编程思想的一次重大飞跃。C 语言引入了变量、数据类型、函数、循环与条件控制流等高级编程概念,奠定了过程化编程范式的基础。在这一阶段,人们得以逐渐摆脱硬件细节的束缚,转而更专注于算法设计及数据结构组织。C 语言在提供高度抽象能力的同时,仍保持了接近汇编语言的执行效率,因此被广泛视为系统编程的基石。其核心思想也从“硬件控制”转变为“通过算法和数据结构来组织逻辑”。
随着软件系统复杂度的不断攀升,面向过程的语言实在过于冗余和难以维护了。面向对象编程应运而生。1985 年,比雅尼·斯特劳斯特鲁普(Bjarne Stroustrup) 在 C 语言的基础上引入“类”的概念,设计出 C++ 语言。该语言将数据(属性)与操作数据的方法(函数)捆绑为独立的“对象”,并通过封装、继承和多态三大核心特性,推动编程思维从“过程”转向“对象”之间的交互与关系。面向对象的编程旨在更自然地模拟现实世界,从而构建模块化、可复用且易于维护的大型软件系统。其核心思想是“对现实世界的抽象与关系建模”。
通用编程语言思想的发展,引导了整个计算机领域的发展方向。然而,在特定的专业领域,如统计学,学者们面临的挑战并非如何构建复杂的操作系统或计算软件,而是如何快速、灵活地探索和理解数据。通用语言的强大伴随着较高的学习成本和操作复杂度,这对于需要专注于数据本身而非编程技巧的统计学家而言,依然是一种负担。于是,自然而然,一种新的思想应运而生:为特定领域创造专属的、更高抽象层级的工具。
在这一背景下,约1976年,贝尔实验室的John Chambers等人创建了S语言。S语言的革命性在于其设计哲学的根本转变:它将“数据”视为首要关注。它提供了向量化操作、高级绘图功能和丰富的统计函数库,并构建了一个交互式解释环境。统计学家可以在此环境中与数据直接“对话”,进行快速的探索性数据分析(EDA),而无需陷入编译、链接等底层编程琐事。S语言的核心思想是“为数据分析和统计建模赋能”。
1993年,罗斯·伊哈卡(Ross Ihaka)和罗伯特·杰特曼(Robert Gentleman)开发了R语言,作为S语言的一种开源实现。R完全继承了S语言的数据中心主义和交互式环境哲学。但其开源特性,使得其更为成功。因为,全球无数的统计学家、数据科学家和领域专家,开始为其贡献扩展包(Package)。如果说R语言本身提供了一个功能强大的基础平台,那么其扩展包生态系统就如同一个无限扩展的“在线工具库”或“应用商店”。任何研究者开发出一种新的统计方法或数据可视化技术,都可以将其打包成一个扩展包,供全球同行直接下载使用。这使得R语言的能力不再受限于其原始设计团队,而是由整个社区共同驱动和更新。正是这种开放协作的“集市”模式,而非传统商业软件的“大教堂”模式,催生了如ggplot2、dplyr、tidyverse等极其强大的工具集,将S语言的强大能力彻底民主化,使其成为全球统计学教育与研究的标准语言。
作为统计领域的专用语言,R语言的核心解释器与基础函数库主要由C语言实现,并广泛集成Fortran数学库和高性能C++扩展功能(如Rcpp)。尽管底层实现多元,其整体设计始终围绕统计计算展开,呈现出多范式并用的面向对象特征。
R语言最初的面向对象实现——S3系统,充分体现了统计学家对灵活性的追求。这个轻量级的系统采用”基于泛型函数的调度”机制,对象本质上只是带有类属性的基础数据类型(如列表)。例如,当对一个线性回归模型(lm类对象)调用print()时,系统会自动寻找并执行print.lm()方法。该设计高度契合快速原型开发的需求,研究者无需预先定义复杂类型即可轻松创建新类,极大降低了使用门槛,使初学者也能迅速上手进行统计处理。然而,这种灵活性也伴随着类型模糊与方法签名缺失的问题,在构建大型项目时易引发维护困难。
为应对这一挑战,R语言引入了更为严谨的S4系统。与C++等语言的类系统类似,S4要求使用setClass()明确定义类的槽位(Slots)结构,并通过setMethod()为泛型函数建立严格的方法签名。这种转变反映了统计软件向工程化发展的趋势,特别是在Bioconductor等大型生物信息学项目中以及nlme等混合效应模型包中,S4的类型安全特性成为构建复杂系统的基石。但统计学家们很快发现,过度的形式化会牺牲R语言特有的交互式分析体验。这促使了后续的范式创新。
最新的R6系统则展现了R语言与主流面向对象编程理念的融合。它引入了引用语义、真正的封装和继承等特性,允许通过obj$method()的方式调用方法。这种设计特别适合需要维护复杂状态的场景,如Shiny应用的开发或者Rtorch分析大数据。值得注意的是,R6虽然借鉴了C++等语言的面向对象特性,但仍保留了R语言的数据中心主义——例如,即便是R6对象,也依然能与R丰富的统计函数无缝协作。
通过这种层层递进的系统演化——从灵活但松散的S3,到严谨却稍显繁琐的S4,再到融合现代OOP特性的R6——R语言在保持其交互式数据分析优势的同时,不断增强对大型项目的支撑能力。但用户却几乎察觉不到这些底层差异。其中关键在于R语言基于泛型函数的统一接口设计超越了对象系统的界限:无论是简单的S3对象、结构严谨的S4类,还是具有引用语义的R6对象,用户都通过同一套熟悉的泛型函数(如print(), plot(), summary())进行交互。例如,对一个S4生物信息对象或一个R6的Shiny会话对象调用print()时,系统会像处理S3对象一样,自动调度到对应的S4或R6方法上。这种高度一致且向后兼容的交互范式,使得用户无需关心对象背后的具体实现是哪种系统。无处不在的泛型函数将底层复杂性完全封装,让数据框、模型乃至自定义的高级对象都能如同自然语法般融入工作流程,使研究者能始终专注于统计任务本身,而非编程细节。只有在开发生态系统的特定领域(如Bioconductor或Shiny应用)或作为包开发者时,才需要主动区分并选择不同的对象系统。这种”进化而不割裂”的设计哲学,使R可以在不断提升工程化水平的同时,始终保持着对统计思维习惯和交互式分析体验的深度契合。
进行统计分析时,我们有多种工具可以选择,例如SPSS、SAS、MATLAB和Python等。尽管如此,我还是强烈推荐R语言。
与SPSS和SAS这类传统商业软件相比,R最显著的优势在于其开源和免费的特性。SPSS以其图形化界面和菜单操作著称,适合不擅长编程的用户完成基础统计分析,但其封闭的生态系统和昂贵的授权费用严重限制了用户的自定义和扩展能力。SAS在企业级应用尤其是医药和金融行业中历史悠久、性能稳定,但其语法体系较为陈旧,学习门槛较高,并且同样需要支付高额许可费用。相比之下,R不仅完全免费,更依托开放源代码和强大的包管理机制,为用户提供了极大的灵活性和控制权。用户不仅可以自由使用、修改和共享代码,还能通过CRAN、Bioconductor等平台轻松获取涵盖经典统计、机器学习、生物信息学乃至金融工程等方向的数万个专用程序包,几乎覆盖了所有现代统计应用场景。
与MATLAB相比,R更加专注于统计分析和数据可视化。MATLAB在数值计算和矩阵运算方面性能出色,但其作为商业软件存在许可限制,且在统计建模的专业深度和方法广度上不及R。R由统计学家开发、为统计研究服务,其数据结构和语法设计——如数据框(data.frame)和因子(factor)——更符合统计学家的思维方式。同时,以ggplot2、dplyr为代表的tidyverse系列工具包重塑了数据操作与可视化的标准,使得复杂数据处理和高级绘图变得简单而直观。另外,考虑到某些情况下MathWorks公司曾停止向中国部分高校提供MATLAB正版授权,使用R这类自由开源软件也显示出其在可持续性与安全性方面的额外优势。
即使是与当前广受欢迎的Python相比,R在统计建模和学术研究方面仍保持着独特地位。Python作为一种通用编程语言,在机器学习、人工智能和自动化任务中表现强大,但其统计生态系统相对分散,很多传统统计方法的实现不如R完善和权威。R由统计学社区推动发展,绝大多数新兴统计方法会首先在R中实现,许多经典和现代的统计学教材也优先选用R作为教学语言。此外,R Markdown能够与学术协作流程紧密结合,支持动态生成可重复的研究文档,极大便利了学术写作与成果交流——本书正是基于R Markdown编写而成。
尽管R语言在学习初期可能对某些用户,尤其是社会科学背景的学习者,具有一定挑战性,但其开源免费、资源包丰富、统计方法全面先进、可视化能力强大,以及与统计思维深度契合的特点,终将使你的学习投入获得长远回报。
如果你已经熟悉C,那学习R语言就是降维打击!比起那些需要手动管理内存、时刻操心指针和类型声明的底层语言,R简直太简单了。我们只需要稍微调整一下思维方式,就能轻松上手。
首先,忘掉那些繁琐的变量声明。在C里你得先声明int i; double pi;,但在R里,直接i <- 10、pi <- 3.14就行,类型什么的R会自动帮你搞定。
其次,是掌握向量化思维——这是R的超能力。比如说要求一组数的平方,我们不用再写循了。
例2-1:
对比一下这两种写法的区别:
C风格(C的掌控):
x <- c(1, 2, 3, 4, 5) result <- numeric(length(x)) # 先分配内存,这很C语言 for (i in seq_along(x)) { result[i] <- x[i] ^ 2 # 逐个元素处理 } print(result) # [1] 1 4 9 16 25
R风格(R的优雅):
x <- c(1, 2, 3, 4, 5) result <- x ^ 2 # 直接对整个向量操作,一行搞定! print(result) # [1] 1 4 9 16 25
R的向量化操作不仅写起来简单,底层还是用C/Fortran优化的,速度和C语言一样快。如果非用R语言中的for循环,在某些情况下真的会慢到怀疑人生!
再次,我们的工作重心也应从”如何实现算法”转向”如何应用算法”。遇到某些问题,我们的第一反应应该是什么包和什么函数可以解决,而不是从基本算法开始编起。只有在万不得已的时候,我们才会自己写一些特殊处理函数。但为了提高性能,可以通过Rcpp包编写C++扩展来提升效率。
内存管理更是轻松,再也不用malloc和free了,垃圾回收全自动。不过要注意R的”修改时复制”机制,操作大对象时可能会复制一份,这时候性能会受影响。还有几个小细节:索引是从1开始不是0;没有指针概念,函数参数都是值传递等等。
说了这么多,其实总结起来就是:R的所有特征都让编程更简单了。如果你会C或Fortran,学R就像老司机开新车——基本操作都会,只是换了个更舒服的驾驶方式。
如果已经熟悉python了,那么你可以直接下载Rstudio进行R语言编程了。两者除了设计目标和基本语法,几乎没有什么大的不同。Python是一种通用编程语言,通过 pandas, numpy, matplotlib 等库来支持数据科学。而R语言作为一门为统计分析和可视化而生的语言,其核心设计就围绕着数据框和统计模型。在了解基础语法的不同之后,两者都是应用不同的包进行数据分析处理。我们了解不同的包,就可以很好地跨越编程语言。
例2-2:
比如,对比一下python和R中两个重要的包:pandas和tidyverse
Python/pandas采用面向对象的链式调用方式:
(df[df[‘mpg’] > 20] .groupby(‘cyl’)[‘hp’] .mean())
R/dplyr使用函数式编程结合管道操作符:
df %>% filter(mpg > 20) %>% group_by(cyl) %>% summarise(mean_hp = mean(hp))
这种语法差异反映了两种语言不同的设计哲学:Python强调明确性和控制力,而R注重代码的可读性和表达力。类似的对应关系也存在于可视化领域,matplotlib/seaborn与ggplot2虽然语法不同,但都能实现类似的可视化效果。
此外,还需要稍微注意一下python和C语言一样,索引从 0 开始,而R的索引从 1 开始。R的数据框取子集方式df[[1]] 或 df$column_name也非常不同。在包管理方面,你会用pip 和 conda管理python的包,就会用install.packages() 和 library()管理R的包。还有很多包,python和R都提供的。比如做机器学的经典包PyTorch,在R里面就有对应的rTorch。语法和用法几乎一样。
现代数据科学工作流经常需要同时使用Python和R。RStudio提供了完善的Python集成支持,允许在同一个项目中无缝使用两种语言。reticulate包更是提供了双向互操作能力,使得在R中调用Python代码变得异常简单。总的来说,对于Python用户而言,学习R语言更像是扩展工具包而不是学习全新技能。许多概念和模式都是相通的,只需要适应语法上的差异。
如果你此前从未接触过任何编程语言,那也很好。这意味着一张纯净的“白板”,你可以在上面清晰地刻画出R语言的思维脉络,而无需受其他语言习惯的干扰。学习R语言,就是学习一种与数据对话的新方式。最理想的情况,是你能找一本经典的R语言入门书籍(例如《R语言实战》或《R数据科学》),配上一杯咖啡,系统地跟着操作一番。但如果时间紧迫,希望快速上手以跟上本书的节奏,那么请听从以下的建议,然后信任这本书的旅程——我们将统计学的每一个核心知识点,都与R语言的具体实现紧密捆绑。你将在解决实际统计问题的过程中,自然而然地掌握编程工具。
这里,我们先了解一些最基本的。编程,其实没有想象那么复杂,本质上就是让你以一种精确的、计算机能理解的方式,对它下达一系列指令。而R语言,就是一套专门为数据分析和统计计算设计的指令集:让计算机帮你处理数据、计算统计量、并生成可视化图表,从而洞察数据背后的故事。
首先从安装开始:
首次打开RStudio,你会看到类似下图的四个面板区域。不必一次性记住所有功能,先认识它们,我们在后续章节中会反复使用。
接下来,为了让你对即将学习的内容有个印象,这里先介绍几个最核心的名词。不用担心,我们很快就会在具体的统计学问题中用到它们。
变量 (Variable):信息的容器。你可以把一个数字、一段文字、一个结果装进一个叫“变量”的盒子里,并给它贴上一个标签(变量名),方便后续使用。
my_height <- 175 # 创建一个名为my_height的变量,里面存放了数字175 my_name <- “Locke” # 创建一个名为my_name的变量,里面存放了文本”Locke”
函数 (Function):预置的“工具”或“食谱”。它是一个接收输入(参数)、执行特定任务、并返回输出结果的黑盒子。R语言拥有极其丰富的函数来完成各种统计和绘图任务。
mean(c(1, 3, 5, 7)) # 使用mean()这个“求平均值”工具,对输入的数据(1,3,5,7)进行处理,输出结果为:4
数据框 (Data Frame):最重要的数据结构,它就像一张Excel表格。每一列是一个变量(如“年龄”、“成绩”),每一行是一条观测记录(如一个学生的所有信息)。我们后续几乎所有操作都将围绕它展开。
包 (Package):功能的“扩展包”。 base R(基础R)已经很强大了,但全世界的研究者和程序员开发了上万个“包”来扩展它的功能,实现更专业、更强大的分析。安装 (install.packages(“包名”)) 和加载 (library(包名)) 包是常态。
现在,你已经知道了“驾驶舱”的基本布局和几个主要“仪表”的名称。从下一章开始,我们将正式启航。每当我们需要计算一个统计量,就会学习相应的函数;需要绘制一张图,就会调用相关的绘图包;需要整理数据,就会使用高效的数据操作工具。
我们在学习统计学的过程中,自然而然地学会R语言。