计算 R 中的小计

发布于 2024-09-30 22:36:35 字数 2605 浏览 2 评论 0原文

我在 R 中有一个包含 900,000 行和 11 列的数据框。列名称和类型如下:

column name: date / mcode / mname / ycode / yname / yissue  / bsent   / breturn / tsent   / treturn / csales
type:        Date / Char  / Char  / Char  / Char  / Numeric / Numeric / Numeric / Numeric / Numeric / Numeric

我想计算小计。例如,我想计算 yname 每次更改时的总和,并将小计添加到所有数值变量中。有 160 个不同的 yname,因此结果表应该告诉我每个 yname 的小计。我还没有对数据进行排序,但这不是问题,因为我可以按照我想要的任何方式对数据进行排序。以下是我的数据片段:

             date     mcode mname            ycode    yname   yissue bsent breturn tsent treturn csales
417572 2010-07-28     45740 ENDPOINT A        5772    XMAG  20100800     7       0     7       0      0
417573 2010-07-31     45740 ENDPOINT A        5772    XMAG  20100800     0       0     0       0      1
417574 2010-08-04     45740 ENDPOINT A        5772    XMAG  20100800     0       0     0       0      1
417575 2010-08-14     45740 ENDPOINT A        5772    XMAG  20100800     0       0     0       0      1
417576 2010-08-26     45740 ENDPOINT A        5772    XMAG  20100800     0       4     0       0      0
417577 2010-07-28     45741 ENDPOINT L        5772    XMAG  20100800     2       0     2       0      0
417578 2010-08-04     45741 ENDPOINT L        5772    XMAG  20100800     2       0     2       0      0
417579 2010-08-26     45741 ENDPOINT L        5772    XMAG  20100800     0       4     0       0      0
417580 2010-07-28     46390 ENDPOINT R        5772    XMAG  20100800     3       0     3       0      1
417581 2010-07-29     46390 ENDPOINT R        5772    XMAG  20100800     0       0     0       0      2
417582 2010-08-01     46390 ENDPOINT R        5779    YMAG  20100800     3       0     3       0      0
417583 2010-08-11     46390 ENDPOINT R        5779    YMAG  20100800     0       0     0       0      1
417584 2010-08-20     46390 ENDPOINT R        5779    YMAG  20100800     0       0     0       0      1
417585 2010-08-24     46390 ENDPOINT R        5779    YMAG  20100800     2       0     2       0      1
417586 2010-08-26     46390 ENDPOINT R        5779    YMAG  20100800     0       2     0       2      0
417587 2010-07-28     46411 ENDPOINT D        5779    YMAG  20100800     6       0     6       0      0
417588 2010-08-08     46411 ENDPOINT D        5779    YMAG  20100800     0       0     0       0      1
417589 2010-08-11     46411 ENDPOINT D        5779    YMAG  20100800     0       0     0       0      1
417590 2010-08-26     46411 ENDPOINT D        5779    YMAG  20100800     0       4     0       4      0

我应该在这里使用什么函数?也许类似于 SQL group by 之类的东西?

I have a data frame with 900,000 rows and 11 columns in R. The column names and types are as follows:

column name: date / mcode / mname / ycode / yname / yissue  / bsent   / breturn / tsent   / treturn / csales
type:        Date / Char  / Char  / Char  / Char  / Numeric / Numeric / Numeric / Numeric / Numeric / Numeric

I want to calculate the subtotals. For example, I want to calculate the sums at each change in yname, and add subtotal to all numerical variables. There are 160 distinct ynames, so the resulting table should tell me the subtotal of each yname. I haven't sorted the data yet, but this is not a problem because I can sort the data in whatever way I want. Below is a snippet from my data:

             date     mcode mname            ycode    yname   yissue bsent breturn tsent treturn csales
