Python并非数据科学领域的理想语言(第二部分):语言特性

它或许是数据科学领域不错的选择,但远非最佳。

本文是我关于 Python 作为数据科学语言局限性的系列文章之二。您可在此处查阅第一部分。若尚未阅读,请先阅读该文以获取重要背景信息。

我通常认为讨论不同编程语言对特定任务的适用性是件乏味的事。我们使用的所有语言都是图灵完备的,任何语言都能解决任何问题。更重要的是,语言对特定任务的适用性往往更多取决于可用的软件库和生态系统基础设施,而非语言本身。现代编程语言具有很强的可塑性,几乎任何语言都能为几乎任何计算任务编写高效优雅的库。

但语言间确实存在本质差异,这些差异常体现在编写的库类型或普遍采用的编程模式上。差异可能源于语言特性,也可能根植于社区对编程的认知及处理特定任务的倾向。

元素周期表

以非标准求值为例:Python不支持该特性,这成为语言的实质性局限,导致pandas或Polars等库的编程接口变得复杂。反观闭包特性,Python虽支持但开发者使用率不高。Python社区通常倾向于实现对象而非闭包,而R社区则相反。这导致了不同的编码风格,在特定场景中可能具有优势,也可能不然。1

本文将聚焦语言本身的实际局限性,社区惯例将在后续文章探讨。我认为Python作为数据科学语言的核心问题在于:按引用传递语义、缺乏内置缺失值处理机制、缺少内置向量化支持,以及缺乏非标准求值功能。Python语法问题虽存在,本文暂不深入探讨。仅需说明:设计出存在空格错误的语言,其开发者对人类同类的同理心显然有所欠缺。

引用传递语义

Python对可变对象采用按引用传递机制。这意味着当你将可变对象传递给函数时,函数可随意修改该对象。你永远无法确保函数调用后对象保持原状。何为可变对象?即所有用于存储数据的非简单数据结构,包括列表、字典及任何自定义类。

为演示此特性,请看以下代码示例:该函数接收字符列表,将首尾字符替换为下划线后拼接成字符串。对初学Python的程序员而言,此实现看似合理,但存在意外副作用——它会修改原始输入列表。

def mask_ends_and_join(x):
    x[0] = '_'
    x[-1] = '_'
    return ''.join(x)

abc = ['A', 'B', 'C']
print(mask_ends_and_join(abc))
## _B_

print(abc) # the list has unexpectedly changed
## ['_', 'B', '_']

为证明动态类型的交互式脚本语言不必如此行事,请看 R 语言的等效实现:

mask_ends_and_join <- function(x) {
  x[1] <- '_'
  x[length(x)] <- '_'
  paste0(x, collapse = '')
}

abc <- c('A', 'B', 'C')
print(mask_ends_and_join(abc))
## [1] "_B_"

print(abc) # the original vector of letters is unchanged
## [1] "A" "B" "C"

我认为后者安全得多。我希望编程语言能保护我免于犯下诸如意外修改调用环境变量这类低级错误,而非在语言中四处设置陷阱。事实上,我认为引用传递是Python语言最大的缺陷之一。这远不止影响数据科学领域——强制引用传递会催生大量难以定位和修复的隐蔽错误。许多初学Python的程序员都曾陷入此陷阱:他们编写类似mask_ends_and_join()的函数,遭遇意外副作用后陷入困惑,觉得一切都毫无逻辑。经验丰富的Python程序员知道在修改列表前应先创建副本,但语言本身对程序员忘记此操作毫无防护机制。2

在我看来,仅凭这一语言特性就足以让Python丧失承接大多数严肃编程项目的资格。面对如此巨大的安全漏洞,你如何能用这种语言构建出任何重要的事物?事实上,你或许会质疑:这种语言行为模式究竟从何而来?我认为这是过早优化的产物。在1990年代Python诞生之初,计算机运行缓慢且内存有限,因此采用对象引用传递机制来构建高性能脚本语言是合理策略。但到了2025年,我绝不愿看到这种机制仍作为函数调用的默认方式。R语言采用写时复制机制,不仅运行高效,更能提供Python无法企及的正确性保障。3 另一种选择是采用严格区分可变与不可变引用的强类型语言,但这可能意味着放弃适用于交互式数据探索的易用脚本语言领域。

内置缺失值机制的缺失

缺失值是数据科学领域普遍存在的现象。几乎所有数据集都存在缺失值,但 Python 处理缺失值的方式却出人意料地繁琐。虽然 Python 提供了 None 关键字,但它无法有效表示缺失数据值。这是因为None本身具有独立类型,无法表示缺失的数字、布尔值或字符串。它本质上是表示缺失值的对象。关键问题在于,你无法对None进行标准运算。例如以下代码会抛出错误:

x = [1, 2, None, 4, 5]
[i > 3 for i in x]
## Traceback (most recent call last):
##   File "<stdin>", line 1, in <module>
## TypeError: '>' not supported between instances of 'NoneType' and 'int'

我认为理想的行为应该是:不抛出错误,而是输出结果 [False, False, None, True, True]

由于Python没有统一的缺失值表示方式,每个数据分析库都定义了自己的缺失值表示:NumPy使用nan,pandas使用NA,Polars使用null。这些库在处理缺失值的计算逻辑上也存在差异。NumPy的做法如下:

import numpy as np
 
x = np.array([1, 2, np.nan, 4, 5])
x > 3
## array([False, False, False,  True,  True])

而pandas的做法是:

import pandas as pd

x = pd.Series([1, 2, pd.NA, 4, 5])
x > 3
## 0    False
## 1    False
## 2    False
## 3     True
## 4     True
## dtype: bool

而 Polars 的处理方式如下:4

import polars as pl
 
x = pl.Series([1, 2, None, 4, 5])
x > 3
## shape: (5,)
## Series: '' [bool]
## [
## 	false
## 	false
## 	null
## 	true
## 	true
## ]

在我看来,这三种情况下只有 Polars 正确处理了缺失值。缺失值应当污染下游计算,避免用户误用缺失数据导致错误结果。NumPy 和 pandas 均未做到这一点。但对 Polars 也别抱太大期望——它同样无法始终如一地污染缺失值计算。例如在计算求和或求均值时,它会直接忽略缺失值且无法修改此行为。5

内置向量化功能缺失

向量化是指对整个数据数组进行批量计算的能力,而非逐个处理数据值。这是早期科学计算语言(如Fortran或Matlab)的常见特性,也是R语言处理数据的默认方式。

如今向量化常被视为过时的技术。鲜有现代语言能在语言层面直接支持该特性,Julia是例外——这种专为数据科学设计的年轻语言实现了此功能。颇具讽刺意味的是,整个深度学习领域都建立在向量化基础上(张量正是向量化数据类型的现代版本)。

现代语言中向量化常被视为非关键特性,因其可通过库实现——借助所有现代语言具备的扩展机制。事实上,Python的向量化正是通过NumPy、pandas或Polars等库实现。虽然可行,但我逐渐认为这并非数据科学语言的良策,因其易导致向量化数据类型的实现方式纷繁复杂。在Python中,我们至少拥有原生列表(未向量化)、NumPy数组、pandas数据系列和Polars数据系列——这些均实现向量化,却各自采用略有差异的规范和API。其结果是代码缺乏可组合性:下游库需预先假定使用何种向量化框架,通常无法直接处理来自其他框架的数据。6 作为数据科学工作者,你经常需要将一种数据类型转换为另一种,只为实现你想要的精确分析。

即使你大量使用了向量化库,也极有可能同时使用Python内置列表——因为总会有某些函数需要常规列表作为输入,或将其作为返回值。这时你不得不处理这些列表:可以将其转换为NumPy数组进行向量化操作后再转回,但实际中你大概率不会这么做。相反,你会选择编写列表推导式。于是你不得不同时使用两种截然不同的编码风格,取决于存储向量化数据的数据类型。

让我们进一步思考列表推导式。它们本质上属于函数式编程范式,但Python的实现方式却使其呈现出命令式编程的特征。通过使用for关键字并强调对值域的迭代,它们不断引导你以迭代思维进行思考——尽管从概念上讲,列表推导式更接近map()而非for循环。需要明确的是,我对列表推导式本身并无异议。这确实是个实用特性,尤其在操作Python内置列表(这些列表不支持向量化)时。但这恰恰是Python不断引导你关注数据分析操作细节的又一例证。当你整天编写列表推导式时,很可能在代码其他部分仍会使用for循环,结果又陷入索引操作和显式处理细节的困境,而非专注于思考代码中数据流的高级逻辑。

非标准评估机制的缺失

非标准评估可能是Python在数据科学领域最关键的缺失特性。这是R语言的核心功能,正是它让tidyverse代码如此优雅精炼,也成就了R语言为统计模型构建的优雅公式化接口。

何为非标准评估?简言之,即在语言本身进行计算的能力。R函数可捕获作为参数提供的R代码,并在后续不同环境中执行。这在数据分析中至关重要——你常需对数据框的多个列进行计算,或通过代码精确表达统计模型中不同变量间的关联关系。在R中,这些计算可通过原生R代码实现——例如将数据框列视为当前环境中可供计算的常规R变量。结合向量化特性,这种编程方式能生成极其精炼的代码。

为演示非标准评估的应用,我将使用penguins数据集提供一个简单示例。我们计算新变量bill_ratio(即企鹅喙长与喙宽之比),然后按岛屿名称升序排列数据框,并按喙比降序排序。在R中的实现如下:

library(tidyverse)
library(palmerpenguins)

penguins |> 
  mutate(bill_ratio = bill_length_mm / bill_depth_mm) |>
  arrange(island, desc(bill_ratio))

此处有两处体现非标准评估:首先在mutate()内部,喙长宽比的计算采用标准R代码,在输入数据框内执行,数据列可作为普通R变量使用;其次在arrange()中,我们使用desc()将升序列转换为降序列。desc()函数虽略显神秘,但对于数值型列,可将其理解为将数据值乘以-1。

Python中进行相同分析时,由于缺乏非标准评估机制,我们不得不采用多种变通方案。pandas包依赖lambda函数实现:

import pandas as pd
from palmerpenguins import load_penguins

penguins = load_penguins()

(penguins
 .assign(
     bill_ratio=lambda df: df[’bill_length_mm’] / df[’bill_depth_mm’]
 )
 .sort_values(
     [’island’, ‘bill_ratio’],
     ascending=[True, False]
 )
)

显然,非标准求值极大提升了代码的简洁性和可读性。7 现在我们再进一步。假设我想按账单长度的余弦值排序。是的,这是个虚构的例子,但它恰恰符合我在本系列第一部分中描述的、可能向学生提出的命题类型:用余弦排序替代降序排序。这能有多难?

借助非标准求值机制,所需修改既简单又显而易见:只需将desc()替换为cos()即可。搞定。

penguins |> 
  mutate(bill_ratio = bill_length_mm / bill_depth_mm) |>
  arrange(island, cos(bill_ratio))

在Python中(具体指我使用的pandas库,但多数框架都需要类似的笨拙编码模式),若不采用非标准求值,我必须创建临时列——因为pandas无法对bill_ratio列即时应用余弦函数:

import numpy as np

(penguins
 .assign(
     bill_ratio=lambda df: df['bill_length_mm'] / df['bill_depth_mm'],
     cos_bill_ratio=lambda df: np.cos(df['bill_ratio'])
 )
 .sort_values(['island', 'cos_bill_ratio'])
 .drop(columns=['cos_bill_ratio']) # drop temporary column
)

要完成如此简单的任务,所需额外编写的代码量相当可观。现在我们需要定义两个lambda函数和一个临时数据列。此外,我们不再需要ascending参数,因为虽然内置支持升序或降序排序,但并不支持余弦排序。

公平地说,pandas的语法在此处或许显得格外繁琐,其他框架的实现可能更简洁。但非标准评估机制的缺失总会以某种形式造成困扰。例如在Polars中实现相同功能的代码更为精炼且无需临时列,但Polars代码中频繁出现的pl.col()调用很快就会令人厌倦。

import polars as pl

penguins = pl.from_pandas(load_penguins())

(penguins
 .with_columns(
     bill_ratio=(pl.col('bill_length_mm') / pl.col('bill_depth_mm'))
 )
 .sort(['island', pl.col('bill_ratio').cos()])
)

非标准求值自R语言诞生起便是其特性,但在tidyverse中得到了极大强化。我认为如何正确运用该特性——在保持最大表达力的同时避免冗余代码——是相对较新的发展成果。重要变革直至2019年6月才正式推出。考虑到ggplot2的首个版本发布于2007年,可见哈德利·威克汉姆及其团队耗费十余年才真正掌握非标准评估的精髓。这些概念尚未在原生语言之外广泛普及,或许不足为奇。

R语言的局限性

为避免被指责为R语言辩护者或Python贬低者,我将简要指出R语言存在的具体缺陷。在我看来,这些缺陷阻碍了R作为通用应用开发语言的发展,但对数据科学领域影响较小。

最令我困扰的是R语言缺乏标量数据类型。R将向量化推向极致,以至于无法定义非向量变量。这使得处理单个数据值时编程极为笨拙——R代码常需进行特殊操作,才能避免将整组向量值误传入仅接受单值的表达式。

同样令人困扰的是R缺乏原生的面向对象编程范式。结果导致开发者常自行构建解决方案,导致众多方案相互竞争。仅凭记忆就能列举出S3、S4、R6、S7等主流方案,还有其他使用较少的方案。选择时往往令人困惑,且这些方案之间未必具备完美的互操作性。

