You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spark DataFrame:基于小于当前时间的两大值平均值计算新列

Solution for Calculating NewVal in Spark DataFrame

Let's break down how to solve this problem step by step, with code examples that match your exact requirements.

First, Clarify the Requirements

We need to:

  • For each row, filter all rows where time is smaller than the current row's time
  • From those filtered rows, take the top 2 largest value values and calculate their average
  • Compute NewVal as the current row's value minus that average
  • Set NewVal to Null for the two rows with the smallest time values

Step 1: Set Up the Sample DataFrame

First, let's initialize our Spark session and load your sample data:

from pyspark.sql import SparkSession
from pyspark.sql import Window
from pyspark.sql.functions import col, avg, row_number, collect_list, sort_array, slice, element_at

# Initialize Spark session
spark = SparkSession.builder.appName("NewValCalculation").getOrCreate()

# Sample input data
sample_data = [
    (3, "n1", 7, 50),
    (10, "n1", 3, 40),
    (11, "n1", 5, 30),
    (1, "n1", 2, 20),
    (2, "n1", 6, 20),
    (9, "n1", 4, 10),
    (4, "n1", 1, 10)
]

# Create DataFrame with specified columns
df = spark.createDataFrame(sample_data, ["id", "nd", "time", "value"])
df.show()

Step 2: Flag Rows with Smallest Time Values

We'll add a row number ordered by time to easily identify the first two rows (the ones with the smallest time values):

# Window to assign row numbers based on ascending time
time_order_window = Window.orderBy(col("time"))
df_with_row_num = df.withColumn("row_num", row_number().over(time_order_window))
df_with_row_num.show()

Step 3: Calculate Top 2 Values & Their Average

Next, we define a window that includes all rows before the current row (since we only care about rows with time < current time). We'll collect all value values in this window, sort them in descending order, take the top 2, then compute their average:

# Window to include all rows with time < current row's time
prev_rows_window = Window.orderBy(col("time")).rowsBetween(Window.unboundedPreceding, Window.currentRow - 1)

# Collect, sort, and extract the top 2 values from preceding rows
df_with_top2 = df_with_row_num.withColumn(
    "top2_values",
    slice(sort_array(collect_list(col("value")).over(prev_rows_window), asc=False), 1, 2)
)

# Calculate average of the top 2 values (returns Null if fewer than 2 values exist)
df_with_avg = df_with_top2.withColumn(
    "avg_top2",
    avg(element_at(col("top2_values"), 1), element_at(col("top2_values"), 2))
)

Step 4: Compute NewVal & Finalize Results

Finally, we calculate NewVal and set it to Null for the first two rows (smallest time values):

# Calculate NewVal, then set to Null for the first two rows
final_df = df_with_avg.withColumn(
    "NewVal",
    col("value") - col("avg_top2")
).withColumn(
    "NewVal",
    col("NewVal").when(col("row_num") <= 2, None)
).select("id", "nd", "time", "value", "NewVal")

# Show the final result
final_df.show()

Expected Output

Running this code will produce exactly the result you specified:

+---+---+----+-----+------+
| id| nd|time|value|NewVal|
+---+---+----+-----+------+
|  4| n1|   1|   10|  null|
|  1| n1|   2|   20|  null|
| 10| n1|   3|   40|    25|
|  9| n1|   4|   10|    20|
| 11| n1|   5|   30|     0|
|  2| n1|   6|   20|   -15|
|  3| n1|   7|   50|    15|
+---+---+----+-----+------+

Key Notes

  • rowsBetween(Window.unboundedPreceding, Window.currentRow - 1) ensures we only consider rows with time smaller than the current row's time
  • sort_array(collect_list(...), asc=False) sorts collected values in descending order, so slice(1,2) grabs the top 2 largest values
  • We use row_number() to quickly identify the two rows with the smallest time values and set their NewVal to Null

内容的提问来源于stack exchange,提问作者anvy elizabeth

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 09:09:08