417572 2010-07-28     45740 ENDPOINT A        5772    XMAG  20100800     7       0     7       0      0
417573 2010-07-31     45740 ENDPOINT A        5772    XMAG  20100800     0       0     0       0      1
417574 2010-08-04     45740 ENDPOINT A        5772    XMAG  20100800     0       0     0       0      1
417575 2010-08-14     45740 ENDPOINT A        5772    XMAG  20100800     0       0     0       0      1
417576 2010-08-26     45740 ENDPOINT A        5772    XMAG  20100800     0       4     0       0      0
417577 2010-07-28     45741 ENDPOINT L        5772    XMAG  20100800     2       0     2       0      0
417578 2010-08-04     45741 ENDPOINT L        5772    XMAG  20100800     2       0     2       0      0
417579 2010-08-26     45741 ENDPOINT L        5772    XMAG  20100800     0       4     0       0      0
417580 2010-07-28     46390 ENDPOINT R        5772    XMAG  20100800     3       0     3       0      1
417581 2010-07-29     46390 ENDPOINT R        5772    XMAG  20100800     0       0     0       0      2
417582 2010-08-01     46390 ENDPOINT R        5779    YMAG  20100800     3       0     3       0      0
417583 2010-08-11     46390 ENDPOINT R        5779    YMAG  20100800     0       0     0       0      1
417584 2010-08-20     46390 ENDPOINT R        5779    YMAG  20100800     0       0     0       0      1
417585 2010-08-24     46390 ENDPOINT R        5779    YMAG  20100800     2       0     2       0      1
417586 2010-08-26     46390 ENDPOINT R        5779    YMAG  20100800     0       2     0       2      0
417587 2010-07-28     46411 ENDPOINT D        5779    YMAG  20100800     6       0     6       0      0
417588 2010-08-08     46411 ENDPOINT D        5779    YMAG  20100800     0       0     0       0      1
417589 2010-08-11     46411 ENDPOINT D        5779    YMAG  20100800     0       0     0       0      1
417590 2010-08-26     46411 ENDPOINT D        5779    YMAG  20100800     0       4     0       4      0

What function should I use here? Maybe something like SQL group by?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

橪书 2024-10-07 22:36:35

好的。假设您的数据位于名为 foo 的数据框中:

> head(foo)
             date mcode      mname ycode yname   yissue bsent breturn tsent
417572 2010/07/28 45740 ENDPOINT A  5772  XMAG 20100800     7       0     7
417573 2010/07/31 45740 ENDPOINT A  5772  XMAG 20100800     0       0     0
417574 2010/08/04 45740 ENDPOINT A  5772  XMAG 20100800     0       0     0
417575 2010/08/14 45740 ENDPOINT A  5772  XMAG 20100800     0       0     0
417576 2010/08/26 45740 ENDPOINT A  5772  XMAG 20100800     0       4     0
417577 2010/07/28 45741 ENDPOINT L  5772  XMAG 20100800     2       0     2
       treturn csales
417572       0      0
417573       0      1
417574       0      1
417575       0      1
417576       0      0
417577       0      0

那么这将对数据中的数字列进行聚合:

> aggregate(cbind(bsent, breturn, tsent, treturn, csales) ~ yname, data = foo, 
+           FUN = sum)
  yname bsent breturn tsent treturn csales
1  XMAG    14       8    14       0      6
2  YMAG    11       6    11       6      5

这是使用您在 Q 中包含的数据片段。我使用了公式界面到aggregate(),在这种情况下更好一些,因为您不需要希望聚合的变量名称上的所有foo$ 位。如果完整数据集中缺少数据 (NA),则需要添加一个额外参数 na.rm = TRUE,该参数将传递给 sum(),像这样:

> aggregate(cbind(bsent, breturn, tsent, treturn, csales) ~ yname, data = foo, 
+           FUN = sum, na.rm = TRUE)

OK. Assuming your data are in a data frame named foo:

> head(foo)
             date mcode      mname ycode yname   yissue bsent breturn tsent
417572 2010/07/28 45740 ENDPOINT A  5772  XMAG 20100800     7       0     7
417573 2010/07/31 45740 ENDPOINT A  5772  XMAG 20100800     0       0     0
417574 2010/08/04 45740 ENDPOINT A  5772  XMAG 20100800     0       0     0
417575 2010/08/14 45740 ENDPOINT A  5772  XMAG 20100800     0       0     0
417576 2010/08/26 45740 ENDPOINT A  5772  XMAG 20100800     0       4     0
417577 2010/07/28 45741 ENDPOINT L  5772  XMAG 20100800     2       0     2
       treturn csales
417572       0      0
417573       0      1
417574       0      1
417575       0      1
417576       0      0
417577       0      0

Then this will do the aggregation of the numeric columns in your data:

> aggregate(cbind(bsent, breturn, tsent, treturn, csales) ~ yname, data = foo, 
+           FUN = sum)
  yname bsent breturn tsent treturn csales
