按组筛选每组Top20%~80%数据的技术实现请求
Got it, let's solve this problem where we need to exclude the lowest 20% and highest 20% of values in each group (like apples and bananas) and keep the middle range. Here's how to do it using Python's pandas library, which is perfect for this kind of grouped data manipulation.
Step 1: Parse the Raw Data into a Structured DataFrame
First, we need to convert your raw string data into a pandas DataFrame so we can work with it easily.
import pandas as pd # Your raw input data raw_data = "fruit price apple 12 apple 13 apple 14 apple 15 apple 16 banana 3 banana 5 banana 1 banana 4 banana 2" # Split the string into individual parts parts = raw_data.split() # Extract (fruit, price) pairs (skipping the header "fruit price") data = [] for i in range(2, len(parts), 2): fruit = parts[i] price = int(parts[i+1]) data.append({"fruit": fruit, "price": price}) # Create the DataFrame df = pd.DataFrame(data)
This gives us a clean, structured DataFrame:
| fruit | price |
|---|---|
| apple | 12 |
| apple | 13 |
| apple | 14 |
| apple | 15 |
| apple | 16 |
| banana | 3 |
| banana | 5 |
| banana | 1 |
| banana | 4 |
| banana | 2 |
Step 2: Filter Each Group to Keep the 20% to 80% Range
We have two reliable approaches here—one ideal for larger datasets and another perfect for small, evenly sized groups like your example.
Approach 1: Using Percentiles
This method calculates the 20th and 80th percentiles for each group and retains values between them. It works seamlessly even if group sizes vary.
def filter_by_percentile(group): # Calculate 20th and 80th percentiles for the group's prices q20 = group['price'].quantile(0.2) q80 = group['price'].quantile(0.8) # Keep rows where price falls between the percentiles (exclusive) return group[(group['price'] > q20) & (group['price'] < q80)] # Apply the filter to each group and reset the index filtered_df = df.groupby('fruit').apply(filter_by_percentile).reset_index(drop=True)
Approach 2: Count-Based Removal
Since your example groups have exactly 5 elements (20% of 5 is 1), we can directly remove the lowest and highest 1 element from each group. This is straightforward for small, consistent group sizes.
def filter_by_count(group): group_size = len(group) # Calculate how many elements to remove from top and bottom (20% of group size) remove_count = int(group_size * 0.2) # Sort the group by price, then exclude the first and last N elements sorted_group = group.sort_values('price') return sorted_group.iloc[remove_count : group_size - remove_count] # Apply the filter to each group filtered_df_count = df.groupby('fruit').apply(filter_by_count).reset_index(drop=True)
Step 3: Convert Back to the Desired String Format
If you need the filtered data back into the space-separated string format like your example, run this:
# Start with the header result_parts = ["fruit", "price"] # Add each fruit and price pair for _, row in filtered_df.iterrows(): result_parts.append(row['fruit']) result_parts.append(str(row['price'])) # Join into a single string result_string = ' '.join(result_parts) print(result_string)
This will output:fruit price apple 13 apple 14 apple 15 banana 2 banana 3 banana 4
Which matches your expected output (the order of banana prices doesn't matter, but you can sort them if needed).
内容的提问来源于stack exchange,提问作者foxight

