我的电子表格使用规则

Hacker News Top 工具

摘要

Dr. Drang 分享了他使用电子表格的规则,主要建议不要使用它们,并基于他分析工程公司 Excel 数据的经验解释了例外情况。

暂无内容
查看原文
查看缓存全文

缓存时间: 2026/08/13 15:22

# 我使用电子表格的规则 来源:https://leancrew.com/all-this/2026/08/my-rules-for-using-spreadsheets/ 下一篇 (https://leancrew.com/all-this/2026/08/a-two-month-calendar-on-my-desktop/) 上一篇 (https://leancrew.com/all-this/2026/07/plotting-baseball-team-progress/) 2026年8月1日 上午10:29 by Dr. Drang 我的基本规则是**不要用**,但一个字可撑不起一篇博客文章。 在下面的内容中,我希望解释我是如何得出这条规则的,以及我为之破例的情况。最近我一直在思考我是如何使用、以及不使用电子表格的。这种反思的灵感部分来自Allison Sheridan在Macstock上的演讲 (https://macstockconferenceandexpo.com/schedule/#:~:text=How%20the%20Cool%20Kids%20Really%20Use%20Spreadsheets)(你可以在她的网站 (https://www.podfeet.com/blog/2026/07/macstock-2026-how-the-cool-kids-really-use-spreadsheets/) 上看到,旁边还有另外几篇 (https://www.podfeet.com/blog/2026/07/visicalc-ken-case/) 关于电子表格的近期文章 (https://www.podfeet.com/blog/2026/07/excel-find-dependencies/)),部分也源于我最近使用Numbers制作差分表 (https://leancrew.com/all-this/2026/07/sum-of-cubes-via-difference-tables/) 和清理数据表 (https://leancrew.com/all-this/2026/07/plotting-baseball-team-progress/) 的经历。 让我们先想想是什么让电子表格如此有吸引力。打开软件,映入眼帘的是一张由充当数据容器的单元格组成的网格。你不需要定义这些容器,不需要给它们命名,也不需要初始化它们——它们就那样在那儿,等着你随意填上内容。 当需要对数据进行操作时,你**仍然**不需要给单元格命名。你只需点击(或点击并拖动)来填入函数参数。电子表格应用会替你填好相应的行/列引用。如果你想提醒自己某个单元格是做什么用的,可以在相邻单元格里输入一个名称或描述。同样,你也不需要搞清楚操作的先后顺序。应用会理清单元格之间的依赖链,然后一次性重新计算所有东西,任何地方、所有内容,全都同时完成。 而且,由于你可以设置每个单元格的大小、颜色、边框和字体样式,你的电子表格可以生成好看的表格,用于插入报告、备忘录和幻灯片。 所以,电子表格既是数据存储工具,又是逻辑机器,还是演示工具。你明白了吗? 但如果电子表格这么全能,我的“**不要用**”规则又从何而来呢?来源有很多,但我必须说,我深受过去10到15年工作经历的影响。在那段时间里,我不得不分析几十个、甚至上百个数据集,而所有这些数据集都是以Excel电子表格的形式发给我的。给我发电子表格的工程公司不只是把它们当作数据存储工具。它们还包含了一些自己的分析(通常与我的分析略有重叠),而且他们把电子表格排版成表格,以便放入自己的报告。这让我的工作变得困难,原因如下: 1. 因为我必须确保自己理解并同意他们的分析,所以我得检查他们所有的公式。有些公式非常复杂——嵌套的`IF`语句在传统编程语言里容易阅读,但在电子表格里却是一团糟。有些公式不一致——同一张表的不同行会使用不同的公式,仿佛是由不同的人在不同时间写的,或者是从以前某个项目的电子表格里改编来的。有些公式引用的单元格在很远的地方,需要大量滚动才能找到。而这些公式中,没有一个——在过去十多年里一个都没有——使用单元格名称来让公式更容易理解。前面提到的复杂公式有时——不常发生,但确实有时——会包含错误。还有些时候公式是对的,但表头单元格里的描述却是错的。这意味着需要打电话来澄清分歧,进一步拖慢了分析进度。 2. 数据被拆分成两个或多个工作表是常见情况。我认为这主要是为了让表格更贴合其他工程师的报告排版,这对他们的目的来说没问题,但对我不是。我必须重新合并数据来进行分析。此外,这些工作表通常带有复杂的多行表头,这意味着我不能直接把它们导出为CSV文件。 3. 和我合作过的每一位工程师都用不同的方式构建他们的电子表格。那些为同一家公司工作的人并没有遵循某种“ABC工程公司”的通用样式。甚至同一个工程师在不同项目之间的电子表格风格也会变化。基本上,每一份进来的电子表格都是**独一无二的**,我必须手工完成所有数据清洗。这拖慢了我的速度,不仅因为这一步没法依赖自动化,还因为我必须反复检查我的工作,以避免复制/粘贴错误。 从根本上说,这段经历——尤其是第1条——让我对把电子表格用于任何大型或复杂的任务敬而远之。和我合作的工程师都很聪明,但他们的电子表格不是。我的结论是,典型的点击-拖动式电子表格搭建方式的简单性,随着表格变大或被改造以适配新数据,会助长糟糕的组织方式和错误。很容易说“哦,我绝不会那样做”,但我年纪够大了,知道**我**确实会那样做。我今天用各种应用可以轻松构建电子表格,而这种轻松在我看来就像塞壬的歌声,会诱惑我撞上礁石。 (如果你正准备写信给我谈Reinhart/Rogoff论文 (https://retractionwatch.com/2013/04/18/influential-reinhart-rogoff-economics-paper-suffers-database-error/),你可以放心了。它是初级电子表格错误的最佳例证——两位哈佛教授**肯定**绝不会犯的错误——它导致了太多人因不必要的政府紧缩政策而受苦。如果你现在又准备写信告诉我,Reinhart和Rogoff的错误并不否定他们结论的本质正确性,那你可以滚一边去了。) 数据和数据分析逻辑放在同一个文档里的便利性,当你需要把同样的逻辑应用到多个数据集时就成了问题,尤其是当这些数据集大小不同的时候。电子表格模板在数据允许你制作多个布局完全相同的电子表格时很好用,但我处理的数据往往不符合那种僵化模式。例如,如果我要分析并绘制多个时间序列,这些序列很少会跨越相同的时间长度、有相同的数据点数量。当逻辑独立于数据、放在程序里时,处理这些尺寸差异要容易得多。 电子表格的另一个问题是,它们能容纳的数据量,远不如使用其他数据分析工作流时那么多。电子表格的大小限制,诚然相当大,但在大数据时代,“相当大”可能还不够大。在Allison的Macstock演讲 (https://www.podfeet.com/blog/2026/07/macstock-2026-how-the-cool-kids-really-use-spreadsheets/) 中,她展示了自己是如何在美国婴儿名字数据集 (https://catalog.data.gov/dataset/baby-names-from-social-security-card-applications-national-data) 上遇到这个问题的。让我们绕个弯,谈谈如何处理那个数据。 --- 下载婴儿名字数据集的一种方式,是得到一个包含CSV文件的压缩包。压缩包里的每个文件对应一个年份,文件名类似`yob1960.txt`。内容看起来像这样: `` Mary,F,51472 Susan,F,39208 Linda,F,37316 Karen,F,36378 Donna,F,34138 [等等] `` 其中第一项是名字,第二项是出生时的性别,第三项是那一年里取这个名字的婴儿数量。各行先按性别、再按数量排序。如果你把所有文件拼接起来,会发现总共有2,181,032条记录。正如Allison发现的,这无法装进Excel电子表格,因为Excel限制为1,048,576行。那是一个非常计算机化的数字,即2的20次方或1024的平方。Numbers的限制则是没那么计算机化、但更有人情味的1,000,000行。 Allison绕过了大小问题,方法是……呃……作弊。她删除了不太流行的名字,以便列表能塞进Excel,然后演示了一些数据透视表操作。你可以在视频 (https://www.podfeet.com/blog/2026/07/macstock-2026-how-the-cool-kids-really-use-spreadsheets/) 的1:13:50处看到。 我决定做类似的工作,但不作弊。首先,我把所有单独的文件拼接成一个大的CSV文件,其中还包含一个年份字段。这是通过下面的shell命令完成的: `` echo 'Year,Name,Sex,Count' > all-years.csv for f in yob*.txt; do y=${f:3:4} sed -e "s/\r$//;s/^/$y,/" $f >> all-years.csv done `` 年份通过子串展开 (https://www.gnu.org/software/bash/manual/html_node/Shell-Parameter-Expansion.html#:~:text=Substring%20Expansion.)从文件名中提取出来,然后通过`sed`加到每一行的开头。原始文件是Windows格式,行尾是CRLF,所以`sed`命令也删除了CR字符。这一切的最终结果是得到一个(Unix换行符的)名为`all-years.csv`的文件,看起来像这样: `` Year,Name,Sex,Count 1880,Mary,F,7065 1880,Anna,F,2604 1880,Emma,F,2003 1880,Elizabeth,F,1939 1880,Minnie,F,1746 [等等] `` (是的,尽管这个数据集据称来自社保登记,但它始于1880年,比《社会保障法》早了几十年。我无法解释这一点。我也无法解释Minnie曾经是第五大流行的女孩名字。) 我将使用Python和Pandas (https://pandas.pydata.org/)来提取2001年到2025年(数据集的最后一年)最受欢迎的前五个女孩名字。这是一个简单的交互式Python会话的开头: `` >>> import pandas as pd >>> df = pd.read_csv('all-years.csv') >>> cols = ['Name', 'Count'] `` 这会把CSV文件读入一个数据框,并定义我们希望包含在输出中的数据框列。每行开头的`>>>`是交互式Python提示符。下面是我们如何得到感兴趣的名字列表: `` >>> df[(df.Sex=='F') & (df.Year>2000)][cols].groupby('Name')\ ... .sum().sort_values('Count', ascending=False)[:5] Count Name Emma 449576 Olivia 423613 Isabella 381577 Sophia 368619 Emily 353077 `` `...`表示续行输入。之后的内容都是输出。 通读这条命令,我们可以看到我们 1. 选取了2000年后出生的女孩数据子集; 2. 将输出限制为Name和Count字段; 3. 按Name分组输出; 4. 对每个Name的Count求和; 5. 按Count降序排序结果;以及 6. 将输出限制为前五个名字。 这显然是一条很长的命令,但你能看到它是以一种逻辑方式构建起来的。 如果我们想把这些和一个世纪前流行的女孩名字相比较,命令非常相似: `` >>> df[(df.Sex=='F') & (df.Year>1900) & (df.Year<=1925)][cols].groupby('Name')\ ... .sum().sort_values('Count', ascending=False)[:5] Count Name Mary 1056333 Helen 505522 Dorothy 475151 Margaret 402317 Ruth 364923 `` 我妻子和我都有一些姑奶奶、姨奶奶辈的人叫这些名字。 如果你是数据库高手,你会认出Pandas的`groupby`函数 (https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.groupby.html)是SQL的`GROUP BY`结构 (https://en.wikipedia.org/wiki/Group_by_(SQL))的翻版。让我们在SQLite (https://sqlite.org/)的交互式会话里重做这件事。我们首先从CSV文件导入数据: `` sqlite> .mode csv sqlite> .import all-years.csv names sqlite> .mode columns `` 将模式 (https://sqlite.org/climode.html)设为`csv`以便导入数据,然后再设回`columns`,让输出看起来符合我们的要求。 现在我们获取21世纪最受欢迎的前五个女孩名字,并按降序显示: `` sqlite> select Name, sum(Count) from names ...> where Sex is "F" and Year > 2000 ...> group by Name order by sum(Count) desc limit 5; Name sum(Count) -------- ---------- Emma 449576 Olivia 423613 Isabella 381577 Sophia 368619 Emily 353077 `` SQL当然更像英语,但你能看到它与Pandas之间的相似之处。现在是20世纪初: `` sqlite> select Name, sum(Count) from names ...> where Sex is "F" and Year > 1900 and Year <= 1925 ...> group by Name order by sum(Count) desc limit 5; Name sum(Count) -------- ---------- Mary 1056333 Helen 505522 Dorothy 475151 Margaret 402317 Ruth 364923 `` Allison用她那被截短了的Excel文件通过数据透视表 (https://en.wikipedia.org/wiki/Pivot_table)做了类似的事情。我讨厌“数据透视表”这个名字,因为我觉得对于分组和汇总这种简单操作来说,它是一个晦涩的术语。出于某种原因,我的感受并不重要,数据透视表已经成为常态。Pandas甚至添加了一个`pivot_table`函数 (https://pandas.pydata.org/docs/reference/api/pandas.pivot_table.html)来安抚从Excel转过来的人。在底层,`pivot_table`调用的是`groupby`。 --- 好吧,这次绕路有点长,如果你忘了我们之前说到哪儿了,我原谅你。我刚才在列举让我对使用电子表格保持警惕的那些事情——为什么我使用电子表格的第一条规则是“**不要用**”。 但我也允许例外。我的两个主要例外是: 1. 当问题足够小,几乎不用滚动就能在屏幕上完整看到,而且操作足够简单,即使不去数逗号和括号也能轻松理解。这就是我为立方和差分表 (https://leancrew.com/all-this/2026/07/sum-of-cubes-via-difference-tables/)所做的。公式主要由减法组成,偶尔有一些乘方和除法运算。只有右下角的联立方程求解涉及真正的函数调用,而且没有嵌套调用。立方和电子表格 2. 当我只是把电子表格当作一个中转站,在把数据传递到别处之前进行编辑。我在棒球队进展 (https://leancrew.com/all-this/2026/07/plotting-baseball-team-progress/)那篇帖子里就是这么做的:把Baseball Reference上那庞大笨拙的赛季结果表 (https://www.baseball-reference.com/teams/CHC/2026-schedule-scores.shtml)编辑精简。在Safari里选中表格、粘贴到Numbers、然后删除不需要的列和行,既快又方便。但我之所以用这种方式,只是因为这是一个一次性项目。如果给我的任务是整个赛季每天为全部30支球队绘制进展图表,我绝不会那样手工做。我会用Pandas的`read_html`函数 (https://pandas.pydata.org/docs/reference/api/pandas.read_html.html)把HTML表格拉进数据框,再用各种`drop`命令 (https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.drop.html#pandas.DataFrame.drop)来精简它。 我以前也用电子表格做数据录入,但现在不了。曾经,这是把书里找到的数字表格可靠地转成电子形式的唯一方式。但OCR已经变得好太多了,我都记不清上一次这么干是什么时候了。 我知道有很多人喜欢使用电子表格。他们花了很多时间学习各种细节,不想换到其他工具。这没问题。这篇帖子讲的是**我的**规则,不是其他人的。我并不是说电子表格做不出好的、准确的、复杂的工作。只是我自己不会去做而已。 下一篇 (https://leancrew.com/all-this/2026/08/a-two-month-calendar-on-my-desktop/)

相似文章