使用sparklyr补全时间序列:tidyr::complete()不适用于spark_tbl的问题
tidyr::complete()) Got it, let's work through this! The issue here is that tidyr::complete() is built for local in-memory data frames (like your original test with dfv), but it doesn't support Spark's distributed tbl_spark objects—hence that "no applicable method" error.
Instead, we need to use Spark-native functions to generate the full set of minute timestamps, cross them with your ElemUID values, then join back to your original data to fill in the gaps. Here are two straightforward approaches using sparklyr and dplyr:
Approach 1: Using sparklyr + dplyr Syntax
This stays close to your original R workflow but adapts it for Spark:
library(sparklyr) library(dplyr) # Step 1: Grab the time range and unique ElemUID values from your Spark table time_bounds <- dfv %>% mutate(timestamp = as.POSIXct(DaySecFrom.UTC.)) %>% summarise( min_ts = min(timestamp), max_ts = max(timestamp) ) %>% collect() # Pull these small values to local R unique_elements <- dfv %>% distinct(ElemUID) # Step 2: Generate a full sequence of minute timestamps (Spark uses Unix timestamps under the hood) full_timestamps <- sdf_seq( sc = spark_connection(dfv), from = as.numeric(time_bounds$min_ts), # Convert to Unix seconds to = as.numeric(time_bounds$max_ts), by = 60 # 60 seconds = 1 minute ) %>% mutate(timestamp = as.POSIXt(x, origin = "1970-01-01", tz = "UTC")) %>% select(timestamp) # Step 3: Create all combinations of timestamp + ElemUID (this replaces tidyr::complete()) full_grid <- full_timestamps %>% cross_join(unique_elements) # Step 4: Join back to your original data to fill in existing values (NA for missing rows) completed_data <- full_grid %>% left_join( dfv %>% mutate(timestamp = as.POSIXct(DaySecFrom.UTC.)), by = c("timestamp", "ElemUID") ) # Optional: Fill NA values for your measurement columns if needed # completed_data <- completed_data %>% mutate(your_measurement_col = coalesce(your_measurement_col, 0))
Approach 2: Using Spark SQL (For SQL-Friendly Users)
If you prefer writing SQL directly, you can use Spark's sequence() and explode() functions to generate the timestamp series:
# Register your Spark table as a temporary view so we can query it sdf_register(dfv, "dfv_temp") # Run the Spark SQL query to fill missing timestamps completed_data <- spark_sql(dfv, " WITH time_range AS ( SELECT MIN(CAST(DaySecFrom.UTC. AS TIMESTAMP)) AS min_ts, MAX(CAST(DaySecFrom.UTC. AS TIMESTAMP)) AS max_ts FROM dfv_temp ), timestamp_series AS ( SELECT EXPLODE(SEQUENCE(min_ts, max_ts, INTERVAL 1 MINUTE)) AS timestamp FROM time_range ), unique_elements AS ( SELECT DISTINCT ElemUID FROM dfv_temp ), full_grid AS ( SELECT * FROM timestamp_series CROSS JOIN unique_elements ) SELECT * FROM full_grid LEFT JOIN dfv_temp ON full_grid.timestamp = CAST(dfv_temp.DaySecFrom.UTC. AS TIMESTAMP) AND full_grid.ElemUID = dfv_temp.ElemUID ")
Key Notes:
- Both approaches work by first creating a "full grid" of every minute timestamp paired with every
ElemUID—this is exactly whattidyr::complete()does locally, but adapted for Spark's distributed environment. - Use
collect()only for small datasets (like the time bounds or unique ElemUIDs) to avoid pulling large data to your local machine. - If you need to fill NA values for your data columns, use
coalesce()(Spark/dplyr function) to replace missing values with a default (like 0 or NA).
内容的提问来源于stack exchange,提问作者user60856839

