【问题标题】:drop_duplicates in a rangedrop_duplicates 在一个范围内
【发布时间】:2019-07-21 10:56:56
【问题描述】:

我在 python 中有一个这样的 datraframe:

    st      se      st_min  st_max  se_min  se_max 
42  922444  923190  922434  922454  923180  923200
24  922445  923190  922435  922455  923180  923200
43  928718  929456  928708  928728  929446  929466
37  928718  929459  928708  928728  929449  929469

正如我们所看到的,我在前 2 列中有一个范围,以及初始范围的 10 个位置的变化。

我知道函数 drop_duplicates 可以根据值的完全匹配删除重复行。

但是,如果我想根据值的范围删除行,例如,索引 42 和 24 都在同一范围内(如果我考虑范围为 10)并且索引 43 和 37 在相同的情况下.

我该怎么做?

Ps:我不能仅基于一列(例如 st 或 se)删除,我需要删除基于两列(st 和 se)的冗余,使用列 min 和 max 的范围作为过滤器...

【问题讨论】:

  • 你的意思是当给定一行时,你需要找到所有剩余的行,其中每个元素减去给定行中的相应元素在一个范围内?
  • 不,当给定一行时,st 和 se 值应该与 st 和 se min ax max 之间的范围进行比较,就像 if 语句的范围:if st in range (st_min,st_max ),但在数据框结构内...
  • 抱歉我还不清楚,你的意思是只比较两行的stse 列吗?
  • 您是否只想删除范围完全包含在另一个范围中的记录,还是要合并范围?比方说,如果您有 20-25、22-24、19-23、24-28 范围,您是否希望这些范围仅被一个范围 19-28 替换,或者您是否希望删除 22-24 ,因为它完全包含在20-25?
  • @ComplicatedPhenomenon Yeap.

标签: python pandas redundancy


【解决方案1】:

我假设,您想组合所有范围。这样所有重叠的范围都减少到一行。我认为您需要递归地执行此操作,因为可能有多个范围,形成一个大范围,而不仅仅是两个。您可以这样做(只需将 df 替换为您用于存储数据框的变量):

# create a dummy key column to produce a cartesian product
df['fake_key']=0
right_df= pd.DataFrame(df, copy=True)
right_df.rename({col: col + '_r' for col in right_df if col!='fake_key'}, axis='columns', inplace=True)

# this variable indicates that we need to perform the loop once more
change=True
# diff and new_diff are used to see, if the loop iteration changed something
# it's monotically increasing btw.
new_diff= (right_df['se_r'] - right_df['st_r']).sum()
while change:
    diff= new_diff
    joined_df= df.merge(right_df, on='fake_key')
    invalid_indexer= joined_df['se']<joined_df['st_r']    
    joined_df.drop(joined_df[invalid_indexer].index, axis='index', inplace=True)
    right_df= joined_df.groupby('st').aggregate({col: 'max' if '_min' not in col else 'min' for col in joined_df})
    # update the ..._min / ..._max fields in the combined range
    for col in ['st_min', 'se_min', 'st_max', 'se_max']:
        col_r= col + '_r'
        col1, col2= (col, col_r) if 'min' in col else (col_r, col)
        right_df[col_r]= right_df[col1].where(right_df[col1]<=right_df[col2], right_df[col2])
    right_df.drop(['se', 'st_r', 'st_min', 'se_min', 'st_max', 'se_max'], axis='columns', inplace=True)
    right_df.rename({'st': 'st_r'}, axis='columns', inplace=True)
    right_df['fake_key']=0
    # now check if we need to iterate once more
    new_diff= (right_df['se_r'] - right_df['st_r']).sum()
    change= diff <= new_diff

# now all ranges which overlap have the same value for se_r
# so we just need to aggregate on se_r to remove them
result= right_df.groupby('se_r').aggregate({col: 'min' if '_max' not in col else 'max' for col in right_df})
result.rename({col: col[:-2] if col.endswith('_r') else col for col in result}, axis='columns', inplace=True)
result.drop('fake_key', axis='columns', inplace=True)

如果你对你的数据执行这个,你会得到:

            st      se  st_min  st_max  se_min  se_max
se_r                                                  
923190  922444  923190  922434  922455  923180  923200
929459  928718  929459  922434  928728  923180  929469

请注意,如果您的数据集大于几千条记录,您可能需要更改上面生成笛卡尔积的连接逻辑。所以在第一次迭代中,你会得到一个大小为 n^2 的joined_df,其中 n 是输入数据帧中的记录数。然后在每次迭代的后期,joined_df 将由于聚合而变小。

我只是忽略了这一点,因为我不知道您的数据集有多大。避免这种情况会使代码更复杂一些。但是,如果您需要它,您可以创建一个辅助数据框,它允许您在两个数据框上“合并”se 值,并将合并后的值用作fake_key。这不是很常规的分箱,您必须为每个 fake_key 创建一个数据框,其中包含范围 (0...fake_key) 内的所有值。所以例如如果您将假密钥定义为fake_key=se//1000,您的数据框将包含

fake_key  fake_key_join
922       922
922       921
922       920
...       ...
922       0

如果您将上述循环中的merge 替换为代码,则将fake_key 上的此类数据帧与right_df 合并,并将fake_key_join 上的结果与df 合并,您可以使用其余代码并得到与上面相同的结果,但不必产生完整的笛卡尔积。

【讨论】:

  • 感谢 jottbe,我将对此进行测试,但我的数据框中的其他列包含另一种类型的信息,我将检查您的函数将如何与其他信息一起使用..
【解决方案2】:

请注意,例如4224 键的 st不同,因此您可以 不要只使用 st 值。

如果例如您的 range 可以定义为 st / 100(向下舍入为整数), 您可以使用此值创建一个列:

df['rng'] = df.st.floordiv(100)

然后使用 drop_duplicates 并将 subset 设置为仅此列和 删除 rng 列:

df.drop_duplicates(subset='rng').drop(columns=['rng'])

或者键 24st 值应该与上述 相同 (对于键 42) 和第二对行中的 se 相同吗? 在这种情况下,您可以使用:

 df.drop_duplicates(subset=['st', 'se'])

没有任何辅助栏。

【讨论】:

  • 这似乎是一种很好的处理方式,我将测试用 st 和 se 用 floordiv 创建 2 列。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多