1  XMAG    14       8    14       0      6
2  YMAG    11       6    11       6      5

That was using the snippet of data you included in your Q. I used the formula interface to aggregate(), which is a bit nicer in this instance because you don't need all the foo$ bits on the variable names you wish the aggregate. If you have missing data (NA)in your full data set, then you'll need add an extra argument na.rm = TRUE which will get passed to sum(), like so:

> aggregate(cbind(bsent, breturn, tsent, treturn, csales) ~ yname, data = foo, 
+           FUN = sum, na.rm = TRUE)
诗化ㄋ丶相逢 2024-10-07 22:36:35

当我试图找到类似问题的答案时,谷歌并没有多大帮助。我想我应该使用带有 split()purrr::map_df()library(janitor) 包来分享下面的解决方案。

我的用例是运行一个脚本,该脚本可以从许多不同的人那里获取 CC 费用,以便由一个人进行审查。


library(janitor)
library(purrr)
library(dplyr)

mtcars %>% 
  split(.[,"cyl"]) %>% ## splits each change in cyl into a list of dataframes 
  map_df(., janitor::adorn_totals)

   mpg cyl   disp   hp  drat     wt   qsec vs am gear carb
  22.8   4  108.0   93  3.85  2.320  18.61  1  1    4    1
  24.4   4  146.7   62  3.69  3.190  20.00  1  0    4    2
  22.8   4  140.8   95  3.92  3.150  22.90  1  0    4    2
  32.4   4   78.7   66  4.08  2.200  19.47  1  1    4    1
  30.4   4   75.7   52  4.93  1.615  18.52  1  1    4    2
  33.9   4   71.1   65  4.22  1.835  19.90  1  1    4    1
  21.5   4  120.1   97  3.70  2.465  20.01  1  0    3    1
  27.3   4   79.0   66  4.08  1.935  18.90  1  1    4    1
    26   4  120.3   91  4.43  2.140  16.70  0  1    5    2
  30.4   4   95.1  113  3.77  1.513  16.90  1  1    5    2
  21.4   4  121.0  109  4.11  2.780  18.60  1  1    4    2
 Total  44 1156.5  909 44.78 25.143 210.51 10  8   45   17
    21   6  160.0  110  3.90  2.620  16.46  0  1    4    4
    21   6  160.0  110  3.90  2.875  17.02  0  1    4    4
  21.4   6  258.0  110  3.08  3.215  19.44  1  0    3    1
  18.1   6  225.0  105  2.76  3.460  20.22  1  0    3    1
  19.2   6  167.6  123  3.92  3.440  18.30  1  0    4    4
  17.8   6  167.6  123  3.92  3.440  18.90  1  0    4    4
  19.7   6  145.0  175  3.62  2.770  15.50  0  1    5    6
 Total  42 1283.2  856 25.10 21.820 125.84  4  3   27   24
  18.7   8  360.0  175  3.15  3.440  17.02  0  0    3    2
  14.3   8  360.0  245  3.21  3.570  15.84  0  0    3    4
  16.4   8  275.8  180  3.07  4.070  17.40  0  0    3    3
  17.3   8  275.8  180  3.07  3.730  17.60  0  0    3    3
  15.2   8  275.8  180  3.07  3.780  18.00  0  0    3    3
  10.4   8  472.0  205  2.93  5.250  17.98  0  0    3    4
  10.4   8  460.0  215  3.00  5.424  17.82  0  0    3    4
  14.7   8  440.0  230  3.23  5.345  17.42  0  0    3    4
  15.5   8  318.0  150  2.76  3.520  16.87  0  0    3    2
  15.2   8  304.0  150  3.15  3.435  17.30  0  0    3    2
  13.3   8  350.0  245  3.73  3.840  15.41  0  0    3    4
  19.2   8  400.0  175  3.08  3.845  17.05  0  0    3    2
  15.8   8  351.0  264  4.22  3.170  14.50  0  1    5    4
    15   8  301.0  335  3.54  3.570  14.60  0  1    5    8
 Total 112 4943.4 2929 45.21 55.989 234.81  0  2   46   49


# if you're sending the output to be reviewed by a person, add a row! 

