Skip to content

Commit a624521

Browse files
authored
Preserve type of row names in vec_ptype() (#1050)
1 parent 745ab1a commit a624521

File tree

4 files changed

+76
-47
lines changed

4 files changed

+76
-47
lines changed

src/type.c

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
#include "vctrs.h"
2-
#include "utils.h"
2+
#include "arg-counter.h"
33
#include "ptype-common.h"
44
#include "ptype2.h"
5-
#include "arg-counter.h"
5+
#include "type-data-frame.h"
6+
#include "utils.h"
67

78
// Initialised at load time
89
static SEXP syms_vec_ptype_finalise_dispatch = NULL;
@@ -11,6 +12,7 @@ static SEXP fns_vec_ptype_finalise_dispatch = NULL;
1112

1213
static inline SEXP vec_ptype_slice(SEXP x, SEXP empty);
1314
static SEXP s3_type(SEXP x, struct vctrs_arg* x_arg);
15+
static SEXP df_ptype(SEXP x, bool bare);
1416

1517
// [[ register() ]]
1618
SEXP vctrs_ptype(SEXP x, SEXP x_arg) {
@@ -32,7 +34,7 @@ SEXP vec_ptype(SEXP x, struct vctrs_arg* x_arg) {
3234
case vctrs_type_character: return vec_ptype_slice(x, vctrs_shared_empty_chr);
3335
case vctrs_type_raw: return vec_ptype_slice(x, vctrs_shared_empty_raw);
3436
case vctrs_type_list: return vec_ptype_slice(x, vctrs_shared_empty_list);
35-
case vctrs_type_dataframe: return bare_df_map(x, &col_ptype);
37+
case vctrs_type_dataframe: return df_ptype(x, true);
3638
case vctrs_type_s3: return s3_type(x, x_arg);
3739
case vctrs_type_scalar: stop_scalar_type(x, x_arg);
3840
}
@@ -54,10 +56,10 @@ static inline SEXP vec_ptype_slice(SEXP x, SEXP empty) {
5456
static SEXP s3_type(SEXP x, struct vctrs_arg* x_arg) {
5557
switch (class_type(x)) {
5658
case vctrs_class_bare_tibble:
57-
return bare_df_map(x, &col_ptype);
59+
return df_ptype(x, true);
5860

5961
case vctrs_class_data_frame:
60-
return df_map(x, &col_ptype);
62+
return df_ptype(x, false);
6163

6264
case vctrs_class_bare_data_frame:
6365
Rf_errorcall(R_NilValue, "Internal error: Bare data frames should be handled by `vec_ptype()`");
@@ -77,6 +79,24 @@ static SEXP s3_type(SEXP x, struct vctrs_arg* x_arg) {
7779
return vec_slice(x, R_NilValue);
7880
}
7981

82+
SEXP df_ptype(SEXP x, bool bare) {
83+
SEXP row_nms = PROTECT(df_rownames(x));
84+
85+
SEXP ptype = R_NilValue;
86+
if (bare) {
87+
ptype = PROTECT(bare_df_map(x, &col_ptype));
88+
} else {
89+
ptype = PROTECT(df_map(x, &col_ptype));
90+
}
91+
92+
if (TYPEOF(row_nms) == STRSXP) {
93+
Rf_setAttrib(ptype, R_RowNamesSymbol, vctrs_shared_empty_chr);
94+
}
95+
96+
UNPROTECT(2);
97+
return ptype;
98+
}
99+
80100
static SEXP vec_ptype_finalise_unspecified(SEXP x);
81101
static SEXP vec_ptype_finalise_dispatch(SEXP x);
82102

tests/testthat/test-type-dplyr.R

Lines changed: 43 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,51 +1,53 @@
11

22
# `grouped_df` -------------------------------------------------------
33

4+
bare_mtcars <- unrownames(mtcars)
5+
46
test_that("grouped-df is proxied and restored", {
5-
gdf <- dplyr::group_by(mtcars, cyl)
7+
gdf <- dplyr::group_by(bare_mtcars, cyl)
68

79
expect_identical(vec_proxy(gdf), gdf)
8-
expect_identical(vec_restore(mtcars, gdf), gdf)
10+
expect_identical(vec_restore(bare_mtcars, gdf), gdf)
911

1012
expect_identical(vec_ptype(gdf), gdf[0, ])
1113

12-
gdf <- dplyr::group_by(mtcars, cyl, am, vs)
14+
gdf <- dplyr::group_by(bare_mtcars, cyl, am, vs)
1315
expect_identical(gdf[0, ], vec_ptype(gdf))
1416

15-
out <- vec_ptype(dplyr::group_by(mtcars, cyl, .drop = FALSE))
17+
out <- vec_ptype(dplyr::group_by(bare_mtcars, cyl, .drop = FALSE))
1618
expect_drop(out, FALSE)
1719
})
1820

1921
test_that("can take the common type of grouped tibbles and tibbles", {
20-
gdf <- dplyr::group_by(mtcars, cyl)
22+
gdf <- dplyr::group_by(bare_mtcars, cyl)
2123
expect_identical(vec_ptype2(gdf, data.frame()), vec_ptype(gdf))
2224
expect_identical(vec_ptype2(data.frame(), gdf), vec_ptype(gdf))
2325
expect_identical(vec_ptype2(gdf, tibble()), vec_ptype(gdf))
2426
expect_identical(vec_ptype2(tibble(), gdf), vec_ptype(gdf))
2527

26-
gdf_nodrop <- dplyr::group_by(mtcars, cyl, .drop = FALSE)
28+
gdf_nodrop <- dplyr::group_by(bare_mtcars, cyl, .drop = FALSE)
2729
expect_drop(vec_ptype2(gdf, gdf_nodrop), FALSE)
2830
expect_drop(vec_ptype2(gdf_nodrop, gdf), FALSE)
29-
expect_drop(vec_ptype2(gdf_nodrop, mtcars), FALSE)
30-
expect_drop(vec_ptype2(mtcars, gdf_nodrop), FALSE)
31+
expect_drop(vec_ptype2(gdf_nodrop, bare_mtcars), FALSE)
32+
expect_drop(vec_ptype2(bare_mtcars, gdf_nodrop), FALSE)
3133
})
3234

3335
test_that("the common type of grouped tibbles includes the union of grouping variables", {
34-
gdf1 <- dplyr::group_by(mtcars, cyl)
35-
gdf2 <- dplyr::group_by(mtcars, am, vs)
36+
gdf1 <- dplyr::group_by(bare_mtcars, cyl)
37+
gdf2 <- dplyr::group_by(bare_mtcars, am, vs)
3638
expect_identical(
3739
vec_ptype2(gdf1, gdf2),
38-
vec_ptype(dplyr::group_by(mtcars, cyl, am, vs))
40+
vec_ptype(dplyr::group_by(bare_mtcars, cyl, am, vs))
3941
)
4042
})
4143

4244
test_that("can cast to and from `grouped_df`", {
43-
gdf <- dplyr::group_by(unrownames(mtcars), cyl)
44-
input <- mtcars[10]
45-
cast_gdf <- dplyr::group_by(vec_cast(mtcars[10], mtcars), cyl)
45+
gdf <- dplyr::group_by(unrownames(bare_mtcars), cyl)
46+
input <- bare_mtcars[10]
47+
cast_gdf <- dplyr::group_by(vec_cast(bare_mtcars[10], bare_mtcars), cyl)
4648

4749
expect_error(
48-
vec_cast(input, dplyr::group_by(mtcars["cyl"], cyl)),
50+
vec_cast(input, dplyr::group_by(bare_mtcars["cyl"], cyl)),
4951
class = "vctrs_error_cast_lossy"
5052
)
5153

@@ -54,15 +56,15 @@ test_that("can cast to and from `grouped_df`", {
5456
cast_gdf
5557
)
5658
expect_identical(
57-
vec_cast(gdf, mtcars),
58-
unrownames(mtcars)
59+
vec_cast(gdf, bare_mtcars),
60+
unrownames(bare_mtcars)
5961
)
6062

6163
expect_identical(
6264
vec_cast(tibble::as_tibble(input), gdf),
6365
unrownames(cast_gdf)
6466
)
65-
tib <- tibble::as_tibble(mtcars)
67+
tib <- tibble::as_tibble(bare_mtcars)
6668
expect_identical(
6769
unrownames(vec_cast(gdf, tib)),
6870
tib
@@ -71,60 +73,60 @@ test_that("can cast to and from `grouped_df`", {
7173

7274
test_that("casting to `grouped_df` doesn't require grouping variables", {
7375
expect_identical(
74-
vec_cast(mtcars[10], dplyr::group_by(mtcars, cyl)),
75-
dplyr::group_by(vec_cast(mtcars[10], mtcars), cyl)
76+
vec_cast(bare_mtcars[10], dplyr::group_by(bare_mtcars, cyl)),
77+
dplyr::group_by(vec_cast(bare_mtcars[10], bare_mtcars), cyl)
7678
)
7779
})
7880

7981
test_that("casting to `grouped_df` handles `drop`", {
80-
gdf_nodrop <- dplyr::group_by(mtcars, cyl, .drop = FALSE)
81-
expect_identical(vec_cast(mtcars, gdf_nodrop), gdf_nodrop)
82+
gdf_nodrop <- dplyr::group_by(bare_mtcars, cyl, .drop = FALSE)
83+
expect_identical(vec_cast(bare_mtcars, gdf_nodrop), gdf_nodrop)
8284
})
8385

8486
test_that("can cbind grouped data frames", {
85-
gdf <- dplyr::group_by(mtcars[-10], cyl)
86-
df <- unrownames(mtcars)[10]
87+
gdf <- dplyr::group_by(bare_mtcars[-10], cyl)
88+
df <- unrownames(bare_mtcars)[10]
8789

8890
expect_identical(
8991
unrownames(vec_cbind(gdf, df)),
90-
tibble::as_tibble(mtcars)[c(1:9, 11, 10)]
92+
tibble::as_tibble(bare_mtcars)[c(1:9, 11, 10)]
9193
)
9294

93-
gdf1 <- dplyr::group_by(mtcars[2], cyl)
94-
gdf2 <- dplyr::group_by(mtcars[8:9], vs, am)
95+
gdf1 <- dplyr::group_by(bare_mtcars[2], cyl)
96+
gdf2 <- dplyr::group_by(bare_mtcars[8:9], vs, am)
9597
expect_identical(
9698
unrownames(vec_cbind(gdf1, gdf2)),
97-
tibble::as_tibble(mtcars)[c(2, 8, 9)]
99+
tibble::as_tibble(bare_mtcars)[c(2, 8, 9)]
98100
)
99101
})
100102

101103

102104
# `rowwise` ----------------------------------------------------------
103105

104106
test_that("rowwise can be proxied and restored", {
105-
rww <- dplyr::rowwise(unrownames(mtcars))
107+
rww <- dplyr::rowwise(unrownames(bare_mtcars))
106108

107109
expect_identical(vec_proxy(rww), rww)
108-
expect_identical(vec_restore(unrownames(mtcars), rww), rww)
110+
expect_identical(vec_restore(unrownames(bare_mtcars), rww), rww)
109111

110112
expect_identical(vec_ptype(rww), rww[0, ])
111113
})
112114

113115
test_that("can take the common type of rowwise tibbles and tibbles", {
114-
rww <- dplyr::rowwise(mtcars)
116+
rww <- dplyr::rowwise(bare_mtcars)
115117
expect_identical(vec_ptype2(rww, data.frame()), vec_ptype(rww))
116118
expect_identical(vec_ptype2(data.frame(), rww), vec_ptype(rww))
117119
expect_identical(vec_ptype2(rww, tibble()), vec_ptype(rww))
118120
expect_identical(vec_ptype2(tibble(), rww), vec_ptype(rww))
119121
})
120122

121123
test_that("can cast to and from `rowwise_df`", {
122-
rww <- unrownames(dplyr::rowwise(mtcars))
123-
input <- mtcars[10]
124-
cast_rww <- dplyr::rowwise(vec_cast(mtcars[10], mtcars))
124+
rww <- unrownames(dplyr::rowwise(bare_mtcars))
125+
input <- bare_mtcars[10]
126+
cast_rww <- dplyr::rowwise(vec_cast(bare_mtcars[10], bare_mtcars))
125127

126128
expect_error(
127-
vec_cast(input, dplyr::rowwise(mtcars["cyl"])),
129+
vec_cast(input, dplyr::rowwise(bare_mtcars["cyl"])),
128130
class = "vctrs_error_cast_lossy"
129131
)
130132

@@ -133,23 +135,23 @@ test_that("can cast to and from `rowwise_df`", {
133135
cast_rww
134136
)
135137
expect_identical(
136-
vec_cast(rww, mtcars),
137-
unrownames(mtcars)
138+
vec_cast(rww, bare_mtcars),
139+
unrownames(bare_mtcars)
138140
)
139141

140142
expect_identical(
141143
vec_cast(tibble::as_tibble(input), rww),
142144
unrownames(cast_rww)
143145
)
144-
tib <- tibble::as_tibble(mtcars)
146+
tib <- tibble::as_tibble(bare_mtcars)
145147
expect_identical(
146148
unrownames(vec_cast(rww, tib)),
147149
tib
148150
)
149151
})
150152

151153
test_that("can cbind rowwise data frames", {
152-
df <- unrownames(mtcars)
154+
df <- unrownames(bare_mtcars)
153155
rww <- dplyr::rowwise(df[-2])
154156
gdf <- dplyr::group_by(df[2], cyl)
155157

@@ -162,7 +164,7 @@ test_that("can cbind rowwise data frames", {
162164

163165
test_that("no common type between rowwise and grouped data frames", {
164166
expect_df_fallback(
165-
out <- vec_ptype_common_fallback(dplyr::rowwise(mtcars), dplyr::group_by(mtcars, cyl))
167+
out <- vec_ptype_common_fallback(dplyr::rowwise(bare_mtcars), dplyr::group_by(bare_mtcars, cyl))
166168
)
167-
expect_identical(out, tibble::as_tibble(mtcars[0, ]))
169+
expect_identical(out, tibble::as_tibble(bare_mtcars[0, ]))
168170
})

tests/testthat/test-type.R

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,3 +221,11 @@ test_that("vec_ptype_finalise() requires vector types", {
221221
expect_error(vec_ptype_finalise(quote(name)), class = "vctrs_error_scalar_type")
222222
expect_error(vec_ptype_finalise(foobar()), class = "vctrs_error_scalar_type")
223223
})
224+
225+
# This might change in the future if we decide that prototypes don't
226+
# have names
227+
test_that("vec_ptype() preserves type of names and row names", {
228+
expect_identical(vec_ptype(c(foo = 1)), named(dbl()))
229+
expect_identical(vec_ptype(mtcars), mtcars[0, ])
230+
expect_identical(vec_ptype(foobar(mtcars)), foobar(mtcars[0, ]))
231+
})

tests/testthat/test-type2.R

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -296,7 +296,6 @@ test_that("vec_ptype2() methods get prototypes", {
296296
expect_identical(x, foobar(int()))
297297
expect_identical(y, foobar(chr()))
298298

299-
skip("Figure out what to do with row names in `vec_ptype()`")
300299
vec_ptype2(foobar(mtcars), foobar(iris))
301300
expect_identical(x, foobar(mtcars[0, , drop = FALSE]))
302301
expect_identical(y, foobar(iris[0, , drop = FALSE]))

0 commit comments

Comments
 (0)