最后,R采用函数参数的延迟求值机制。这意味着函数调用时不会立即评估参数,而是在函数需要特定参数值时才进行求值。延迟求值对R的非标准评估框架至关重要,但可能引发奇怪的错误,尤其当用户试图以命令式而非函数式方式使用R时。这常成为ggplot2中虚假错误报告的根源,例如参见此处此处 该问题在StackOverflow.上也频繁被提及。

我指出R语言的这些局限性,旨在强调任何设计决策都涉及权衡取舍。非标准求值对数据科学非常有用,但它需要惰性求值,而这对于主要以命令式方式使用和/或用于标准编程任务(如应用程序开发)的语言来说并不是一个好的选择。永远不会有一种语言能同样出色地完成所有可能的事情。回到本系列文章的标题,就个人偏好而言,Python中存在太多损害高效可靠数据科学的设计选择——即便这些选择在其他应用领域完全合理。

下期将探讨Python因现有软件包、社区惯例及常用编程模式所导致的局限性。敬请期待。

1 我并非在此主张闭包优于对象。事实并非如此。二者各有其用。我只想强调Python中存在但尚未被广泛运用的语言特性。

2 而在函数体内使用方法操作对象时,问题会变得更严重。因为每次调用对象的方法时,都存在该方法在不知不觉中悄然修改对象的风险。这种修改可能以完全不明显的方式发生,例如方法改变了某些仅在极少数情况下才重要的内部状态。关键在于,当你调用对象的方法时,永远无法确定对象的状态是否发生了改变。

3 我确信有人会提出写时复制的性能问题。对此我只想说:请参阅我在第一部分中关于性能的论述。若性能对你的应用至关重要,使用Rust或许是更优选择。此外,我很难想象存在既要求性能又无视结果正确性的场景。

4 注意 Polars 相较于 NumPy 或 pandas 的一个奇怪特性:无法使用 Polars 的 null 类型初始化包含缺失值的序列,必须手动写入 None

5 我明白这是SQL的行为方式,但这并不意味着它是正确的选择。静默忽略缺失值几乎必然导致某处数据科学家因未察觉数据存在缺失而得出错误结论。

6 例如绘图库plotnine无法直接绘制Polars数据框,必须先转换为pandas格式。

7 顺带一提,我们不妨思考一下Python为何需要用括号包裹来美化数据操作链的格式?我长期以来都觉得Python的代码格式要求相当令人沮丧,这又是一个例证。

本文文字及图片出自 Python is not a great language for data science. Part 2: Language features

