我有一个较大的人口普查/BLS 数据集,其中包含大约 1000 万人的记录,每条记录大约有 250 个变量,包括缺失值。其中许多价值都是美元金额。我怀疑,但不知道,最右边的数字不成比例地是零。我想计算从右边开始的连续零的数量,并排除小数点后的任何内容。我想生成一个表,显示不同数量的零出现的频率,另一个表显示不同数字作为最右边的非零数字出现的频率。
如果我的数字是
13,568,700
449,000
43,560
20,010
34,600
32,620
我想要的桌子是 对于零:
3. 1
2. 2
1. 3
对于最右边的非零数字:
digit. count
1. 1
2. 1
6. 2
7. 1
9. 1
我有一个函数可以对单个数字执行此操作并适当增加一些计数器,但它根本不是矢量化的,而且速度慢得令人无法接受。因为有很多变量可能会针对每个人进行四舍五入,所以如果我只对每个数字运行该函数一次,我需要运行大约 500,000,000 次。如果我想要最左边的数字,则很容易矢量化,但我无法为右侧零的数量或最右侧非零数字制定矢量化算法。
如果有人比我更聪明,或者至少比我更有知识,我会很感激。
从概念证明等不太快的事情开始:
n = 5E7
set.seed(42)
library(tidyverse)
df1 <- tibble(signif = rpois(n, lambda = 2),
x = floor(runif(n, max = 1000)) * 10^signif)
# Here I'll start with 50M rows
# (note, my fake data is a little misleading because it will understate some
# significant digits, e.g. when runif produces a number ending with zeroes.
tictoc::tic()
df2 <- df1 |>
mutate(v1 = format(x, trim = TRUE, scientific = FALSE),
zeroes = nchar(x) - nchar(sub("0*$", "", format(x, trim = TRUE, scientific = FALSE))),
right_dig = (x %/% (10^zeroes)) %% 10)
tictoc::toc()
这在我的机器上大约需要 90 秒,而你需要 10 倍的时间和多个列,因此还有很大的改进空间,但在紧要关头也许可以达到临界值。
结果
# A tibble: 50,000,000 × 5
signif x v1 zeroes right_dig
<int> <dbl> <chr> <int> <dbl>
1 4 9020000 9020000 4 2
2 4 3770000 3770000 4 7
3 1 5370 5370 1 7
4 3 540000 540000 4 4
5 2 95900 95900 2 9
6 2 33700 33700 2 7
7 3 656000 656000 3 6
8 0 152 152 0 2
9 2 43100 43100 2 1
10 3 659000 659000 3 9
# ℹ 49,999,990 more rows
# ℹ Use `print(n = ...)` to see more rows
然后就可以直接快速地运行这些:
df2 %>%
count(zeroes)
# A tibble: 6 × 2
zeroes n
<int> <int>
1 0 6094178
2 1 12837105
3 2 13457359
4 3 9459420
5 4 5228366
6 5 2923572
df2 %>%
count(right_dig)
# A tibble: 10 × 2
right_dig n
<dbl> <int>
1 0 1234711
2 1 5419854
3 2 5419492
4 3 5418213
5 4 5417654
6 5 5414904
7 6 5418602
8 7 5417624
9 8 5419975
10 9 5418971