pub struct DataFrame {
sexp: SEXP,
}Expand description
A cheap Copy view over a validated R data.frame.
The view carries no GC root. It is sound while an R .Call argument frame,
a ProtectScope, or an owning BuiltDataFrame
keeps the SEXP reachable. Rust-side constructors never return this bare
view; they return BuiltDataFrame.
§Building
Prefer the IntoDataFrame trait on your data (it returns an owned,
GC-rooted BuiltDataFrame that Derefs to DataFrame):
let df = rows.into_dataframe()?;or the closure-fill DataFrame::builder for heterogeneous parallel column fill
(feature = "rayon").
§Reading
Wrap an incoming SEXP with DataFrame::from_sexp (or accept DataFrame directly as a
#[miniextendr] argument), then pull typed columns with DataFrame::column, or
deserialize whole rows with FromDataFrame.
Fields§
§sexp: SEXPImplementations§
Source§impl DataFrame
impl DataFrame
Sourcepub fn group_by(&self, col: &str) -> Result<GroupedDataFrame, DataFrameError>
pub fn group_by(&self, col: &str) -> Result<GroupedDataFrame, DataFrameError>
Partition this frame’s rows by the values of the named column.
Computes group indices in a single pass on the main thread. Supported
key columns: factor (fast path — levels are the keys, level order kept,
empty levels included), character, integer, logical, and double
(Date and POSIXct included; keyed as GroupKey::Real, labelled by
R’s as.character()). List columns and bit64 integer64 columns are an
error.
NA keys form one group, ordered last (unlike R split(), which drops
NA-keyed rows). See the module docs for the full key semantics.
Sourcepub fn group_by_multi(
&self,
cols: &[&str],
) -> Result<GroupedDataFrame, DataFrameError>
pub fn group_by_multi( &self, cols: &[&str], ) -> Result<GroupedDataFrame, DataFrameError>
Partition this frame’s rows by a composite key over several columns —
the multi-column analogue of group_by.
Each supported column contributes one scalar key per row (factor level,
character, integer, logical, or double — same rules and errors as
group_by); the per-row keys are zipped into a
GroupKey::Tuple in column order.
§Order
Non-NA groups match split(df, interaction(col1, col2, …, drop = TRUE)):
the first column varies fastest (R interaction()’s default,
lex.order = FALSE), each column ordered as group_by
would order it alone (factor level order, byte-sorted characters, numeric
integers and doubles — NaN after every number — and FALSE then
TRUE). For character keys the match is exact for
keys whose byte order coincides with the session’s collation — always
true in the C locale; single-case ASCII in practice (e.g. en_US.UTF-8
collates a A b B where byte order gives A B a b). This is inherited
from group_by’s byte-order choice for character keys
— see the group-order note in the module docs.
§NA
interaction() maps any row with an NA in any component to NA and
split() drops it. This method instead keeps such rows: every distinct
NA-containing tuple forms its own group, all ordered after the non-NA
groups, in first-encounter row order. This extends the single-column
NA-last convention (see the module docs).
§Slice
An empty slice is an error. A single-column slice delegates to
group_by and yields scalar keys (not 1-tuples), so
callers never have to special-case one-element tuples.
Sourcepub fn group_declaration(
&self,
) -> Result<Option<GroupDeclaration>, DataFrameError>
pub fn group_declaration( &self, ) -> Result<Option<GroupDeclaration>, DataFrameError>
Read a dplyr-grouped frame’s grouping declaration — its grouping
variables and .drop policy — without its cached group rows.
Returns what dplyr’s group_vars() and group_by_drop_default()
report: Ok(None) when the frame is not grouped, and an error when it
is grouped but its groups attribute is corrupt, where group_vars()
errors too (see below). Nothing is partitioned and no row index is
read, so the key-column types and whether the cached .rows still fit
the frame never matter. A function that groups the frame’s current
rows itself (e.g. with group_by_multi over
GroupDeclaration::vars) reads this instead of
group_by_metadata, which trusts the cached
rows.
§Grouped frames
A frame is grouped when it inherits from grouped_df or rowwise_df,
whatever its attributes: dplyr::is_grouped_df() is
inherits(x, "grouped_df"). A plain data.frame with a stray groups
attribute is not grouped (Ok(None)).
§Errors
A grouped frame’s groups attribute must pass the structural checks
of dplyr’s validate_grouped_df(), which group_vars() runs on a
grouped_df: it is a data frame (GroupsNotDataFrame), its last
column is called .rows (MissingGroupRows), and that column is a
list of integer vectors (BadGroupRows; a list of doubles is
rejected, as in dplyr). The row indices themselves are not checked,
as group_vars() does not check them either, so stale .rows after a
subset still yield the declaration.
A rowwise_df gets the same checks. dplyr is looser here:
group_vars() reads a rowwise_df’s groups attribute without
validating it. validate_rowwise_df() applies the same checks, plus
row-shape checks that this method skips (one .rows element per row,
each holding its own row number).
§Example
// df arrived as dplyr::group_by(data, site, .drop = FALSE)
let decl = df.group_declaration()?.expect("a grouped_df");
assert_eq!(decl.vars, ["site"]);
assert!(!decl.drop);Sourcepub fn group_by_metadata(&self) -> Result<GroupedDataFrame, DataFrameError>
pub fn group_by_metadata(&self) -> Result<GroupedDataFrame, DataFrameError>
Ingest a dplyr grouped_df’s existing grouping from its groups
attribute — honoring the caller’s grouping without recomputing it.
dplyr stores a grouped_df’s grouping in attr(df, "groups"): a
data.frame whose leading columns are the group-key columns (one row
per group, in dplyr’s group order) and whose trailing .rows
list-column holds, per group, the 1-based row indices into df. This
method reads that metadata verbatim into a GroupedDataFrame — the
same type group_by / group_by_multi
produce — so a #[miniextendr] function handed a dplyr-grouped frame can
respect the caller’s grouping, including multi-column groupings.
Unlike group_by, this does no recomputation: a
frame that is not grouped (see group_declaration
for the test) is an error (NotGroupedDataFrame). Callers who want the
framework to compute grouping should use group_by /
group_by_multi.
§Keys
A single key column yields scalar GroupKeys; multiple key columns
yield GroupKey::Tuples (labels .-joined), consistent with
group_by_multi. Supported key-column types are
the same as group_by (factor, character, integer,
logical, double — Date and POSIXct included); a list / integer64
key column is an error.
§Order & empty groups
The groups-frame row order is preserved verbatim — dplyr’s order is
authoritative, with no re-sorting and no NA reordering. .drop = FALSE
empty groups (zero-length .rows) are kept as groups with empty
index vectors, mirroring the empty-factor-level convention of
group_by.
§Trusts the cached rows
The .rows are dplyr’s cache of the grouping, and this method uses them
as they are. It checks that they still fit the frame — every index in
1..=nrow, every row in exactly one group — and reports stale metadata
otherwise (GroupIndexOutOfRange, GroupRowUncovered,
GroupRowDuplicated). The usual cause is subsetting a grouped frame
without dplyr loaded: [ then keeps the old groups attribute. It does
not compare the rows’ key values with their group’s key, so a pure
reorder done the same way (df[c(3, 4, 1, 2), ]) still fits and yields
the old row positions (#1687). To group the current rows instead, read
the declaration with group_declaration and
group by its variables.
§Errors
NotGroupedDataFrame (not grouped); for a corrupt groups
attribute, the errors group_declaration
reports (GroupsNotDataFrame, MissingGroupRows,
BadGroupRows: .rows must be a list of integer vectors, so a
list of doubles is rejected, as in dplyr); UnequalLengths (a key
column’s length differs from that of .rows);
UnsupportedGroupColumn (a key column of an unsupported type); or
one of the stale-metadata errors above. Every .rows index is
converted from R’s 1-based to 0-based.
§GC
The groups attribute frame (and its columns) is only read here, during
construction; it stays reachable via self’s attribute pairlist — which
R protects as a .Call argument frame for the duration of the call — so
it needs no separate root. Only the returned GroupedDataFrame roots
the source frame for its own lifetime (see its GC-rooting docs).
Sourcefn groups_attr(&self) -> Result<Option<GroupsAttr>, DataFrameError>
fn groups_attr(&self) -> Result<Option<GroupsAttr>, DataFrameError>
The groups attribute of a dplyr-grouped frame, checked the way
dplyr’s validate_grouped_df() checks it (with its default
check_bounds = FALSE).
Ok(None) when the frame inherits from neither grouped_df nor
rowwise_df. Otherwise the attribute must be a data frame with at
least one column (DataFrameError::GroupsNotDataFrame), whose last
column is called .rows (DataFrameError::MissingGroupRows) and is
a list of integer vectors (DataFrameError::BadGroupRows). These are
dplyr’s tests: inherits(groups, "data.frame") with at least one
column, the last name, typeof(.rows) == "list", and every element’s
typeof() == "integer". .rows and the key columns are read by
position, so a column named .rows elsewhere in the frame is not
mistaken for it.
Source§impl DataFrame
impl DataFrame
Sourcepub unsafe fn from_built_sexp(sexp: SEXP) -> Self
pub unsafe fn from_built_sexp(sexp: SEXP) -> Self
Wrap an already-built data.frame SEXP without re-validation.
Used by the column assemblers, which produce a well-formed data.frame by
construction.
§Safety
sexp must be a VECSXP with the data.frame class and consistent row.names.
Sourcepub fn from_sexp(sexp: SEXP) -> Result<Self, DataFrameError>
pub fn from_sexp(sexp: SEXP) -> Result<Self, DataFrameError>
Wrap an existing R data.frame SEXP, validating it.
Validates that the object:
- Is a VECSXP (list)
- Inherits from
"data.frame" - Has a
namesattribute - Has extractable
row.namesfor nrow
§Errors
Returns DataFrameError if validation fails.
Sourcepub fn column<T>(&self, name: &str) -> Option<T>where
T: TryFromSexp,
pub fn column<T>(&self, name: &str) -> Option<T>where
T: TryFromSexp,
Get a column by name, converting to type T.
Returns None if the column name is not found or conversion fails.
T may be a vector/collection target (Vec<f64>, Vec<i32>,
Vec<String>, …) — the natural shape for a column — or a scalar for a
length-1 column. The conversion error is discarded (that is what makes
this return Option), so T::Error is unconstrained; use
column_raw when you need the error.
Sourcepub fn column_index<T>(&self, idx: usize) -> Option<T>where
T: TryFromSexp,
pub fn column_index<T>(&self, idx: usize) -> Option<T>where
T: TryFromSexp,
Get a column by 0-based index, converting to type T.
As with column, T may be a vector/collection or a
scalar target type; the conversion error is discarded.
Sourcepub fn column_raw(&self, name: &str) -> Option<SEXP>
pub fn column_raw(&self, name: &str) -> Option<SEXP>
Get the raw SEXP for a column by name.
Sourcepub fn contains_column(&self, name: &str) -> bool
pub fn contains_column(&self, name: &str) -> bool
Check whether a column name exists.
Sourcepub fn validate(
&self,
spec: &TypedListSpec,
) -> Result<TypedList, TypedListError>
pub fn validate( &self, spec: &TypedListSpec, ) -> Result<TypedList, TypedListError>
Validate the data frame’s column types against a TypedListSpec.
Sourcefn named_list(&self) -> NamedList
fn named_list(&self) -> NamedList
Build the NamedList index for O(1) column-by-name access.
Sourcepub fn rename(self, from: &str, to: &str) -> Self
pub fn rename(self, from: &str, to: &str) -> Self
Rename a column. No-op if from doesn’t match any column name.
In-place edit of the names attribute — returns the same frame (and
therefore inherits whatever root the input already had), unlike the
new-frame producers (drop and friends), which return a
rooted BuiltDataFrame. On a BuiltDataFrame receiver the inherent
forward (BuiltDataFrame::rename) keeps the handle instead.
Other attributes are untouched, so renaming a grouping column of a
dplyr grouped_df leaves its groups naming the old column (#1702).
Sourcepub fn strip_prefix(self, prefix: &str) -> Self
pub fn strip_prefix(self, prefix: &str) -> Self
Strip a prefix from all column names that start with it.
In-place edit of the names attribute — returns the same frame; see
rename’s rooting note.
Sourcepub fn drop(self, col: &str) -> BuiltDataFrame
pub fn drop(self, col: &str) -> BuiltDataFrame
Remove a column by name. No-op if the column doesn’t exist.
§Frame attributes
The new frame keeps every attribute of the input except names: its
class, row.names, and anything else on the frame (a units map, a
package’s metadata), as dplyr::select() and tibble’s [ do. This
holds for every column producer (select,
prepend_column,
with_column).
A dplyr grouped_df / rowwise_df keeps its grouping while every
grouping column is still there, unchanged. Removing or replacing one
returns the ungrouped frame instead (no groups attribute, no
grouped_df / rowwise_df class), where dplyr’s [ would regroup by
the grouping columns that remain.
§Rooting
Returns an owned, GC-rooted BuiltDataFrame (#1247): the result frame
is rooted before this method returns, so holding it across R allocations
is safe by construction. The no-op path re-roots the input frame (each
BuiltDataFrame releases exactly its own root — R_PreserveObject
entries stack, so re-rooting an already-rooted SEXP is sound).
The input view must be reachable when calling (the usual DataFrame
view contract: an R .Call argument frame, a
ProtectScope, or a live BuiltDataFrame).
Sourcepub fn select(self, cols: &[&str]) -> BuiltDataFrame
pub fn select(self, cols: &[&str]) -> BuiltDataFrame
Keep only the named columns, in the order given. Unknown names are skipped.
Keeps the frame’s attributes and, while every grouping column is
selected, its dplyr grouping; see drop’s frame-attribute
note.
§Rooting
Returns an owned, GC-rooted BuiltDataFrame — see
drop’s rooting note (#1247).
Sourcepub fn select_rows(&self, idx: &[usize]) -> BuiltDataFrame
pub fn select_rows(&self, idx: &[usize]) -> BuiltDataFrame
Keep only the rows at the given 0-based indices, in order.
Subsets every column to the specified rows and rebuilds compact integer
row.names. Used by the enum reader to densify a flattened sub-frame
before recursing into the inner type’s reader, and by group iteration.
A row is a row of every column, as in df[idx, , drop = FALSE]:
- A vector or list column is gathered by element, and its element
names, if any, are subset with the values. - A column with a
dim(anI(matrix)column, or an array) is gathered along its first dimension:dimbecomesc(length(idx), dim[-1])and the firstdimnamescomponent (the row names) is subset, the rest kept. Arrays of three or more dimensions followvctrs::vec_slice()here; base[.data.framesubsets those by element. - A data.frame column (a packed column) is subset the same way, recursively.
Each column keeps all of its other attributes (SexpExt::copy_most_attrib, the
vctrs::vec_slice() rule), so a POSIXct column keeps its tzone, a
difftime its units and an I() column its AsIs class.
§Frame attributes
The new frame keeps every attribute of the input frame except names
(the columns’ names, as before) and row.names (fresh, 1:length(idx)):
its class, and anything else on the frame, such as a units map
describing some columns or a package’s metadata. This is what
dplyr::slice() (dplyr_row_slice() / dplyr_reconstruct()),
vctrs::vec_slice(), tibble’s [ and base [.data.frame keep on a row
subset. A packed data.frame column keeps its own the same way.
dplyr’s grouping does not survive. A grouped_df or rowwise_df caches
each group’s row indices in its groups attribute, which no longer
matches the selected rows, so the result has no groups attribute and
no grouped_df / rowwise_df class: it is the ungrouped frame (a
tibble, for a dplyr input). dplyr::slice() recomputes the groups
instead. To keep the grouping, read it from the input with
group_declaration and regroup the result,
in Rust with group_by_multi over its
vars, or in R with
dplyr::grouped_df(out, dplyr::group_vars(df)).
Any other attribute indexed by row is copied as it is. A package that
keeps one must rebuild it after a row subset, as dplyr’s extension guide
(?dplyr_row_slice) asks of dplyr_row_slice() methods.
§PROTECT discipline
Allocates new column vectors (and dim / dimnames for matrix columns)
while the output list is OwnedProtected, rooting each new column in it
before the next allocation.
§Rooting
Returns an owned, GC-rooted BuiltDataFrame — see
drop’s rooting note (#1247).
Sourcepub fn prepend_column(self, name: &str, column: SEXP) -> BuiltDataFrame
pub fn prepend_column(self, name: &str, column: SEXP) -> BuiltDataFrame
Insert a column at index 0 (leftmost), removing any same-named column first.
Keeps the frame’s attributes and its dplyr grouping, unless name
replaces a grouping column; see drop’s frame-attribute
note.
§Rooting
Returns an owned, GC-rooted BuiltDataFrame — see
drop’s rooting note (#1247). column must be kept
reachable by the caller (e.g. under an
OwnedProtect) across this call — the new frame
is allocated before column is stored into it.
Sourcepub fn with_column(self, name: &str, column: SEXP) -> BuiltDataFrame
pub fn with_column(self, name: &str, column: SEXP) -> BuiltDataFrame
Upsert a column: replace the column named name if it exists, else append.
The append path builds a new frame that keeps the input’s attributes
and dplyr grouping (see drop’s frame-attribute note).
The replace path stores column into the input frame itself and
returns that frame, attributes untouched, so replacing a grouping
column leaves dplyr’s groups stale (#1702).
§Rooting
Both paths return an owned, GC-rooted BuiltDataFrame (#1247) — the
in-place replace path re-roots the same frame it received (sound:
R_PreserveObject entries stack; this handle releases exactly one).
column must be kept reachable by the caller across this call — the
append path allocates the new frame before column is stored into it.
Sourcepub fn builder(nrow: usize) -> RDataFrameBuilder
pub fn builder(nrow: usize) -> RDataFrameBuilder
Start a closure-per-column builder yielding a rooted BuiltDataFrame.
The heterogeneous-column analogue of with_r_matrix: each column buffer is R memory
filled by a per-column closure. Available regardless of the rayon feature (#1055);
the columns are filled in parallel when rayon is enabled and serially
otherwise — the resulting data.frame is identical either way.
let df = DataFrame::builder(1000)
.column::<f64>("x", |chunk, off| for (i, v) in chunk.iter_mut().enumerate() { *v = (off + i) as f64 })
.column_str("label", |i| Some(format!("row{i}")))
.build();Trait Implementations§
impl Copy for DataFrame
Source§impl IntoR for DataFrame
impl IntoR for DataFrame
impl TrivialClone for DataFrame
Source§impl TryFromSexp for DataFrame
impl TryFromSexp for DataFrame
Source§fn __mx_input_has_na(&self, _input: SEXP) -> bool
fn __mx_input_has_na(&self, _input: SEXP) -> bool
#[miniextendr(no_na)] refuses this converted argument, read
from input (the R value it converted from), when no R guard ran. Read moreSource§fn try_from_sexp(sexp: SEXP) -> Result<Self, Self::Error>
fn try_from_sexp(sexp: SEXP) -> Result<Self, Self::Error>
Source§const NATIVE_BORROW: Option<NativeBorrow> = None
const NATIVE_BORROW: Option<NativeBorrow> = None
Source§const CHARACTER_ONLY: bool = false
const CHARACTER_ONLY: bool = false
NULL, is a character vector or a factor. Read moreSource§#[doc(hidden)]const __MX_EXPECTATION: Option<&'static str> = None
#[doc(hidden)]const __MX_EXPECTATION: Option<&'static str> = None
#[miniextendr] cannot tell from the type as written: the
<expected> of a failed conversion’s '<p>' must be <expected>: .... Read moreSource§unsafe fn try_from_sexp_unchecked(sexp: SEXP) -> Result<Self, Self::Error>
unsafe fn try_from_sexp_unchecked(sexp: SEXP) -> Result<Self, Self::Error>
Source§#[doc(hidden)]unsafe fn __mx_try_from_rooted(root: Root<'_>) -> Result<Self, Self::Error>
#[doc(hidden)]unsafe fn __mx_try_from_rooted(root: Root<'_>) -> Result<Self, Self::Error>
Source§#[doc(hidden)]fn __mx_has_na(&self) -> bool
#[doc(hidden)]fn __mx_has_na(&self) -> bool
#[miniextendr(no_na)]
refuses after the conversion. Read moreSource§#[doc(hidden)]fn __mx_try_from_sexp_with_check(
sexp: SEXP,
check: &ElementCheck<'_>,
) -> Result<Self, SexpError>
#[doc(hidden)]fn __mx_try_from_sexp_with_check(
sexp: SEXP,
check: &ElementCheck<'_>,
) -> Result<Self, SexpError>
check, an element type’s
TryFromSexpElement::check_sexp,
on the R value that carries an element’s class (#1837). Read moreSource§#[doc(hidden)]unsafe fn __mx_try_from_sexp_unchecked_with_check(
sexp: SEXP,
check: &ElementCheck<'_>,
) -> Result<Self, SexpError>
#[doc(hidden)]unsafe fn __mx_try_from_sexp_unchecked_with_check(
sexp: SEXP,
check: &ElementCheck<'_>,
) -> Result<Self, SexpError>
Auto Trait Implementations§
impl Freeze for DataFrame
impl RefUnwindSafe for DataFrame
impl Send for DataFrame
impl Sync for DataFrame
impl Unpin for DataFrame
impl UnsafeUnpin for DataFrame
impl UnwindSafe for DataFrame
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> ConsumingFallible for Twhere
T: Clone,
Source§impl<I> OptionInner for I
impl<I> OptionInner for I
Source§const OPTION_NATIVE_BORROW: Option<NativeBorrow> = const OPTION_NATIVE_BORROW: Option<NativeBorrow> =
<Option<I> as TryFromSexp>::NATIVE_BORROW;
const OPTION_NATIVE_BORROW: Option<NativeBorrow> = const OPTION_NATIVE_BORROW: Option<NativeBorrow> = <Option<I> as TryFromSexp>::NATIVE_BORROW;
<Option<Self> as TryFromSexp>::NATIVE_BORROW.Source§const OPTION_CHARACTER_ONLY: bool = const OPTION_CHARACTER_ONLY: bool =
<Option<I> as TryFromSexp>::CHARACTER_ONLY;
const OPTION_CHARACTER_ONLY: bool = const OPTION_CHARACTER_ONLY: bool = <Option<I> as TryFromSexp>::CHARACTER_ONLY;
<Option<Self> as TryFromSexp>::CHARACTER_ONLY.impl<T> Printable for T
Source§impl<T> RCopy for Twhere
T: Copy,
impl<T> RCopy for Twhere
T: Copy,
Source§impl<T> RDebug for Twhere
T: Debug,
impl<T> RDebug for Twhere
T: Debug,
Source§impl<T> SizeHint for Twhere
T: ?Sized,
impl<T> SizeHint for Twhere
T: ?Sized,
Source§default fn lower_bound(&self) -> usize
default fn lower_bound(&self) -> usize
core_io_internals)[u8; 12] could return any value between 0 and
12 inclusively as a correct implementation. Read moreSource§impl<T> SizedTypeProperties for T
impl<T> SizedTypeProperties for T
Source§#[doc(hidden)]const SIZE: usize = _
#[doc(hidden)]const SIZE: usize = _
sized_type_properties)Source§#[doc(hidden)]const ALIGN: usize = _
#[doc(hidden)]const ALIGN: usize = _
sized_type_properties)Source§#[doc(hidden)]const ALIGNMENT: Alignment = _
#[doc(hidden)]const ALIGNMENT: Alignment = _
ptr_alignment_type)Source§#[doc(hidden)]const IS_ZST: bool = _
#[doc(hidden)]const IS_ZST: bool = _
sized_type_properties)Source§#[doc(hidden)]const LAYOUT: Layout = _
#[doc(hidden)]const LAYOUT: Layout = _
sized_type_properties)Source§#[doc(hidden)]const MAX_SLICE_LEN: usize = _
#[doc(hidden)]const MAX_SLICE_LEN: usize = _
sized_type_properties)[Self]. Read moreLayout§
Note: Most layout information is completely unstable and may even differ between compilations. The only exception is types with certain repr(...) attributes. Please see the Rust Reference's “Type Layout” chapter for details on type layout guarantees.
Size: 8 bytes