mtcars %>% 
  split(.[,"cyl"]) %>% 
  map_df(., ~janitor::adorn_totals(.x) %>% 
           dplyr::add_row()) %>% 
  write.csv(., "demo.csv")

Google wasn't super helpful when I tried to find an answer to a similar question. I thought I would share my solution below using the library(janitor) package with split(), and purrr::map_df().

My use case was to run a script that would grab CC expenses from many different people to be reviewed by a person.


library(janitor)
library(purrr)
library(dplyr)

mtcars %>% 
  split(.[,"cyl"]) %>% ## splits each change in cyl into a list of dataframes 
  map_df(., janitor::adorn_totals)

   mpg cyl   disp   hp  drat     wt   qsec vs am gear carb
  22.8   4  108.0   93  3.85  2.320  18.61  1  1    4    1
  24.4   4  146.7   62  3.69  3.190  20.00  1  0    4    2
  22.8   4  140.8   95  3.92  3.150  22.90  1  0    4    2
  32.4   4   78.7   66  4.08  2.200  19.47  1  1    4    1
  30.4   4   75.7   52  4.93  1.615  18.52  1  1    4    2
  33.9   4   71.1   65  4.22  1.835  19.90  1  1    4    1
  21.5   4  120.1   97  3.70  2.465  20.01  1  0    3    1
  27.3   4   79.0   66  4.08  1.935  18.90  1  1    4    1
    26   4  120.3   91  4.43  2.140  16.70  0  1    5    2
  30.4   4   95.1  113  3.77  1.513  16.90  1  1    5    2
  21.4   4  121.0  109  4.11  2.780  18.60  1  1    4    2
 Total  44 1156.5  909 44.78 25.143 210.51 10  8   45   17
    21   6  160.0  110  3.90  2.620  16.46  0  1    4    4
    21   6  160.0  110  3.90  2.875  17.02  0  1    4    4
  21.4   6  258.0  110  3.08  3.215  19.44  1  0    3    1
  18.1   6  225.0  105  2.76  3.460  20.22  1  0    3    1
  19.2   6  167.6  123  3.92  3.440  18.30  1  0    4    4
  17.8   6  167.6  123  3.92  3.440  18.90  1  0    4    4
  19.7   6  145.0  175  3.62  2.770  15.50  0  1    5    6
 Total  42 1283.2  856 25.10 21.820 125.84  4  3   27   24
  18.7   8  360.0  175  3.15  3.440  17.02  0  0    3    2
  14.3   8  360.0  245  3.21  3.570  15.84  0  0    3    4
  16.4   8  275.8  180  3.07  4.070  17.40  0  0    3    3
  17.3   8  275.8  180  3.07  3.730  17.60  0  0    3    3
  15.2   8  275.8  180  3.07  3.780  18.00  0  0    3    3
  10.4   8  472.0  205  2.93  5.250  17.98  0  0    3    4
  10.4   8  460.0  215  3.00  5.424  17.82  0  0    3    4
  14.7   8  440.0  230  3.23  5.345  17.42  0  0    3    4
  15.5   8  318.0  150  2.76  3.520  16.87  0  0    3    2
  15.2   8  304.0  150  3.15  3.435  17.30  0  0    3    2
  13.3   8  350.0  245  3.73  3.840  15.41  0  0    3    4
  19.2   8  400.0  175  3.08  3.845  17.05  0  0    3    2
  15.8   8  351.0  264  4.22  3.170  14.50  0  1    5    4
    15   8  301.0  335  3.54  3.570  14.60  0  1    5    8
 Total 112 4943.4 2929 45.21 55.989 234.81  0  2   46   49


# if you're sending the output to be reviewed by a person, add a row! 

mtcars %>% 
  split(.[,"cyl"]) %>% 
  map_df(., ~janitor::adorn_totals(.x) %>% 
           dplyr::add_row()) %>% 
  write.csv(., "demo.csv")

梦言归人 2024-10-07 22:36:35

或者是 plyr 库,它可以轻松扩展到其他数据类:

> library(plyr)
> result.2 <- ddply(df$a, .(df$b), sum)
> result.2
  df.b V1
1 down 30
2   up 25

Or the plyr library, which is easily extensible to other data classes:

> library(plyr)
> result.2 <- ddply(df$a, .(df$b), sum)
> result.2
  df.b V1
1 down 30
2   up 25
冷默言语 2024-10-07 22:36:35

您还可以使用 xtabstapply

xtabs(cbind(bsent, breturn, tsent, treturn, csales) ~ yname, data)

tapply(data$bsent, data$yname, sum)

You can also use xtabs or tapply:

xtabs(cbind(bsent, breturn, tsent, treturn, csales) ~ yname, data)

tapply(data$bsent, data$yname, sum)
倾城花音 2024-10-07 22:36:35

如果您的数据很大并且速度很重要,我建议使用 R 函数 rowsum,它要快得多。我应用了答案中建议的 3 种方法(f1=aggregate、f2=ddply、f3=tapply),将其与 f4=rowsum 进行比较,这就是我发现的:

   test replications elapsed relative
4 f4()          100   0.033     1.00
3 f3()          100   0.046     1.39
1 f1()          100   0.165     5.00
2 f2()          100   0.605    18.33

如果有人想更详细地探索,我在下面添加了我的代码。

library(plyr);
library(rbenchmark);

val  = rnorm(50);
name = rep(letters[1:5], each = 10);
data = data.frame(val, name);

f1 = function(){aggregate(data$val, by=list(data$name), FUN=sum)}
f2 = function(){ddply(data, .(name), summarise, sum = sum(val))}
f3 = function(){tapply(data$val, data$name, sum)}
f4 = function(){rowsum(x = data$val, group = data$name)}

benchmark(f1(), f2(), f3(), f4(),
          columns=c("test", "replications", "elapsed", "relative"),
          order="relative", replications=100)

if your data is large and speed matters, i would recommend using the R function rowsum, which is a lot faster. i applied the 3 methods (f1 = aggregate, f2 = ddply, f3 = tapply) suggested in the answers to compare it with f4 = rowsum and here is what i find:

   test replications elapsed relative
4 f4()          100   0.033     1.00
3 f3()          100   0.046     1.39
1 f1()          100   0.165     5.00
2 f2()          100   0.605    18.33

i have added my code below if someone wants to explore in more detail.

library(plyr);
library(rbenchmark);

val  = rnorm(50);
name = rep(letters[1:5], each = 10);
data = data.frame(val, name);

f1 = function(){aggregate(data$val, by=list(data$name), FUN=sum)}
f2 = function(){ddply(data, .(name), summarise, sum = sum(val))}
f3 = function(){tapply(data$val, data$name, sum)}
f4 = function(){rowsum(x = data$val, group = data$name)}

benchmark(f1(), f2(), f3(), f4(),
          columns=c("test", "replications", "elapsed", "relative"),
          order="relative", replications=100)
愛放△進行李 2024-10-07 22:36:35

你可以使用aggregate

例如,假设你有

val = rnorm(50)
name = rep(letters[1:5], each=10)
data <- data.frame(val, name)

那么你可以这样做

aggregate(data$val, by=list(data$name), FUN=sum)

You can use aggregate

For instance, say that you have

val = rnorm(50)
name = rep(letters[1:5], each=10)
data <- data.frame(val, name)

Then you can do

aggregate(data$val, by=list(data$name), FUN=sum)
半寸时光 2024-10-07 22:36:35

有一个名为 sqldf 的 R 包,使您能够在 R data.frames 上使用 SQL 命令。
除了像你已经说过的那样,GROUP BY 会很好。您可以轻松地将数据存储在本地 MySQL 数据库中,并使用 RMySQL 包连接到 R(您也可以使用大多数其他 DBMS,但 MySQL 是最容易设置的)。

据我判断,plyr 也是一个很棒的软件包。但从你提出问题并将问题与 GROUP BY 进行比较的方式来看,我猜你对 SQL 有所了解,所以使用它对你来说可能更容易。有一些舒适的函数,例如 dbReadTable,此外,如果您的数据变得更大,您可以仅选择数据的子部分,以便只运行您真正需要的分析。

There is a R package called sqldf that enables you to use SQL commands on R data.frames.
Besides like you already said, GROUP BY would be nice. You can easily store your data in a local MySQL database and connect to R using the package RMySQL (You can use most other DBMS too but MySQL is the easiest to set up).

As far as I can judge it plyr is a great package, too. But from the way you ask and compare your problem to GROUP BY, I guess you know something about SQL, so using this might be easier for you. There are comfortable functions like dbReadTable, plus if your data grows bigger you can select only subparts of your data to only run your analysis with what you really need.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文