共有 302 条讨论

  1. > 例如将箱线图转换为小提琴图或反之,将折线图转换为热力图,绘制密度估计图而非直方图,对排序数据值而非原始数据值进行计算等等。

    这些大多与Python无关,而是matplotlib的问题。若想在Python中获得ggplot那种设计精妙的体验,请使用plotnine

    > 我认为R代码更易于阅读(注意Python代码需要多少引号和括号)

    这与Python无关,而是tidyverse的问题。R能使用更简洁语法的原因在于其非标准评估机制,允许包扩展语法——这是Python未开放的功能:http://adv-r.had.co.nz/Computing-on-the-language.html

    1. “能在R中使用这种简化语法,是因为其非标准评估机制…”

      所以本质上还是Python与R的对比。

      不过这种非标准评估机制虽在命令行交互时很便利,但用于编写复杂分析代码时我认为意义不大。在这种场景下,我反而认为这是R的缺陷——你不得不绕弯子才能让非标准评估处理简单操作。

      1. R包中非标准评估日益泛滥,正是我工作时从R转投Python的主要原因。光是让某个参数在函数中生效,就得经历繁琐流程和持续的API变更,简直令人抓狂。

        1. > 更别提API频繁变更

          确实痛苦至极。我曾维护过一个库,它几乎用遍了所有NSE实现方式,简直糟透了。

    2. >> 我认为R代码稍易阅读(注意Python代码需要多少引号和括号)

      天啊别这样,有人会写成那样吗?末尾还加管道符?正确写法是像Elixir那样把管道运算符放在开头。

      若真想通过混淆参数/函数/变量来“提升”可读性(只为省略引号),Python也能做到——只需用包装对象配合getattr技巧,就能实现my_magic_strings.foo‘foo’。至于括号…好吧这确实是合理改进,但同样与语言无关,属于函数签名的库API设计范畴。

      1. 正确做法是将管道运算符置于表达式开头。

          (-> (gather-some-data)
            (map 'Vector #'some-functor)
            (filter #'some-predicate)
            (reduce #'some-gatherer))
        

        或者,对于那些对括号有非理性恐惧的人:

          ->
            gather-some-data
            map 'Vector #'some-functor
            filter #'some-predicate
            reduce #'some-gatherer
        
      2. 据我所记,将管道运算符 `|>` 置于行尾可防止表达式过早终止。否则换行符会导致其终止。

    3. 好奇R语言里“不依赖库的逻辑回归”最后会是什么样。根据我做“底层R”的经验,那绝对是场噩梦。

      在R中,有现成库和方案的事物往往简单,但一旦缺乏这些支持,事情就会变得极其困难。而通常的做法是:如果库方案无法轻松实现,那这事就干脆不做。

      1. Python:简单的事简单,困难的事困难。

        R:简单的事困难,困难的事简单。

      2. 按你描述的情况,能否说R在不知不觉中实现了AI优先?

        1. R明显深受Lisp影响,而Lisp曾是AI领域的重要技术。他们深谙其道。

    4. 重点不在Python,而在于R能实现Python无法做到的事?

        1. 可为什么每次我尝试使用它时,它总像踢我电池那样伤害我?

      1. Go语言的设计与成功有力证明:语言与其工具包生态系统不可分割,也不应割裂。

        1. Python的成功恰恰在于它在许多场景下无需依赖更广泛的生态系统。

          当然,他们现在正在放弃这个理念。

          1. Python的成功恰恰在于它在许多场景下无需依赖更广泛的生态系统。

            老实说我觉得那纯属巧合。Perl和Ruby还有其他缺点,Python能胜出并非因为糟糕的包管理和臃肿的标准库,反而要归功于这些缺陷。

            1. 臃肿的标准库是我忍受包管理噩梦仍坚持使用Python的唯一原因。我能不依赖任何库完成大部分工作,或者仅依赖像matplotlib这样反复使用的单一库。

              倘若Python原本精简却需依赖包才能实现实用功能,同时仍存在包管理噩梦,那它根本无法使用

              1. 确实如此,但我同样认为,若当时包管理问题更紧迫,人们会投入更多精力解决它。

                1. 实际已有巨大努力,多种包管理器的涌现便是明证。

                  1. 或许吧。但其中许多项目感觉像是无人问津的个人工程。我认为恰恰相反,众多包管理器并存却无明显赢家,部分原因正是这个问题对多数社区成员而言并不严重。

            2. 正是标准库的臃肿特性,才使得单个.py文件能被即时传递执行。

              绝大多数Python用户既不了解也不需要venv、uv、pip等工具。

            3. 因为Ruby占领了网络市场,Python则统治了其他领域——我认为全面布局比单点突破更具长远价值。

              1. Ruby在网络市场曾与Python等多方竞争但落败。部分原因在于Python生态更庞大,PHP通过WordPress等平台广泛普及,而JavaScript正从浏览器领域扩张。

      2. 电池组是用什么语言编写的?

            1. 如今依然大量使用Fortran,只是表面撒了点Rust调味。(:

              1. 毕竟自Fortran 2003甚至Fortran 95起,这语言就变得相当好用。

                1. 我个人觉得它变得过于冗长了,现在看起来几乎像COBOL了。(我认为Fortran 66才是最后一个忠于其“公式翻译器”本质的Fortran版本…)

                  1. 现在我们早就超越了把语言和COBOL相提并论的阶段了——毕竟现在很多人都在小小的聊天窗口里为他们的AI超载输入整本书大小的描述。

      3. Python圈的人总爱说这话——简直像按字数拿报酬似的。它和Perl、Ruby、Java或C#(DotNet)有本质区别吗?依我经验没有,只不过那些社区的人没这么爱重复这套说辞。

        讽刺之处在于:我们讨论的是数据科学。98%的Python数据科学项目起步时,都要创建虚拟环境并安装Pandas和NumPy——而这些库在基础库之外还依赖着无数(真的:数以亿计)的外部依赖项。

        1. 若我完全错误请指正:默认情况下(即预编译轮子),numpy依赖为0,pandas依赖5个(其中之一正是numpy)。所以“数以亿计”的依赖说法并不准确。

          pandas==2.3.3

          ├── numpy [所需版本: >=1.22.4, 已安装: 2.2.6]

          ├── python-dateutil [要求:≥2.8.2,已安装:2.9.0.post0]

          │ └── six [要求:≥1.5,已安装:1.17.0]

          ├── pytz [要求:≥2020.1,已安装:2025.2]

          └── tzdata [要求版本:≥2022.7,已安装版本:2025.2]

          1. 我不清楚_squillions_的情况,但numpy绝对有_requirements_,即使在python图中未明确标注。

            例如:

              https://github.com/numpy/numpy/blob/main/.gitmodules(部分源代码依赖)
              https://github.com/numpy/numpy/tree/main/requirements(主要为构建/CI/...依赖)
              ...
            
            1. 这些依赖未被显示,因为它们属于构建时依赖。大多数用户执行 pip install numpy 或类似操作时,仅获取预编译二进制文件,这些依赖均不会被安装。即使自行编译,运行 numpy 时也无需这些依赖。

          2. 请参阅https://numpy.org/devdocs/building/blas_lapack.html

            若系统未安装更优实现,NumPy 必然 会回退至内部实现的极其低效的BLAS/LAPACK。但既然您使用NumPy是为了性能而非单纯为Python增添数组编程功能,自然需要更高效的实现方案——而具体选择很大程度上取决于您使用的计算机配置。

            这其实并非Python特有的问题。任何科学计算领域都面临着这个棘手的难题。若你坚持使用动态解释型语言(探索性交互分析可能不得不如此),同时又需要处理大规模数据集时的高速运算,那么就必须借助原生FFI并链接原生库。得益于标准化,你将拥有多种选择,而具体哪种方案最快则很大程度上取决于你的硬件配置。

            1. 轮子通常会自带openblas,因此虽然能获取原始blas(相较之下确实较慢,但小任务中用户可能难以察觉),这通常不成问题。

    5. > 这与Python无关,而是tidyverse的问题。

      > 其非标准评估机制允许扩展语法,这是Python未开放的功能

      这正是Python与R的根本差异。

      1. 关键在于:R的语法扩展能力虽常导致混乱(普遍现象),但在tidyverse中若运用得当,反而能提升编写与阅读代码的体验。

    6. R更偏向统计软件而非编程语言。所以如果你是所谓的“统计学家”,R会让你倍感亲切

      1. 不,R是严肃的通用编程语言,能构建几乎任何类型的复杂科学软件。Bioconductor这类项目就是明证。

        1. 并非如此。即便是成熟的软件包也存在因R语言特性导致的漏洞。尽管如此我依然喜欢它。

          1. 是的,R是真正的通用编程语言。它具备图灵完备性,支持函数式、过程式、面向对象等多种编程范式…

            1. 谨防有人看到blubber那句斩钉截铁的“不”。blubber此处绝对错误。我曾用R完成所有编程工作。若想知道R是否存在类似Python中___的包,不妨将问题投进大型语言模型。

        2. 或许可以对比Python的场景?

          就我有限的经验而言,使用R的感觉就像在浏览器中使用JavaScript:它是一个高度聚焦于高级、功能丰富的对象(如数据框和专用绘图对象)的平台,但你几乎也能用它构建任何东西。

  2. 我认为这本质上指向一个核心问题:为何表格不能成为编程语言的一等公民?

    退一步看,主流编程语言中竟无表格作为一等公民的存在,这本身就很奇怪。结果我们被迫学习多种API(如polars、pandas),这些API本质上是为表格设计的编程语言。

    R语言或许最接近这个目标,因为它将data.frame作为“第一类公民”,但多数人似乎并未采用,反而更倾向于使用dplyr中的tibbles等替代方案。

    根本原因似乎在于我们尚未找到操纵表格数据的最佳语言(即表达方式)。目前某些通用理念似乎已趋于共识。Polars与dplyr有相似之处。但除SQL外尚无统一标准。

    顺便说一句,我认同Python表现欠佳,但同样认为R也存在不足。不过我不赞同文中具体比较的结论。

    1. 我认为主流编程语言存在若干结构缺失。表格结构是其一,矩阵是其二。图结构及其相关的状态机因语言层面的支持不足而严重被低估。最后虽非严格意义上的结构,但我认为任何内置正则引擎的语言都应配备完整的PEG解析引擎——绝大多数正则表达式灾难的根源,恰恰在于“内置正则引擎”的简单设计。

      语言默认提供的工具,决定了其优雅的路径,进而塑造了整个语言的体验。我们已普遍接受的范例是键值存储——如今它们已成为标准库的必备功能。回溯90年代,当时最流行的语言 至多 将键值存储视为二等公民,其地位更接近导入对象而非数组这类基础结构。当然,任何语言都能实现哈希映射,或导入他人实现,但开发者往往会陷入噩梦般的同步数组困境——只因后者作为内置功能触手可及。

      1. 当某种特性缺乏明确的规范实现方案时,将其纳入编程语言(或标准库)便充满风险。人们往往在为时已晚时才意识到选择错误,继而添加第二个版本。接着是第三个。如此循环往复。最终造就的是一门充斥新手陷阱的混乱语言。

        图是一个很好的例子,因为它们是一大类相关的结构。例如,边是无向的、有向的,还是更奇特的类型?节点/边是否有标识符和/或标签?所有节点/边是否同类,还是存在多种类型?相同节点之间能否存在重复边?这取决于节点/边的类型,还是取决于标签?

        1. 即便是图的原始存储方式也并非唯一解:既可采用边列表存储,也可使用邻接矩阵存储。某些算法更适合前者,某些则更适合后者。通常不建议同时存储两种形式,因为这不仅会增加额外内存开销,若需同时原子更新两种表示还会引发锁定问题。你可能不希望自动在两种表示形式间切换,因为这可能引发垃圾回收器频繁活动(若配合广度优先或深度优先搜索则更甚),同时也不应鼓励用户手动转换数据结构(以免为用户埋下性能陷阱)。因此,尽管边表图和邻接矩阵图之间存在简单转换(如前所述转换成本可能较高),但你仍应让它们在内部存储机制上呈现显著差异——这正是底层存储机制的核心设计。当涉及更高层次的图类型区分时(有向无环图与树结构、环状结构等差异,以及节点类型变体、边权重标记等),存储复杂度可能呈指数级增长。

      2. > 我认为任何内置正则表达式引擎的语言,都应配备完整的PEG解析引擎

        那样只会催生更多PEG恐怖故事。况且正则处理中的字符串与索引是通用的,而解析器本质上更像框架,复杂度远超前者,注定与多数应用场景不匹配。

      3. 真希望看到一种语言能将分层状态机、数学/线性代数、传感器与执行器的I/O操作、时间/计时机制作为第一类公民。

        主要用于编程机器人和航空航天应用的控制系统

      4.     > 我认为主流编程语言缺失了若干结构,表格是其一,矩阵是其二。
        

        我不同意。多数程序员整个职业生涯都用不到矩阵数据结构。当然,他们会使用包含矩阵的库,但绝不会直接操作矩阵。现代编程语言中矩阵不作为独立数据类型存在,这完全合理。

        1. 除非你认为“多数程序员”等同于“低水平网页应用开发者”,否则我强烈反对。矩阵是统计学、数据分析、图形学、电子游戏、科学计算、仿真、人工智能等众多领域中至关重要的核心组件。

          而那些程序员要么使用专业语言(比如当他们想把程序改造成烂网页应用时就会遇到问题),要么在语法上犯下罪行,比如:

          rotation_matrix.matmul(vectorized_cat)

          1. 这种说法过于苛刻。撇开网页应用不谈,游戏开发甚至无需了解矩阵概念。

            在多数原生应用、嵌入式系统及操作系统内核开发中,这类构造根本用不上。

            1. 我在嵌入式领域工作。为优化嵌入式算法权重,采用线性回归时就需要用到矩阵。

              若从事机器人领域,遇到矩阵的概率极高。

            2. 公平地说,我在游戏开发中确实合理使用了矩阵。若从零编写引擎而非使用Unity这类工具,几乎必然需要矩阵。

            3. 这正是我的观点。即便在高度专业化的证券定价库中,使用矩阵的代码量也少得惊人。

          2. 我不明白为何多数工程师要迁就你的小众用例。既然是编程语言,若缺少所需库,你大可自行开发。没人阻拦你。

            况且已有大量第三方项目被纳入Python标准库。

    2. 现有诸多动态语言可供选择,其中表格/数据框才是真正的一流数据类型:最典型的当属Q[0]。新兴语言如Rye[1]或我开发的Lil[2]也值得关注。

      我认为随着时间推移,主流语言终将全面接纳表格化编程——正如它们逐步吸收了集合操作中的map/filter/reduce等传统函数式编程范式那样。

      [0] https://en.wikipedia.org/wiki/Q_(programming_language_from_K

      [1] https://ryelang.org/blog/posts/comparing_tables_to_python/

      [2] http://beyondloom.com/tools/trylil.html

      1. 有趣的链接——感谢分享。关于“最终实现”的乐观预期,我认为当前语言对键值对集合、命名空间等功能的支持仍相当匮乏。每种语言仅支持其他语言中被证明有效的简洁性、API和数据结构的子集。这距其成为主流并成为多种主流语言的核心已过去三十余年。边际效益递减、技术孤岛、应用领域割裂、范式/取向/惯用法分化、领域内各类功能失调等问题层出不穷……所谓“终将实现”可能需要数十年。或许大型语言模型能加速这个进程…或终结这个时代,让我们坦然承认“不,我们集体从未真正创造出支持所有{X}特性的统一语言”。

        1. 有趣的是Numshell、Rye和Lil之间存在诸多相似性,尽管我认为它们源自不同影响。或许这正是当下主流趋势——人们追求轻量、高阶且交互式的工具。

    3. > R或许最接近,因其将data.frame视为“第一类公民”,但多数人似乎更倾向使用dplyr的tibble等替代方案。

      R用户普遍使用data.frame,因为tibble(及data.table)继承自data.frame。这意味着基础R的“第一类”函数可直接作用于tibble/data.table。这也使得在tibble、data.table和data.frame之间转换变得轻而易举。

    4. > R或许最为接近,因为它将data.frame视为“第一类公民”,但多数人似乎并未采用,反而更倾向于使用dplyr中的tibbles等替代方案。

      你忽略了R语言的data.table,https://cran.r-project.org/web/packages/data.table/vignettes…,

      它非常出色。Tibbles之所以胜出,主要是因为他们在文档/入门引导方面做得更好,最终赢得了业界的认可。

      1. 没错,data.table堪称真正高吞吐量“实时”数据分析领域最顶尖的工具/包。若你正在学习基础知识,或想编写让经验较少的同事能轻松复核的代码,dplyr确实很棒。但根据我的经验,若你接触过银行、贷款机构、保险公司等一线工作者——那些每天手动运行数百次交叉表/相关性分析的人——你会发现大量数据表用户。

        关于作者的观点,Python在这类场景下表现相当糟糕。Pandas性能堪忧。Polar、DuckDB、Dask等工具或许适合生产级数据管道,但快速迭代时过于冗长且挑剔。若有人持枪逼我从海量平面文件中挖掘洞见,我定会要求使用搭载256GB+内存的虚拟机运行RStudio云实例+data.table。

      2. 可读性也是关键。data.table功能强大,但其操作语法(无论是读取还是写入)远不如dplyr直观。

        不过通过https://dtplyr.tidyverse.org/,你可以兼得两者优势——既能享受data.table的性能提升,又能使用dplyr的语法。

    5. 这观察很有意思。主流语言缺乏强大的表级操作支持,可能源于现实中表规模的巨大差异——每当行数规模跃升一个数量级,就会伴随相互排斥的子问题。

      处理百万行表与十亿行表面临的挑战截然不同,而十亿行表的问题规模在万亿行表面前不过是微不足道的误差。标准库必须以某种方式优雅地支持这种海量差异,这绝非易于设计的API接口。

    6. 所有版本的Microsoft Excel均内置Power Query功能,该工具采用M语言编写,将表格作为基础数据类型。程序本质上是对表格列与行的转换操作。虽不确定是否主流,但该技术已广泛普及。M语言同样应用于PowerBI和Power Automate等工具。

    7. > 为何表格不能成为编程语言中的第一类公民?

      它们在q/kdb中运行,效果令人惊叹。SQL表达式同样是第一类公民,这使得编写代码变得非常愉快。

    8. 我认为这并非真正的问题。在R和Julia中,表格功能强大,且属于库类。关键在于这些语言具有极强的表达力和可塑性。

      简而言之,R语言深受Scheme启发,并叠加了惰性求值机制。Julia则是对Dylan开创的设计空间的另一次探索。

    9. SQL不仅涉及单个表,更涵盖多表及其关联关系。若仅需对单表执行查询,基本排序、筛选、聚合和注释功能在任何语言中都易于实现。

      一旦涉及连接操作,复杂度便急剧上升。理论上可通过ORM接口实现多数功能,但若仅依赖运算符,很快会陷入运算符重载(滥用)或创建新语言的困境——后者需重新定义运算符语义:

        orders * customers | (customers.id == orders.customer_id | orders.amount > Decimal(‘10.00’)
      

      其中*表示外连接,|表示过滤。一旦添加排序运算符、分组等功能,本质上就成了带额外步骤的SQL。

      但若能内置这些功能,与数据库交互将更具原生特性。

      1. 每当看到这类设计(比如谷歌新推出的带管道符的类SQL语言),我都感到困惑。在我看来SQL本身极具可读性,语法流畅优美。

        顺带一提,Python同样具备这种特性,所以并非我偏爱Perl之类的语言。

        1. 我完全同意。问题在于它们本质上是两种不同的语言。在Python文件中,SQL只是字符串——没有语法高亮,没有编译时检查等等。若能创造出融合自身语言与SQL作为第一类概念的“奎萨兹·哈德拉克”式语言固然理想,但SQL差异实在太大。

          首先,SQL本就不适合在SQL环境中动态构建。但我们常需动态构建查询(例如客户对产品列表应用多重筛选条件)。SQL的处理方式要么是写满千百个if/else的通用查询,要么依赖存储过程——这会让代码从“行云流水”变成“天啊这是谁写的?”。或者你也可以在擅长字符串拼接的语言(如Python)中直接进行字符串组合。再将整个过程封装为函数和对象,便形成了ORM。

          至今我尚未见过任何将类似SQL功能融入语言本身,从而实现基础ORM功能的编程语言。

          1. 您是否想到Elixir中的Ecto这类查询生成器?

    10. PyTorch最初仅是Torch,且基于Lua语言。当时我没太关注,但显然因需求旺盛,它被重写为Python版本,于是PyTorch诞生了。

    11. 1960年代的达特茅斯BASIC第三版就包含处理矩阵的MAT命令。

    12. R语言才是最佳选择,因为它自1974年诞生起就是专为统计分析设计的语言(其开发初衷就是数据分析/建模)。此外,Tidyverse生态系统堪称绝妙,能极大提升数据整理与增强效率。还有无可争议的最佳可视化系统ggplot,以及内置函数如barplot()或plot()。

      但数据分析终将超越Python和R,迈向Stan和PyMC3等概率编程语言的领域。这是因为我们需要处理嵌套积分,而这些软件生态系统(及其他概率编程语言)提供了最佳解决方案。它们让我们能够理解复杂情境,并做出优质/有价值的决策。

    13. 人们在R中也使用data.table(这是我最喜欢的工具之一,不过已有几年没用了)。相比dplyr,data.table在操作表格数据的语言风格上形成鲜明对比。

    14. 我知道Lua的主要数据结构称为表,但我对其了解有限,不确定它们是否符合数据科学领域对表格的预期。

      1. Lua的表本质上是关联数组。虽然其特性不止于此,但与pandas等系统使用的数据框/表格并不相同。不过你可以在Lua表的基础上构建此类框架。

        https://www.lua.org/pil/2.5.html

      2. 据我所知这些本质上是哈希表,在许多语言中早已是第一类公民

    15. 这正是我对SAS最大的不满——所有数据要么是表格要么是文本。

      多数过程都将表格作为输入输出,还得祈祷表格列名完全匹配。

      想要循环?要么通过表行实现隐式循环,要么用系统调用逐行处理表数据,要么就得写宏(纯文本实现)。

    16. 因为异构N维数据存在分布差异时,没有明显的通用最优数据结构?这当然可行,但资源消耗会比基准方案高出一个数量级。

    17. Fortran不仅满足需求,还提供更强大的功能——它具备一流的多维数组特性,包括矩阵运算。

    18. > 为什么表格不能成为编程语言的一流公民?

      因为它们诞生于需求出现之前,甚至可能早于编程语言的发明。

      在Python中操作数组和矩阵略显笨拙,因为它并非为科学计算设计,相关功能都是后期添加的库。而在Matlab等科学计算语言中,这类操作则高度集成且自然。但反之亦然:由于Matlab未设计处理Python的任务,在非科学计算场景下使用起来反而更笨拙。

      1. 表格结构在编程语言出现前就已存在。

        古苏美尔的泥板文书就通过表格形式呈现信息。

    19. 深有同感。我极度渴望某种数据框架能具备编译时类型系统,让LSP/IDE能准确识别。Kusto查询语言(Azure数据探索器)具备此特性,其自动补全和错误检查功能极其实用。但该语言目前仅限于单一云产品。

      1. 这是处理表格分析的完美方案。据传pandas发明者Wes McKinney也受其启发。

        我对APL的顾虑在于:1) 处理常规任务时语法不够惊艳;2) 唯一可用于生产环境的版本全是商业产品。我不会为需要支付开发许可费和分发版税的项目买单。

      2. 赞同。我曾用它(GNU APL)为数据科学项目做数据预处理。经历陡峭的学习曲线后,它让我感觉像在编写数学公式——既有趣又简洁,我非常喜欢。但它在当今数据科学领域完全无人问津。分享工作成果几乎不可能。不过若只是个人项目,我可能会再尝试一次。

    20. Mathematica最近新增了Tabular命令,姑且一试。我尚未深入使用,但功能似乎相当强大。

    21. 目前存在多款以数据为核心的无代码/可视化/拖放工具,其中数据表/数据框被视为核心功能(例如Easy Data Transform、Alteryx、Knime) 888/1/HenriTEL

      说得没错,你需要的正是SQL语言。近年来duckdb能获得如此关注绝非偶然。我认为数据科学家们往往忽视了SQL和Excel这类工具的价值。

      1. 在现有选项中,我完全赞同——我甚至为此写过博客!https://www.robinlinacre.com/recommend_sql/

        但另一方面,这并不意味着SQL是理想选择——远非如此。使用Python操作DuckDB时,为追求代码简洁性、复用性和可维护性,我常陷入编写生成SQL字符串的Python函数的模式。

        这恰恰揭示了SQL的缺陷:作为语言它基本不具备组合性(相较于拥有顶级抽象能力的通用语言)。DuckDB语法虽略有改进,但我认为这更多是SQL的本质局限。我想表达的是:总觉得应该存在更优解。

      1. 并非不能用这种方式建模数据(或用数组结构体建模),问题在于用户体验会变得糟糕。你可能需要突破内存容量的数据集,或实现文件系统/内存/显存的透明存储。你可能需要高效索引和查询数据。你可能需要动态连接并投影其他结构体数组。你可能需要检测错误数据结构的乘法操作。你可能需要卓越的反射支持。这些功能在现有语言中固然可实现——毕竟它们本就存在于这些语言中——但若能更简便地实现,并获得更核心的地位,无疑会更好。

      2. 其实可以采用数组结构体实现。

        抛开吹毛求疵不谈,若能有个精简的库来处理“表格相关操作”,而不必依赖“庞大的表格框架”,那将非常理想。

        手动实现这些功能并不困难,但更优雅的方式依然值得追求。

      3. 我认为这涉及数据存储方式。我真正想表达的是:编程语言本身应支持高级表格抽象/转换功能,例如分组、聚合、连接等操作。

        1. Map/filter/reduce 是 Java/Kotlin/Scala 的惯用表达。

          SELECT thing1, thing2 FROM things WHERE thing2 != 2;

          val thingMap = things.map { it.thing2 to it.thing2 }.filter { it.thing2 !=2 }

          此外还有distinct()、排序方法、用于限制的take/drop、count/sumOf/average/minOf/maxOf等操作。

          支持集合运算,可执行并集、差集、成员检测等操作。

          连接操作较为复杂,但通过map()配合lambda表达式可实现。

        2. 实现这些功能的复杂度比大多数语言中任何其他一等基本数据类型高出一个数量级,且不存在能满足所有用例的“唯一正确方案”——似乎库和独立数据库才是可行之道,这也是我们当前的做法。

        3. 这听起来很像.NET中的LINQ(实际上它通常与查询表的ORM兼容)。

        4. 没错,就是LINQ+EF。人们长期厌恶ORM(部分理由确实成立),或许已经忘记了它的核心用例。

          (而且LINQ确实有语言级支持,因此它属于“语言本身”而非“库”)

      4. 区别在于语义层面。

        段落不过是句子的集合,句子不过是单词的集合,单词不过是字母的集合。这种递归可以无限延伸。最终你需要为事物赋予意义,而此时明确该事物 究竟是什么 就至关重要——因为结构体的集合可以是多种非表格形态的存在。

    22. 从历史角度看这很合理。表格在许多语言中确实存在,只是主流开发者使用的语言中没有。事实上,若按开发者以外的用户使用率排序,顶级语言 全部 都采用表格类隐喻(SQL、Excel、R、Matlab)。

      开发者使用的语言大多源自Algol。Algol是用于表达算法的语言,而算法本质上是对图灵机的抽象——后者基于无限一维磁带存储模型。这种一维存储模型被植入早期计算机、操作系统及编程语言中,我们称之为“机械同情”。

      与此同时,另一些语言的诞生并未如此紧密地捆绑机器,而是更侧重于科学与数学计算。它们对这种一维世界观的关注度较低。早期语言如Fortran和Matlab就具备二维数据矩阵的概念,因为数学和科学领域本身就存在二维数据矩阵的概念。而C语言等则乐于通过指针数组支持这类操作,因其完美契合自身数据模型。

      同样的逻辑也适用于1基索引与0基索引——Matlab、R和Excel采用1基索引,因其符合表格索引习惯;而C和Java采用0基索引,因其契合内存寻址方式。

      1. 对您观点稍作补充:C语言确实存在类似Fortran的基于存储映射的N维数组/张量,只是保留了传统的列优先/行优先差异,且采用笨拙的“多重[][]”语法。早期存在限制:数组维度需在编译时确定(至少最终维度如此),这既因该特性当时处于半完成/半支持状态,也因其与线性数据模型高度契合。因此常见如 char *argv[] 这样的指针数组,或数值计算库中通过传入维度自行推导存储映射的实现。

        此外,线性内存模型本身并非仅源于Algol/图灵机/理论计算机科学/早期硬件及机械同情。DRAM内部虽存在行列结构,但字节寻址特性使其对硬件客户端系统不可见(除非实施行锤攻击等操作)。相较磁带的快进/倒带,随机访问确实意义重大,但我认为线性存储真正流行的核心在于其作为接口的简洁性。例如:采用近/远指针的分段x86内存相较于32位地址空间显得笨拙,而磁盘文件及其他分配区内部都具备巨大的线性寻址/寻址空间。人们总想推迟使用多个数字,直到真正需要时才动用。学习单变量X时,人们先掌握基础知识,再进阶到多变量X——这里的X可以是微积分、统计学等等。

    23. >为何表格不能成为编程语言的一等公民?

      Matlab支持表格,实际上它包含多种竞争性的表格概念。

    24. Dplyr对data.frame相当满意。R语言是围绕表格数据构建的,其他统计语言如Stata亦是如此。

    25. 说SQL是操作表格数据的标准,就像说COBOL是金融交易的标准一样。基于当前使用情况或许成立,但没人认为这是长久之计。它们都基于过时的观念——认为编程语言应该像皮钦英语而非数学那样表达。

      1. 在R语言中,data.table本质上是另一种形态的SQL

  3. 精彩文章——不过正如其他评论者所言,作者或许该将论据的支撑部分设置为悬念式呈现。

    作者的优先级设定合理,基于这些标准选择R确实合乎逻辑。但数据科学家群体中并非人人如此。我从事数据科学八年,发现绘图和数据框处理仅是工作的一部分,通常还涉及文件操作、解析以及作者所称的“后勤工作”——而R在后勤处理方面表现糟糕。它同样不擅长编写可维护的软件。

    若你更重视后勤保障和代码维护,结论将倾向Python——它在数据框处理方面仍表现不俗。若你同时频繁关注运行速度,Julia则更值得考虑。

    这些优先级选择皆无谬误。我曾期盼Julia能更接近R的特性,但现实是它做不到,兼顾R的功能与通用编程的实用性本就是极难的平衡。

    编辑:对了,还得提一句:我同时从事教学和学生指导工作,总看到学生用pandas解决非表格问题,比如试图将图表示为数据框。显然有些人极度倾向于用数据框处理一切——如果你是其中一员,请重新评估你的工具选择,同时,R语言可能更适合你。

    1. >精彩文章

      但事实并非如此。Python数据科学几乎必然需要使用numpy。因此他举的均值/方差代码实属拙劣比较——numpy内置了数组均值与方差函数。

      即便采用他示例中的纯Python实现,某些语法也能大幅精简:

      groups = defaultdict(list) [groups[(row[‘species’], row[‘island’])].append(row[‘body_mass_g’]) for row in filtered]

      学习Python/numpy所需的认知成本与R语言相当。区别在于前者能将代码无缝集成到任何应用程序中。

      1. > Numpy为数组内置了均值和方差函数。

        即便在Numpy之外,标准库的statistics包也为常规可迭代对象提供了均值、方差、总体/样本标准差等统计函数。那些刻意让Python开箱即用代码显得糟糕的尝试,要么是为夸大问题而精心设计,要么是对Python及其标准库相关功能的刻意无知。

      2. 我不确定。Numpy拥有独立的数据类型、集合结构和语义体系,这些都与Python存在显著差异,我认为将其视为独立领域特定语言(DSL)是合理的。若它仅通过运算符重载为Python提供广播功能,情况或许不同,但Numpy存在的根本目的正是弥补Python在数据结构方面的诸多缺陷。

    2. >我发现通常还涉及文件处理、解析等操作[…]

      正因如此,我对Python和R的使用比例恰好是五五开:在高性能计算环境或服务器上用Python处理海量文件,然后获取百万字节级别的汇总数据,再用R进行本地分析。

      R语言 不擅长 循环处理数百个GB级文件,Python 不擅长 从摘要数据中提炼美观的洞察。各司其职。

  4. 作为偶尔使用R语言的Python程序员:当然如此。Python并非专精于任何领域的语言,而是几乎适用于所有场景的优秀工具——这始终是它的核心优势。

    若你从事数据科学工作,就该学习R语言。即便初学时(尤其对C语言背景者)会因其独特风格感到困难重重,但R正是为统计学家的思维模式量身打造,而非遵循程序员的惯常逻辑。若你终日从事数据科学工作,就该像统计学家那样思考和工作,并使用R语言。这种看似扭曲思维的体验其实大有裨益——因为统计学家的思维模式本就与程序员截然不同。

    不过我几乎全程使用Python工作。

  5. 我的经验是,用pandas做数据科学可行但笨拙又丑陋。用polars稍好些,但真的只是稍好。而对我而言,duckdb则实现了质的飞跃。

    如今我会在OLAP数据库上运行大型查询,将结果下载到云笔记本虚拟机的本地磁盘上存储的parquet文件中,然后直接通过duckdb读取这些parquet文件进行深度挖掘。

    笔记本最终呈现出清晰的SQL查询与结果(多数笔记本服务器支持带高亮和补全功能的SQL单元格),仅在少数需要命令式语言处理的特殊场景下,才使用少量Python单元格。

    因此当我读到文章末尾对比Python和R的段落时,忍不住想喊:“用SQL实现不是更优雅吗?” 🙂

    1. 所以你是说更喜欢SQL而非数据框?我则倾向于使用数据框并保持原生语言操作。

      1. Duckdb同样能读取和操作数据框。Duckdb虽有自有存储,但其他表存储——比如我提到的parquet文件、csv文件,甚至pandas和polars的数据框——都是平等公民。Duckdb能让你快速高效地查询它们。

    2. 咦,作为polars的常用户,我得试试duckdb。

  6. 最后那个示例的纯Python代码比实际需要的更冗长。

        groups = {}
        for row in filtered:
            key = (row[‘species’], row[‘island’])
            if key not in groups:
                groups[key] = []
            groups[key].append(row[‘body_mass_g’])
    

    可重写为:

        groups = collections.defaultdict(list)
        for row in filtered:
            groups[(row[‘species’], row[‘island’])].append(row[‘body_mass_g’])
    

    以及

        variance = sum((x - mean) ** 2 for x in values) / (n - 1)
        std_dev = math.sqrt(variance)
    

    可改写为:

        std_dev = statistics.stddev(values)
    
    1. > (n – 1)

      有趣的是有人会手写标准差函数并 包含 贝塞尔修正项。通常我手动重写标准差函数,是因为担心实现者盲目套用修正项却未考虑其对具体分析的实际意义。至少该函数的正确命名应为sample_std_dev

      1. 这种不一致性实在令人遗憾。标准库statistics模块提供了两个独立函数:stdev用于样本,pstdev用于总体。Numpy和pandas都提供带ddof(自由度)参数的.std()方法,但numpy默认值为0(总体),pandas默认值为1(样本)。

    2. 还有itertools.groupby,虽然可能没短多少(需要定义键函数、排序再迭代),但确实能清晰表达意图。

    3. 不同意。

      首先,原始代码可读性强且直观明了。你的示例为了标新立异而牺牲了可读性。

      清晰的代码(即使冗长)远胜于炫技。

      1. 使用标准库中常见的工具避免重复造轮子,这难道不是“干净的代码”?

        defaultdict在现代Python中无处不在,其概念也绝非难以理解。

        1. 我觉得这个比喻不太恰当,它所处的层面与我理解的“重新发明轮子”不同。在我看来,后者更像是试图在没有充分理由的情况下,为程序增添某种新奇的外在表现形式。例如,用自定义内核驱动重新实现共享内存作为进程间通信机制,尽管它根本没能实现共享内存无法做到的事情。

          这两个例子差异如此微小,实在不明白楼上为何要抱怨。

      2. 依我之见,初次阅读此类代码时,你可能更偏好第一种写法;但读到第二十遍时,或许会倾向第二种。当你真正理解实现逻辑后,往往更青睐能简化大型项目复杂性的精炼语法——尽管初看时可能觉得“过于聪明”。

        1. 我用Python的comprehensions和JS的匿名/箭头函数时也经历过类似情况。

          一旦习惯了语言的“怪癖”(只要它们被视为惯用表达),这些特性就不再显得古怪,而且通常能很快上手。

          1. 非惯用语法同样能达到这种境界,唯一问题在于只有你自己能看懂。

            1. 前提是你能保持这种习惯。

              我确实写过些代码,后来重看时不得不重新学习(这种感觉介于尴尬和谦卑之间)。

      3. 我认为代码清晰度是主观的。我发现第二种更易阅读,因为需要看的代码更少。阅读代码时,我会本能地将其拆解并观察结构关联,因此对第二种方法毫无障碍。而第一种方法代码长度翻倍,阅读时间也大约增加一倍。

      4. 很有意思!感谢大家的反馈。我并非Python母语者,对Python的掌握也不如在座某些人深入。

        话虽如此,我在此改变立场,同意使用标准库,但为提升清晰度,我仍会在此处单独进行'key'赋值。

      5. 我会保留显式key=赋值,因为它不仅是单个字面量。但除此之外,第二种写法更符合惯例且可读性更高。

  7. 最后那个示例除了反Python的讽刺外,实在看不出什么意义。若手动实现标准差等计算,那并非真实世界编程,而是本科生折磨包——这类操作本该在STEM学士阶段终结。

    当然存在大量循环等操作;你揭示了R和Python所有包底层必须执行的核心逻辑。

    1. > 这不是现实世界的编程

      很明显,这篇帖子聚焦于学术研究实验室的工作场景。在此背景下,我认为多数观点相当合理,但就我个人而言,使用Python在现实世界中最大的价值在于能更紧密地与工程团队协作(即使对方并非Python团队)。

      我职业生涯中曾将R代码部署到生产环境,感觉极其脆弱。

      R语言在探索性数据分析(EDA)领域表现卓越,但不适合迭代构建大型软件项目。虽然R拥有出色的包管理系统,但在需要中间层抽象时就显得力不从心了。

      1. 确实,对我而言R从来都不是首选的编程语言… 它本质是统计分析利器——凭借强大包库/尖端统计方法处理数据集,而非生产工具。

  8. 我用Julia的TidierData.jl实现过类似功能,代码结构与R版本高度相似:

      using TidierData, DataFrames
      using PalmerPenguins: load
    
      penguins = load()
    
      @chain penguins begin
        DataFrame
        @drop_missing(body_mass_g)
        @group_by(species, island)
        @summarize(
          body_weight_mean =
            mean(body_mass_g),
          body_weight_std =
            std(body_mass_g)
        )
        show(_, allrows=true)
      end
    
  9. 我实在不明白作者的抱怨。他们举的唯一具体例子是:在没有Pandas的情况下,Python实现相同结果显得冗长且丑陋,因此Python不适合数据科学。

    这要么是糟糕的论点,要么是幼稚且显而易见的谬误——取决于你的视角。

    Python并非为数据科学而生,它不是数据科学的专用语言。MATLAB或许堪称科学计算的专属工具,却在StackOverflow喜好指数中位列最不受欢迎语言榜首。

    不妨换个角度思考:优秀的编程语言如同城市的天气。我当然渴望常年23℃的宜居之地,但若那地方荒无人烟又无知己相伴,我还会选择吗?恐怕不会。

    顺便说一句,Python就像瑞典或芬兰——每年有半年天气糟糕透顶,却依然蓬勃发展。

    附注:我认为这篇文章的标题有点哗众取宠(讨论本身并不特别有价值),因为它极具争议性,且没有人能对此给出百分百正确的答案。或许最好将其视为一篇观点文章。

    1. 对于撰写心理测量学博士论文的整个项目时间线而言,节省40分钟的重要性如何?

      1. 至关重要。这仅是模拟数据集,最终分析将基于规模大得多的数据集(遗憾的是,因无关因素最终未能完成)。此外,重写耗时不多;最终的Julia代码量很小,仅数百行,最多千行左右。

  10. Python之所以成为数据科学领域的优秀语言,在于其广泛的普及度和易读性。若选用Clojure、Common Lisp、Julia等冷门语言,多数人将无法理解代码内容,导致同行评审受阻——而同行评审正是科学研究的基石。若仅以“任务适配性”为优化标准,显然存在优于Python的语言。但若以“科学价值”为衡量尺度,Python(及R语言)堪称最佳选择。在科学领域,单纯完成任务远远不够——他人必须能够读懂并理解你的工作成果。

    顺带一提,人工智能并未带来帮助,反而催生了一代只会编写提示词却不懂代码原理、更无力进行同行评审的科学家。

    1. Julia我无法评价——从未使用过; 也从未用Common Lisp做数据分析(感觉它对现代数据形态不够“数据导向”),但Clojure绝非“冷门”——初见时或许怪异,十五分钟后便会发现它实属最直观合理的语言之一,甚至比Python和JavaScript更简洁。默认不可变特性让代码逻辑推演轻松得多。天啊,它才是真正的数据导向语言——居然没人用简直不可思议。多数人甚至从未听闻。

      1. 我尝试过学习Clojure,但许多JVM托管语言都需要Java基础。Scala、Kotlin或.NET平台的F#也一样。

        早期工具链还高度依赖Vim或Emacs。如今有了VSCode之类的集成开发环境,或许都变得简单多了。

        1. 它本身不需要任何Java知识,但文档有时会默认读者对JVM有一定了解——初学时这点挺让人抓狂。比如直接使用“类路径”这类术语却不作解释。不过如今借助大型语言模型,这些障碍已微不足道。

          若需使用Java,除“创建类实例并调用方法”外无需深究Java知识。我实在不想学习Java这种“恐龙级”语言,但能调用海量Java库确实帮了大忙。通过REPL交互式探索成熟Java库的过程充满乐趣且令人愉悦 🙂

          话虽如此,我至今仍不会写Java的HelloWorld程序

          PS:赞同Emacs的评价。我热爱Emacs…但它专为超级极客设计。同时学习Emacs和Clojure简直是疯狂的门槛(而且Emacs绝非外界宣称的那么简单)

        2. 以上说法完全不成立。我当年完全不懂Java就入门了Clojure,十年后的今天,在成功构建部署了无数项目后,依然对Java一窍不通。播客《Clojure apropos》的联合主持人Mia曾是我的同事,我们共事过多个团队,她把Clojure当作人生第一门编程语言。后来她尝试学Java时,发现和Clojure相比简直怪得离谱,震惊不已。更何况,Clojure完全无需JVM环境——比如配合nbb就能运行。我用它实现浏览器自动化测试,比如搭配Playwright。

          工具链同样强大——我用Emacs,但许多同事朋友选择IntelliJ、Vim、Sublime和VSCode,其中不乏从Atom迁移过来的开发者。

          1. 这或许对你不成问题,但确实困扰过许多人。我最初确实读了三本Clojure书籍。REPL和列表等基础操作固然简单,但工具链相比我习惯的配置相当简陋(我喜欢Lisp,但Emacs需要投入精力)。此外当时许多教程明显假设读者熟悉Java,尤其需要理解Java堆栈跟踪调试。

            1. > 这对你或许不成问题,但确实困扰过许多人

              你是否习惯用复数形式指代自己?还是说你常根据个人经历泛化事物?

              我认识的数百位Clojure开发者从未遇到你描述的困境。当然这可能存在幸存者偏差——或许我结交的朋友恰好都顺利入门了。但正如俗语所说:“愿意付出努力的人会找到解决方案,不愿付出的人只会找借口。”

              Clojure过去确实存在挑战,至今仍有部分问题。但绝非你所言的那些。这绝非夸大其词——安装VSCode的Calva扩展包就足够轻松上手Clojure了,仅此而已。

      2. 我是Common Lisp爱好者,但并非数据科学家。为何建议避免用CL做数据分析?并非想挑刺,只是好奇你的使用体验。

        1. 我对用CL分析数据的经验有限,因为存在“为何要用它?”的疑问——既然已有另一种在数据处理上堪称惊艳的Lisp语言。

          Clojure与传统Lisp的列表不同,其集合基于可组合的统一抽象,默认采用惰性求值,且数据结构直观易读。相较于任何语言(不仅是Common Lisp,甚至Python),Clojure更易于内省且不那么“不透明”,尤其擅长处理异构数据。Clojure 凝聚的數據操作体系,是 Common Lisp 的列表与符号体系无法企及的。

          1. 作业需求另当别论,自1985年以来,极少有严肃的Common Lisp程序将列表和符号作为核心数据结构。

            Common Lisp拥有O[1]向量、多维数组、哈希表(即Clojure的映射)、结构体和对象。它支持集合运算但不强制成员唯一性。它还支持大整数、多种精度浮点数、无限精度有理数及复数。更不用说字符、字符串和位逻辑运算了。与Clojure的核心差异在于Common Lisp数据结构并非不可变。但这与“Common Lisp缺乏现代数据结构库”的论点并无直接关联。

            Common Lisp从未局限于“列表处理”。

            1. 我无意贬低Common Lisp,若伤及你的感受深表歉意。它确实全面支持各类数据结构。我并非指其局限于“列表处理”。SBCL在许多领域表现优异,但从实际应用角度看,Clojure在数据分析方面更具优势。

              你所说的“哈希表(Clojure称为映射)”不仅不准确,更刻意忽略了Clojure的核心设计哲学(不可变性、结构共享、惰性序列)——这些并非表面差异,正是Clojure数据结构在数据分析领域更具根本优势的根源。我认为你混淆了“拥有等效数据类型”与“用相同方式解决相同问题”的概念。

    2. > Python之所以成为数据科学的优秀语言,在于其广泛普及性

      虽然我原则上认同此观点,但这也导致了所谓的“VB效应”。当年VB作为标准课程被普及到每所学校。这让每个孩子都成了“计算机天才”。我曾不得不修复许多由“天才外甥”编写的遗留代码库。

    3. 同行评审是科学研究的基础,但在机器学习领域,评审者几乎从不检查代码,而Python包管理机制几乎无法复现。显然我们尚未达到理想状态,无论是否使用Python。

    4. 没关系,我认为根本没人懂得如何正确编写Julia代码。使用一段时间并关注社区动态(观看讲座、查阅论坛等)后,我发现它似乎缺乏代码质量的概念。人们只是把随机代码扔到墙上直到它开始工作。考虑到用户多为科学家,这种做法倒也合乎情理。

  11. 我写Python已有20年左右,从事数据科学/机器学习工作约15年。尽管最初是Python程序员,但我曾有整整5年只用R语言。我真心喜爱R语言的诸多特性,坚信开发者对R的贬低实属不公…但过去五年我专攻数据科学领域时只用Python,确有充分理由。

    > Python在深度学习领域表现优异。PyTorch成为行业标准自有其道理。此处讨论数据科学时,我特意排除了深度学习范畴。

    职业生涯中我编写的深度学习代码寥寥无几,却频繁运用GPU和可微编程处理非深度学习任务。总体而言,Python更易编写利用硬件的量化程序,且当问题超出内存容量时,它能提供更多解决方案。

    > 我在计算生物学领域运营研究实验室已逾二十载。

    这二十年我几乎完全在工业界工作,而我认为Python更胜一筹的 关键 原因在于:当使用真正通用的编程语言时,它能极大简化与工程其他环节的接口对接。虽然我从未在纯Python团队工作过,但使用Python通常能更轻松地将机器学习/数据科学解决方案投入生产环境。

    > 我在此定义的数据科学,包含大量交互式数据探索以及快速的一次性分析或实验。

    这再次印证了先前的差异。根据我的经验,这可称为所有数据科学相关工作的“第一步”。首要任务是理解问题并降低风险。但绝大多数代码和工作都与交付可扩展产品相关。

    你可以说这不属于“数据科学”范畴,但若真这么认为,在多数我曾参与的团队中你将难以谋得职位。

    综上所述,我对R与Python的经验总结如下:若最终产出是PDF报告,R更胜一筹;若最终目标是交付产品,Python则更具优势。而我的经验表明,在大学实验室之外,仅追求生成PDF的DS从业者能获得的工作机会寥寥无几。

  12. 并非如此。Julia更优越,远胜一筹。但Julia问世太迟。

    大量数据科学代码已基于Python编写。重写代码耗时巨大,这些代码将长期存在。我认为我们将持续渐进式改进Python并重构现有代码。

    1. > 并非如此。Julia更优越,远胜一筹。但Julia来得太迟。

      这听起来很像“差即是好”。Python虽是次优选择——不完善且不够优雅——却因先发优势和主流关注而更具实用性。

    2. Julia的宏功能堪称颠覆性创新。

      你根本不需要领域特定语言。

  13. 我目前正把Python当作兴趣语言学习。靠C/C++和C#谋生。最大的难题是找不到优质的最新示例。我耗费整日才弄明白格式化字符串竟有四种(我记得是)实现方式。这种语法“臃肿”让简单的print语句都难以消化。我甚至懒得用Python 2,只用3.0版本。虽然用空格分隔代码块听起来很吸引人,但实际操作时必须依赖能批量缩进/取消缩进的编辑器,否则永远搞不定。

    1. 十五年前,Python程序员常引用《Python禅》嘲讽Perl:“实现方式应当只有一种——最好是唯一一种显而易见的方式。”这与Perl的“TIMTOWTDI”信条形成鲜明对比:“实现方式不止一种”。

      可悲的是,《Python禅》如今已沦为彻头彻尾的谎言。

      1. 与其说是彻头彻尾的谎言,我更倾向于将其视为天真或非黑即白的思维方式——这种思维在CRUD应用和本科入门项目之外已然过时。

    2. 你似乎在无谓地增加开发难度。

      字符串处理直接用f字符串,其他全抛开。调试时甚至能这样写:

        >>> class User:
        ...     pass
        ... user = User()
        ... user.name = “Surac”
        ...
        >>> print(f“{user.name=}”)
        user.name=‘Surac’
        >>>
      

      关于代码块缩进,你用的是什么编辑器?几乎所有现代编辑器都支持选中代码块后用Tab/Shift+Tab实现缩进/取消缩进。

      VS Code和PyCharm都是免费的Python编程利器,它们都配备了完整的调试器,这对学习语言而言价值连城。

      1. 我认为他们的观点是:对于零基础的Python学习者而言,众多实现方式(如字符串插值)中究竟哪种才是“正确”/符合惯例的做法并不明确。

    3. > 但现实中必须使用能批量缩进/取消缩进的编辑器,否则我永远搞不定

      你用的什么编辑器连这都做不到?记事本吗?

  14. 文章写得不错,但将核心论点推迟到续篇讨论,反而未能支撑自身论述。目前仅暗示Python因需专用包而不够优秀。(反例R语言同样依赖包实现。)

      1. 感谢!这类系列文章通常会在结尾附上续篇链接,由于未见链接,原以为后续内容尚未完成。本文确实阐述了核心论点。以下摘自原文的简要总结:

        >我认为Python作为数据科学语言的核心问题在于:按引用传递语义、缺乏内置缺失值处理机制、缺少内置向量化支持,以及不支持非标准求值。

    1. 完全赞同。作者最关键的例证是两个高度相似的代码片段,二者都相当优雅。

  15. 构思数据科学流程时,本质上包含三个独立步骤:

    [数据预处理] –> [数据分析] –> [结果呈现]

    无论是Python还是R语言,在这三方面都表现欠佳。

    原文似乎侧重于Python在数据准备/处理环节的挑战,主要指出了Pandas库及“原始”Python代码在数据处理中的局限性。

    这个问题可以通过切换到类似duckdb和SQL的数据处理工具来解决。

    在数据分析领域,Python和R各有其适用场景,具体取决于研究领域。同样地,其他专业化语言(如SAS、Matlab)仍被用于特定领域的应用。

    我个人认为Python和R在结果呈现方面都存在局限。Stargazer用于导出回归表尚可,但体验并不理想。在ggplot生态系统中,R的可视化功能可能更胜一筹(我知晓Python的移植版本)。

  16. >>> 每次遇到使用Python的学生,他们的回应总是:"这个需要我花点时间。让我回座位好好琢磨,稍后再答复。"

    这纯属题外话,但我不会因此责怪学生或Python。学生们或许遵循着职场政治的古老法则:“绝不在观众面前排查故障”。至于为何Python用户中更常见这种现象…样本量仅30人。

  17. 我本以为作者会合理抱怨工具链问题,包括代码检查器、格式化工具和包管理器。这些年随着Astral的ruff、uv和alpha阶段的ty工具,情况已大幅改善。

    但文章却说奇特语法反而更易读。我认为这主要关乎库的质量——说实话我对matplotlib和R语言的ggplot同样不感冒。但我不认为这是语言本身的问题。

    我本期待看到性能基准测试,而非仅凭主观感受评价特定代码块。别误会,我虽用Python写过大量生产代码,却也非其铁杆拥趸。提及代码臃肿和模板化问题…作者或许该看看Java或任何现代JavaScript项目。

  18. 该示例用SQL实现会更优。按作者逻辑,这岂不是让SQL成了数据科学的理想语言?况且SQL原生支持表格结构。这种结论显然荒谬,暴露了文章推理的肤浅。

  19. Python在表格数据分析和可视化领域表现欠佳,而这恰是本文核心议题。R语言显然更胜一筹,甚至Tableau、Matlab、JMP、Prism乃至Excel在多数场景下都更出色。Pandas+seaborn虽有长足进步,但seaborn仍存在令人沮丧的局限性。而pandas本质上是独立的编程语言。

    若你的数据已呈表格化,却仍选择Python,那不过是为求职而学习Python罢了。而非因为它是当前工作的最佳工具。Python相较其他选项的唯一优势在于$$$。相比死守R语言,掌握Python将使你获得更高的就业竞争力。

    原因在于Python是数据与机器学习 工程 领域最 优秀的 语言之一,而这恰恰占据数据科学岗位实际工作内容的80%。

    1. > 而pandas本质上是另一门独立的编程语言。

      我认为dplyr/tidyverse之于R,远比pandas之于Python更像独立语言。

    2. …除非你的数据工程工作基于数据库,这种情况下R的dbplyr远胜Python任何解决方案。

  20. 这取决于“数据科学”的具体定义。在随机模拟领域,C++与Eigen库堪称无与伦比——借助Eigen的“零成本抽象”,你既能获得高级代码的可读性,又能享受低级代码的性能优势。

    若数据科学指的是加载数据到内存并运行预设的回归、分类等问题处理程序,那么Python表现出色——它内部主要调用C/FORTRAN二进制文件,因此Python本身的开销相对较小。

  21. 这番论述令人失望。我日常使用Python和Pandas,能举出更笨拙的工作流程实例。最常见的是遇到数据框[(数据框.列1 == 某个值) & ~数据框.列2.isna()]这类构造,这恰恰暴露Python语法在此场景下的局限性,不适合此类操作。遗憾的是没有替代方案,R语言也未必更优雅,同样存在大量丑陋的实现。

    Julia 存在严重缺陷,例如从命令行启动脚本时的冷启动速度极慢,这使其不适合命令行工作流。

    否则就只能转向编译型语言,但它们同样存在取舍问题。

    1. > 遗憾的是没有替代方案,我认为 R 语言也未必更优雅,其中同样存在大量丑陋的设计。

      你试过Polars吗?它能有效避免你展示的代码中低效创建中间布尔数组的情况。

      > 还有Julia——它存在严重缺陷,比如从shell启动Julia脚本时冷启动速度慢,这使其不适合命令行工作流。

      随着时间推移,Julia在启动速度方面有了显著提升,尤其体现在绘图功能上。基于REPL或笔记本的开发模式显然更具优势,它能将编译成本分散到多次执行中。编译机制正日益模块化,既支持基于包的预编译,也提供提前编译模式。我特别欣赏其典型编译过程作为隐式步骤的设计,这使得工作流更接近脚本语言而非传统编译语言。

      同时我也欣慰于传统的前置静态编译至二进制可执行文件的部署方式如今同样可用。

      在R或Python中开发一天后,我常开始懊悔未选用Julia——因为深知若使用Julia,昨日的代码本可运行得更快。真正的抉择在于:是愿付出今日的时间成本,还是承担项目生命周期中的持续代价。

      1. > 你试过Polars吗?它能有效避免你展示的代码中低效创建中间布尔数组的问题。

        问题通常不在于效率,而在于语法冗余。Polars确实能消除部分冗余,但整体反而更赘述(显然是设计使然),在探索性数据分析中很快就会令人烦躁。

  22. 我基本认同这个观点,但反对用底层代码来论证语言缺陷。具体到R语言,若没有tidyverse生态,其表现确实难称完美。

    尽管承认这令人不快,但Python之所以成为数据科学利器,几乎完全源于其庞大的用户群体。这种普及性本身就是优势。

  23. Python本质上是这样一种语言:

    1. 代码易读

    2. 便于扩展为科学数据工作者偏好的语言形态

    攻读硕士期间,我们在天文物理研究中曾修改numpy源代码并贡献了部分代码。

    虽然Java和R语言也有类似工具,但我们第一学期学过C语言,Python更易于阅读,而且与MATLAB不同,numpy无需许可证。

    当数据科学兴起时,这个领域聚集了许多从事过类似工作的物理学家。他们带来了自己的工具,其他人也带来了他们的工具。

    1. Python的核心优势在于它能吸引毫无编程经验的新手。他们或许模糊地知道想让计算机先执行这个操作再执行那个操作——命令式编程正是他们的起点,而Python完美契合这种需求。它最初作为脚本语言设计,主要用途确实是串联其他工具。它始终擅长此道,这也是九十年代的主要应用场景。

      当Linux发行版开始大量依赖Python脚本(如红帽和Debian)后,Python迅速走红。其副作用是早期便广泛存在于众多Linux和Unix系统中。2000年代初及九十年代末的科研人员普遍使用运行Linux/Unix的工作站,因此Python作为现成的易用工具自然成为首选。

      正因其易用性,大量用户由此入门。于是形成独特发展轨迹:各领域研究者代代相传,将Python视为首选工具。它从未在任何领域达到顶尖水平——这本就不是开发目标。相较某些数据科学家偏好的替代方案,它运行稍慢,语法略显冗长笨拙,且缺乏诸多其他语言具备的功能。等等。但这些都不重要,因为它简单易用。初学编程的用户需要的是能理解的简单工具,而非数学家或计算机科学家追求的理想化语言。

      Python相当于现代版的BASIC——在Python诞生前,BASIC曾扮演过这个角色。它本身并不惊艳。但早期家用电脑将其作为操作系统组成部分。例如我的第一台电脑Commodore 64,其核心操作体验就是通过交互式BASIC控制台从磁带加载游戏。

  24. 作为Python和R语言的深度使用者,我的结论也类似:

    处理、探索或可视化数据时,我必然选择R语言。

    若需构建机器学习/深度学习模型或处理大型语言模型,则通常选择Python。

    如今借助Quarto工具,在同一文档中同时使用两种语言变得非常便捷。

    1. Python存在若干语言层面的根本性缺陷,其运行速度和精度高度依赖集成库绑定。

      Julia支持嵌入R和Python代码,并提供强大的数据集深度挖掘工具:

      https://www.queryverse.org/

      这是数十年来我见过的首款能将完整范式浓缩为单字符语法的语言,其性能在多数场景下甚至超越C语言和Numpy。=3

      1. 一位Julia语言的支持者竟在毫无证据的情况下,将一种广受欢迎的语言污蔑为“根本性缺陷”,这实在充满讽刺意味。

        https://yuri.is/not-julia/

        1. 这就像那些人转发迪杰斯特拉倡导0基索引的信件,却从未读懂或理解自己转发的内容。

          1. 索引语法与Julia在正确性缺陷和陷阱代码方面糟糕的历史有何关联?

            1. 当然啦,若论缺陷频发史,所有软件都糟糕透顶…

              https://github.com/python/cpython/issues

              某些喷子在博客上拿v1.0.5版本里几年前已关闭的票据大做文章,试图证明Julia很烂…这种论证实在站不住脚。Julia的回归测试功能甚至内置在绘图库输出中,因此问题通常能通过严谨的可复现性保持已解决状态。另外,在任何LLVM语言代码中运行合理性检查通常都是明智之举。

              祝你好运 =3

              1. 仅说“其他语言也有错误报告”这种方式来推广Julia实在太差劲了 =3

                1. 直白地说:摩尔定律如今已名存实亡,若执着于用懒惰单子追逐单体架构哲学,终将限制你的选择空间。

                  Julia这类语言通过广播运算符能更简洁地处理条件并行,并支持通过SSH透明地远程创建主机进程实例(但要达到OTP级集群功能仍需大量工作)。

                  与Go类似,将库资源移植到原生语言能悄然解决开发者面临的Python式多语言兼容问题。

                  祝好运。=3

        2. Python的线程与计算错误问题由来已久。它虽是广受欢迎的集成“粘合剂”语言,却依赖SWiG封装器来规避诸多未解决/不可解的缺陷。

          这并非“抹黑”,而是众所周知的语言局限。或许你的环境与我的运作逻辑不同。

          人们竟对如此琐碎平庸之事投入情感真是荒谬。Julia当前版本v1.12.2可能存在差异,但Queryverse确实充满乐趣=3

  25. 本文主要缺陷在于将面向生产系统的通用语言(Python)与专为交互式分析设计的领域特定语言(R)进行比较… 切忌混淆概念,因为将R代码产品化通常需要重写为其他语言。

  26. 从事计算生物学数十年,用过十余种语言,我确实认为R更适合数据科学。但实际工作中我几乎每次都选择Python——它拥有更丰富的库,且更容易找到软件工程师和Python协作伙伴。然而R能编写更简洁的代码,减少无提示错误,且1索引特性让生物序列处理轻松许多。

    1. 恕我直言?减少无提示错误?R在宽容解析用户意图方面存在不少陷阱。这虽便利于探索性分析,但用于生产代码时却脆弱得多。

      仅举一例:R采用1索引制。但若访问向量myvec[0]不会报错,而向量长度为3时访问myvec[10]却会返回NA(本应合法的值)。更危险的是向量末尾赋值操作:myvec[15] <- 3.14 会静默扩展数组,插入NA值

    2. 根据我的经验,R堪称“无视错误之王”——明明该在100行前报错时,它却能若无其事地吐出荒谬结果。

  27. 我认为从›Python v. R‹之争中得到的启示是:人们更倾向于使用一种既能胜任数据科学任务又具备通用性的语言,而非专为数据科学打造却存在规模不经济问题的语言。具体来说:假设某款新型数据库刚问世。此时,既需要将其集成到应用程序中,又需要从中提取数据进行分析的用户群体,共同推动了Python库的需求。这种协同效应远优于为满足两类需求分别开发两种语言库的方案。

    1. 你提到了使用Python输出结果的优势。

      我认为Python在数据预处理方面也比R这类专用分析工具更胜一筹——它能将输入数据打磨成足够干净的可操作数据集。

  28. 编程语言天生具有网络效应;全球多数人学习英语是为了与同样懂英语的专业人士交流,而非出于对狄更斯的热爱。

    我认为(也基于自身经验)Python胜出是因为团队其他成员都熟悉它。我更倾向于使用R语言,但我们的网页开发人员不熟悉它,而编写团队其他成员能够审查、扩展和维护的代码对我来说更为理想。

  29. Python的普适性造就了其流行度。借助成熟库可实现GPU数据双向加载,必要时支持内存映射。若循环效率低下,可将热点循环重写为Rust或C语言。仅需几行代码就能读写绝大多数文件格式。

  30. Python是我的饭碗。若由我选择,我会用其他语言,但不可否认它如今在几乎所有领域都占据重要地位。随着年龄增长,我逐渐意识到编程语言只是解决计算机问题的工具,学会了在公司/项目使用的任何语言中寻找解决问题的乐趣。

    但在个人项目中,我最钟爱的语言是Dart。

  31. > 对比这段仅使用基础Python特性、未借助特殊数据处理包的代码,其等效实现却充斥着繁琐逻辑

    我并非Python的吹鼓手,而是基于现实选择的用户——它确实是相当出色的粘合语言。但上述问题确实存在。

    正是Duckdb、pandas、numpy等库让Python如此出色。

    约十年前我在某大型商业智能软件公司工作时,推广R语言时遭遇过另一个荒谬问题:维基百科、知识库和搜索引擎不支持单字母搜索词。

    因此无论当时R语言多么优秀,人们总觉得它的学习难度远超实际水平。

  32. 尽管我个人更偏爱Python而非R,但对此观点并无异议。不过我也认为R未必是数据科学的 理想 语言——它自有缺陷,比如上次尝试时发现编写自定义循环(或用map/reduce实现功能等效)相当笨拙。

    另一个问题是,R语言的许多优势其实源于tidyverse生态。这固然要归功于R作为可扩展语言的特性,它让优秀的API设计师得以大放异彩,但我认为Python语言完全可以拥有类似的库。事实上它已经有了plotnine库(虽然我还没试过Polars库,但至少它似乎拥有更统一的API)。

  33. 在文章中

    > 对比这段仅使用基础Python语言特性、未引入特殊数据处理包的等效代码:

       n = len(values)
       # 计算均值
       mean = sum(values) / n
       # 计算标准差
       variance = sum((x - mean) * 2 for x in values) / (n - 1)
       std_dev = math.sqrt(variance)
    

    他显然不知道Python标准库里有统计包(https://docs.python.org/3/library/statistics.html)。当然,如果你不懂Python,确实会写很多冗余代码。

  34. 这并非在说“Python不适合数据科学”,而是作者对R语言的专业偏好。我猜这种标题点击率会低得多。

  35. 从诸多实践角度看,Clojure在数据处理领域表现优异。通过clj-python库还能调用Python库资源。

      1. 值得强调的是,你并非被某个库的生态系统所束缚。通常你可以独立使用许多分离的组件。我在处理气候数据时基本不用Scicloj的大部分工具,但tech.ml.dataset却被我广泛应用。

  36. 所示例的纯Python/标准库方案(以及刻意回避数据科学类扩展库的做法)实在…欠妥?(此处“欠妥”意指刻意规避标准库功能,以凸显作者后续诟病的缺陷。)

    更优的纯标准库方案应为:

        from palmerpenguins import load_penguins
        import math
        from itertools import groupby
        from statistics import fmean, stdev
    
        penguins = load_penguins()
    
        # 将DataFrame转换为字典列表
        penguins_list = penguins.to_dict(‘records’)
    
        # 创建按物种/岛屿分组排序的键函数
        def key_func(x):
            return x[‘species’], x[‘island’]
    
        # 过滤缺少体重数据的行,按物种和岛屿排序
        filtered = sorted((row for row in penguins_list if not math.isnan(row[‘body_mass_g’])), key=key_func)
    
        # 按物种和岛屿分组
        groups = groupby(filtered, key=key_func)
    
        # 计算各组均值与标准差
        results = []
        for (species, island), group in groups:
            values = [row[‘body_mass_g’] for row in group]
            mean_value = fmean(values)
            sd_value = stdev(values, xbar=mean_value)
            results.append({
                ‘物种’: species,
                ‘岛屿’: island,
                ‘体重均值’: mean_value,
                ‘体重标准差’: sd_value
            })
    
  37. 有人注意到Python的类型提示功能有多像Scala吗?我2015年左右用过Scala,看到类型提示时立刻意识到它与Scala的实现方式高度相似。

  38. 数据经清理并规范化后,选择哪种工具就成了个人偏好问题。

    工作经验表明,90%的工作在于从不同来源收集、清理和转换数据。在这方面Python提供了更多可选方案。

  39. 免责声明:我对R或Python并无偏见,亦无倾向性。

    Python语言本身或许并非数据科学的理想选择。但作者完全可以借助Python中的Pandas、Polars或其他数据科学相关库/框架,实现原本计划用R语言编写的任务。我能同样理解她提供的R语言和Pandas代码片段。

    这篇文章读起来就像在说:“看啊,我所有食材都从零开始制作,多难啊!”

  40. 他们的批评似乎集中在两点——pandas的局限性和内置函数支持不足。

    就个人经验而言,polars库已解决我使用pandas时遇到的多数“棘手”问题。它运行速度更快,拥有符合人体工学的API,与pandas无缝兼容,并支持强大的自定义扩展。我们必须意识到pandas诞生至今已近二十年。

    我承认Shiny是卓越的包,但考虑到大型语言模型将编写大部分代码,其重要性已大不如前。

  41. 结尾不妨加上“Python并非完美语言”。

    但Python是否成功?绝对是。

  42. 现实是它胜出在于易用性而非完美契合

    1. “不优秀”未必等于“糟糕”,也可解读为“优秀”甚至“非常优秀”。诚实的标题本应明确说明作者对其适用性的具体评价。

      作者避免直接称Python是糟糕语言,恰恰说明了它的适用性。当然,这还得益于数据科学实践中的主流地位。

  43. 他们基本上在推荐使用R语言。我认为这取决于他们对“数据科学”的定义,以及该从业者是否仅从事数据科学工作。若专注于此领域,R语言或许更胜一筹。比如整个职业生涯都将扎根于该领域。但若走的是通用计算机科学路线,学习Python的收益可能远超R——仅因它能胜任更多场景。

    > 无论如何,本文不再深入探讨。我也不会考虑Matlab或Mathematica这类专有语言,以及Octave这类生态系统匮乏的冷门语言。

    我认为对多数程序员而言,R语言属于同类。它之于他们,正如Octave之于本文作者——虽说R很不错,但他们真想学这种“小众”语言吗?即便它某些特性优于Python?值得为掌握全新范式、语法和库生态而费神吗?

  44. 猜猜看,用专业化且成熟的库(Pandas)完成相对复杂但标准的任务(筛选和聚合企鹅样本),比用基础列表和字典“徒手操作”更高效。

    更简洁、更高效、更少出错、数值精度更高——这就像Python拥有与R比肩的精心设计的库生态系统。

  45. 也许对长期使用R的人来说它很合适?但作为偶尔需要做数据分析的软件工程师,我发现使用熟悉的工具比R更轻松。R作为语言实在过于复杂。

  46. Python几乎在所有领域都是第二好的语言

    1. 没错,矛盾的是它几乎在所有领域都是第二差的语言。

  47. 从事数据科学的R用户通常来自生命科学或统计学背景,而Python数据科学者则多源于工程类专业。当然这并非绝对,但确实是普遍现象。

    最近我注意到Python正被大力推广至所有数据科学领域。客观而言,Python未必是最佳选择——尤其在统计领域。但当某种工具成为“行业标准”后,即便其实用性存疑,也难以改变其主导地位。

  48. R语言之所以卓越,部分归功于迪·库克、哈德利·威克汉姆和谢毅辉等开发者打造的理想化软件环境。

    R语言的另一优势在于:任何函数都能彻底改变参数的评估方式,这使得tidyverse套件能实现诸如在数据框上下文中评估参数、添加管道运算符等语言特性。虽然将这种功能交到统计学家手中风险极高,但它确实比Python提供了更丰富的语法创新空间。

    1. 与Python类似,R是双层语言系统(2 (+…))。随着问题规模扩大,需借助C/Fortran后端提升性能。

      Julia和Nim[1]分别代表动态与静态单层语言系统,二者均支持用户自定义运算符和宏。个人而言,Julia的表面语法令人不悦,且我不习惯整天泡在PLang REPL/emacs环境中。当然,Julia和Nim都未到难以调用C/Fortran的程度,但其社区倾向于直接在新语言中实现功能。

      [1] https://nim-lang.org/

  49. Shell才是数据科学领域的最佳语言。为数据获取、清洗、转换和可视化分别选用最优工具,再凭借文本作为通用互操作协议、文件作为数据中间阶段存储媒介的天然优势,将它们无缝衔接起来。

    最妙的是,只需编写–help文档,就能将这些工具加载到大型语言模型中,让模型自动为你解决问题。

    来辩论吧。

    1. 使用Shell脚本重构[1]已成为我处理多步骤数据问题的首选方法。它能轻松追溯数据获取、清洗、转换等每个环节。

      我在脚本中使用mlr、sqlite、rye、souffle和goawk,并借助visidata交互式审查中间文件。

      1. https://redo.readthedocs.io/en/latest/

  50. 但Python确实是数据科学领域的卓越语言。正如英语谚语所言:实践是检验真理的唯一标准——它在数据科学领域的大规模应用,恰恰证明了其卓越性。

    或许你会反驳:并非所有成功的事物都值得称赞。此言不假,但Python的成功源于自然演进,而非广告宣传、事实垄断、政治因素、资金支持或先发优势。

    不过确实存在一个非Python固有因素——来自numpy开发者的贡献。整个生态中存在这样一个统一的数值库:极易使用、运行迅捷且功能完备,这点至关重要。

  51. 好吧,标题党成功吸引我了。但这些论点站不住脚。本质上就是"Python并非优秀语言… 因为它不像R那样是领域专用语言"

    平庸之作!

  52. 我认为TypeScript在此领域将大放异彩。尤其适用于数据输出管道,可生成强类型数据集。

    此外基于TS的探索性代码还能通过d3绘制SVG图表,甚至直接导出网页。

  53. 我们团队正逐步从R迁移到Python。这并非迫不得已——R的模块体系笨重难用,自动化处理颇具挑战。Python的通用性远胜R任何优秀模块。若有人想在Python中复现R的特定包,通常都能找到对应方案。

    虽然绘图功能可能不够流畅,但我实在看不出R有明显优势。况且到2025年,我只需向大型语言模型提供数据样本和所需图表类型,就能获得零编码生成的理想图表。

    作者的行文风格在我看来颇具学术气息。

  54. JavaScript 也不是很适合网页开发,不过……

  55. 作者不是在说 Python + Pandas 几乎和 R 一样好,但没有 Pandas 的 Python 功能不如 R 强大吗?

    我忍不住得出结论:Python 和 R 一样好,因为我需要时仍可选择使用 Pandas。我哪里理解错了?

    1. 你漏掉了开头那句的“几乎”。

      而且我们根本没定义“优秀”的标准。

  56. 当数据科学成为主流时,Python确实是绝佳的数据科学语言。

    它易于构思结构(迭代器),易于扩展,拥有强大的社区支持。

    正因如此,人们开始通过库扩展它。

    如今已有更多替代方案。

  57. 这真该搞个Python与R开发者的A/B编程速度测试。

  58. 修正标题:若没有pandas/polars/ibis,Python绝非数据科学的理想语言

    1. 请仔细阅读原文。文中明确以pandas代码作为示例。

  59. > 我认为人们严重高估了Python在数据科学领域的地位。它存在诸多值得注意的局限性。许多数据科学任务我宁愿用R而非Python完成。1 我相信Python在数据科学领域如此普及,更多是历史偶然性使然,加上它在多数场景下勉强可用,而非其天生适合数据科学工作的体现。

    Python无需在任何单一领域做到顶尖,只需在众多领域具备可用性即可。你可以让精通完全不同软件领域(Web开发、运维、系统管理等)的人员接触数据科学领域,而无需他们学习全新的语言和工具链。

    1. 但这并非它被数据科学领域采用的真正原因。许多数据科学家终日使用Python,甚至从未考虑过转行。

      它之所以被数据科学采用,是因为它本身就是数据科学的代名词。

      1. 它之所以被数据科学采用,是因为没有其他语言能提供如此完善的库支持。

        而它能获得这种前所未有的支持,正是因为从诞生之初就明确聚焦于语法和(感知上的)简洁性。

        此外,它对算法工作的友好性也产生了某种累积效应。

        Guido的长远战略击败了众多其他强有力的竞争者。

        1. 我认为大多数数据科学家未能察觉的关键在于:他们使用Python并非因其满足需求,而是我们两次辜负了他们。

          1. 数据科学家 并非 程序员,为何需要编程语言?他们应使用的工具并不存在。这些工具需要程序员开发,而我们提供的却只是…更多编程语言。

          2. 现代软件的核心困境在于:现代编程语言最重要的特性是易读易写,而这一特性在多数主流语言中却明显缺失。

          他们陷入两难境地。没有编程语言就无法完成工作,但可选语言屈指可数。Python最终获得如此完善的库支持,本质上是因为他们别无选择。

          1. 当首批科学库为Python编写时,其他语言甚至不考虑可读性或便捷性。当时的选择更像是C/C++/Fortran与Python的对决。

            随后Python进入自我强化的循环:科学界不断探索改进Python对数据分析所需交互式工作的支持。想想 ipython → jupyter → jupyter 分支及其他以 Python 为核心的笔记本系统。

            因此当数据分析演进为数据科学和机器学习时,GPU 优先的库供应商早已面对大批精通 Python 的用户群体。

            如今仅凭这些用 json 包装的脏 Python 代码就能调用数百个 GPU,简直不可思议。

            1. 你似乎忽略了Perl(及各类Unix工具)和Matlab。PDL(Perl数据语言)曾风靡一时,IDL等类似工具亦然。

      2. 但数据科学绝非孤岛。

        个人项目随你喜欢用什么语言,但若想让模型直接投入生产环境,就该选更适合生产化的方案。

        将R模型投入生产相当痛苦。常规做法就是直接重写成非R语言版本。

        1. 我对直接将数据科学代码投入生产深感失望,结果往往是难以维护的混乱。

          若先用R编写再转译为C语言(更优方案:用英语重构代码,将R代码作为辅助注释,再由他人转译为C),至少能确保你已深入思考过解决实际问题所需的抽象概念和操作逻辑。

      3. 部分原因如此,但更关键的是,在“数据科学”工作中,90%的任务并非直接分析。

        你得先从某处获取数据。是否需要爬取?毕竟Python擅长爬取?哎呀,爬取后发现数据格式是ObtuseBinaryFormat0.0.LOL。测试阶段发现——真巧,有人写了Python转换器。接着得清理所有损坏条目,Python在这方面表现尚可。诸如此类。

        关键在于:虽然Python未必是特定任务的首选,但对多数任务而言,它作为第二或第三选择完全可行。

        所以你可以学Python。或者你学习<最佳语言><其他语言>。若<其他语言>是Python,那么<最佳语言>是否真的比Python优秀到值得花时间学习?

      4. 现在或许如此,但曾经他们需要招募数据科学人才时,可是从其他领域挖人呢。

  60. 我对Python的质疑在于它让错误变得太容易犯——它包容万物,接纳所有人。这种过度包容和宽容虽利于表达与创造,却不利于精确科学和严谨学科。在某些领域,主观臆断和迷信式编程往往损害科学本质。遗憾的是,在高级抽象层面要兼顾正确性与速度并非易事,因此行业在多方面迫使社区做出妥协。

  61. 问题在于其发展势头过于强劲,难以修正航向。PyTorch如今已是庞然巨物。

  62. 数据科学是我认为Python尤其擅长的领域

  63. 等等,这难道不是证明R和Python实现基本等价的典型案例吗?

    我满怀期待准备看到令人信服的实例论证,结果除了引号和括号外一无所获。

    太令人失望了。

  64. 所以说… 这是篇 Python 嫉妒文吗?

  65. “Python 不是一门优秀的语言”这句话就俘获了我

    1. 不擅长什么?

      我同意Python在任何特定领域都不算顶尖,但它几乎无所不能——这正是它的伟大之处。

  66. 我认为用“数据科学代码就是从头到尾写个程序,输出结果,画个图就完事”这种标准来比较编程语言,根本不是个好方法。R语言确实是高效的统计专用语言,但用它构建持久化软件简直痛苦不堪。Python虽非完美,但我见过更少令人眼花缭乱的代码库——即便图表再漂亮。

  67. Python在任何领域都不算顶尖。或许适合教学(但结果就是培养出只会Python的人)。

  68. 发现这本质上是R与Python的对比文章时颇感失望——这已是数据科学领域的陈词滥调。从业二十余载,我曾坚定支持R阵营,如今却认为数据科学领域尚未出现真正优秀的语言。我曾对Julia寄予厚望,甚至Clojure的数据生态也颇具吸引力,但面对Python的强势发展态势,此刻它似乎难以撼动其地位。

    1. Python无处不在。最近面试一批数据科学家时,仅有一人掌握SQL。他们全都使用Python,我敢说其中无人听说过R语言。

      1. SAS > R > Python。

        SAS和R的应用主要局限于数据科学相关领域;而Python作为通用性更强的编程语言,接触者群体更为广泛。因此,具备Python经验的人才储备远超SAS/R——即便在SAS曾被本科/研究生课程积极教授/应用的时期亦是如此。

        作为数据科学与工程领域的招聘负责人,我对上述所有技术(及SQL等)均有丰富经验。如今招聘已能轻松跨越专业背景/学历限制,找到能即刻上手的优秀人才。

        1. 你抢先一步了。我理解SAS为何饱受诟病,但这往往源于人们对其强大功能的认知不足。

          1. 它曾是卓越的语言,但高昂成本始终是致命缺陷,加之学术界因类似原因逐渐弃用,最终被免费替代品取代。

      2. 天啊。他们是资深数据科学家还是刚毕业的新人?竟不懂SQL实在令人费解(还有点可怕)。

        1. 经验丰富的数据科学家和/或刚毕业的新人,在宝贵的SQL经验方面始终存在严重缺口。以拥有25年SAS经验的数据科学家为例,其中 许多人 精通DATAstep,却极少掌握通过PROC SQL以最高效方式查询数据的技巧——即便他们通过SAS/ACCESS的直通模式提取数据也是如此。

          他们通常采用极其简单的查询方式,先通过DATAstep进行数据处理,随后才运行建模或报告PROC程序,而非将数据推送至上游,通过穿透机制采用原生数据库SQL进行更高效的数据提取。

          早在2008/2009年,我通过将常规报告的全部代码重构为SQL直通模式,成功节省了30小时以上的运行时间。而数据科学家最初的代码仅是从外部源提取数据后在DATAstep中进行处理。运行时间从30小时缩短至3分钟(Oracle后端),相当于释放一名全职员工——从每周三次守着长时作业变成每日多次处理。

    2. 所谓“适合数据科学的优秀语言”究竟意味着什么?

      首先,数据科学更像是人们给一袋猫贴上的标签,而非由尺寸相仿的盒子构成的广阔领域。

  69. 对于觉得文章太长不看的人,作者的论点如下:

    – 像Python这样的通用编程语言足以胜任数据科学,但并非为此专门设计。
    – 像R这样专为数据科学设计的语言更擅长数据科学。

    谁能想到呢?

  70. 继承的Python代码参差不齐,继承的R代码则是噩梦。

  71. 说得对,这不过是我们现有的最佳选择罢了

  72. 我想指出的是第一个企鹅示例中的Python代码——这到底是什么鬼?

    看起来像状态不佳时的Perl,甚至更像是自动生成的JavaScript。

    为什么非要嵌套这么多层对象?

  73. 尝试Langgraph代理时,我被迫使用Python。

    效果尚可,但TS/JS版的Langgraph严重落后。React代理只需几行代码,而JS/TS实现相同功能却要50多行。

    与其维护50行代码,不如改用其他语言——哪怕是不熟悉的语言——来实现几行代码的效果。

  74. 标题最后三个词完全可以省略 8-/

    Python绝非优秀语言

    首先,空格要求让人想起1970年代Fortran的糟糕设计。

    其次,它堪称最不兼容自身的语言。

  75. Python的审美实在糟糕。__init__(self)这种写法在2025年的语言里完全不可接受。Ruby本应是更优选择。语言设计中的粗糙感就是个糟糕的主意。

    1. 2025年居然还有@dataclass这种东西

  76. Python在数值计算方面更是令人难堪。它既不支持不同浮点类型,缺乏n维数组数据结构,运行速度又极其缓慢。

    但数值计算领域必须掌握它。这至少向我表明:只要缺陷能基本弥补,“足够好用”才是关键——数百万次集成、实例和文档的重要性,远超语言特性是否契合特定场景。

    1. 原生Python在数值计算领域毫无希望,因此几乎所有人都使用numpy来解决这些问题。当然,这需要额外安装包。但Python的优势在于能无缝集成这类增强核心功能的扩展包。另一个重要例子:pytorch。

  77. 注意文章中load_penguins()示例的处理方式:它在数据科学所有繁琐环节完成后才开始,并在下一个痛点出现前戛然而止。

    它栖身于一个无菌的理想化世界。

    Python之所以成为实践中卓越的数据科学语言,是因为数据科学本质上也包含:

       - 整合大量数据源
    
       - 清理海量结构混乱的数据
    
       - 验证与错误处理
    
       - 输入输出、网络通信与格式转换
    
       - 引导非程序员入门编程
    
       - 封装编译型语言库或系统接口
    
       - 快速原型开发并向相关人员展示
    
       - 将原型转化为更持久的项目
    

    而事实证明,Python及其生态系统在这些方面表现优异,同时在其他领域也保持着良好性能。

    其他语言或许在某些特定领域表现出色,但鲜少能全面兼顾。鉴于人类社会庞大、多元且持续革新,在这些领域成为第二好的选择终将赢得胜利。

    无论你是谁,在完成任务X时若体验不佳都会感到恼火。但若在任务Y和Z上遭遇最糟糕的体验,你定会羞愧难当。而任务X、Y、Z的定义因人而异。

    你渴望高效完成工作,可一天只有24小时。

    理解Python现象的诀窍一如既往:必须把握全局,而非局限于泡沫中的狭小角落。更不是你脑海中的理想世界。生活不是数学题,没有明确的前提和优雅的答案。

    这就像讨论PHP为何能在2000年称霸网络(无论代码多么混乱),Windows为何长期被使用(尽管糟糕透顶),人们为何在各种问题后仍坚持用iPhone等等。其中蕴含的因素远超你日常使用的场景。人们的需求是你未曾设想过的。

    所以这不是“语言战争开战”,而是“兄弟,多积累经验吧——去和会计师、非政府组织、政府部门及物流链合作,去中国、非洲和南美工作,从初创公司到学校再到企业,满足极客、艺术家和商人的需求,然后我们再谈”。

  78. > 我认为人们严重高估了Python在数据科学领域的地位。它存在诸多值得注意的局限性。许多数据科学任务我宁愿用R语言而非Python完成。

    R属于高度专业化的语言,而Python更具通用性。

    坦白说,R语言未能实现进化。Python凭借Jupyter胜出——我在大学里随处可见它的身影。R语言虽仍在使用,但基本局限于统计相关课程。

    或许R在特定领域更胜一筹,但Python势头更猛,因而占据主导地位。这就是现实。它就像一辆高速前进的推土机。

    > 我说:“这很好,但能否快速换种方式绘制数据?”

    好吧…他得修改R代码对吧?而找到优质的修改指南实在太难了。他说自己有大学教学经验。我也是,但我的经验是人们对Python的掌握远胜于R。你会发现放弃R的学生远多于放弃Python的——这同样是现实情况。

    > 它们似乎足够繁琐或令人困惑,以至于我认为理应简单的请求往往难以实现。

    反之亦然。随便挑个Python库实现某个酷炫功能,再让R学生照做,我敢打赌他会遇到同样的问题。

    > 很多时候,我发现本该用几行简单R代码实现的功能,最终却变得冗长复杂。

    好了,这分明是在挑刺。我直说吧——他就是在故意找茬。

    我写过大量Python代码,也写过不少R代码。现实中90%的应用场景里,R代码绝不可能比Python代码更简洁。抱歉,事实就是如此。R语言更冗长。

    > 以下是采用tidyverse方法的R语言相关代码:

        penguins |>
          filter(!is.na(body_mass_g)) |>
          group_by(species, island) |>
          summarize(
    

    这简直像Perl。他们同样缺乏适应性。R注定会失去市场。

    这位教授尚未意识到,自己正因无法看清X优于Y的事实,而逐渐沦为活化石。

    1. > 坦白说,R语言未能进化。Python凭借Jupyter胜出

      Ju = Julia Pyt = Python Er = R

      R不仅在Jupyter中被支持,它从一开始就在那里。我从未写过一行R代码。人们对工具的认知如此匮乏,实在荒谬。

      1. 但它最初叫iPython(而且笔记本界面推出时它还叫iPython)。

        1. 没错。新增语言支持正是他们改名的原因之一。

  79. 拒绝学习工具反而怪工具有问题…Python在这方面毫无障碍。若厌恶模板化工作,加入自动化俱乐部让机器学习模型代劳,转而专注实质工作(或直接参与语言/库的改进)

  80. 我个人偶尔用R写脚本或做tidyverse快速处理。

    但这门语言存在诸多缺陷:
    1. 非标准的eval机制非常诡异,rlang库修正了这些缺陷
    2. 命名反直觉或函数不归属包的问题,基础库混杂着各类函数
    3. S3与命名混用——个人对S3无异议,S7更优,但混用S3名称与普通名称反直觉,应保持蛇形命名规范 4. data.frame设计反直觉,tidyverse已修正此缺陷 5. f(a=)这种写法?或在函数主体中处理离散范围参数时使用反直觉函数?6. 包内文件不支持独立导入,勉强能接受……但终究……
    7. AST函数缺乏直观性

    R语言存在诸多优秀特性:

    非标准评估机制、基础语言中的AST、惰性求值

    但这些优势正被缺陷部分所吞噬

    我认为所有外部修复和名称规范都应纳入基础库
    但由于遗留问题,即便实施也需耗费大量时间。

    Julia虽未如R般优雅地解决这些问题,但其实用主义方法实在太具吸引力。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

你也许感兴趣的: