我有一个大数据帧(~1000万行(。每行具有:
- 类别
- 起始位置
- 结束位置
如果两行属于同一类别,并且开始和结束位置以 +-5 容差重叠,我只想保留其中一行。例如
1, cat1, 10, 20
2, cat1, 12, 21
3, cat2, 10, 25
我想过滤掉 1 或 2。
我现在正在做的事情不是很有效率,
import pandas as pd
df = pd.read_csv('data.csv', sep='t', header=None)
dfs = []
for seq in df.category.unique():
dfs[seq] = df[df.category == seq]
for index, row in df.iterrows():
if index in discard:
continue
df_2 = dfs[row.category]
res = df_2[(abs(df_2.start - row.start) <= params['min_distance']) & (abs(df_2.end - row.end) <= params['min_distance'])]
if len(res.index) > 1:
discard.extend(res.index.values)
rows.append(row)
df = pd.DataFrame(rows)
我还尝试了使用数据帧的排序版本的不同方法。
my_index = 0
indexes = []
discard = []
count = 0
curr = 0
total_len = len(df.index)
while my_index < total_len - 1:
row = df.iloc[[my_index]]
cond = True
next_index = 1
while cond:
second_row = df.iloc[[my_index + next_index]]
c1 = (row.iloc[0].category == second_row.iloc[0].category)
c2 = (abs(second_row.iloc[0].sstart - row.iloc[0].sstart) <= params['min_distance'])
c3 = (abs(second_row.iloc[0].send - row.iloc[0].send) <= params['min_distance'])
cond = c1 and c2 and c3
if cond and (c2 amd c3):
indexes.append(my_index)
cond = True
next_index += 1
indexes.append(my_index)
my_index += next_index
indexes.append(total_len - 1)
问题是这个解决方案并不完美,有时它会错过一行,因为重叠可能在前面几行,而不是在下一行
我正在寻找有关如何以对熊猫更友好的方式解决这个问题的任何想法(如果有的话(。
这里的方法应该是这样的:
- 熊猫按类别分组
- agg(Func( on groupby result
- Func 应该实现在类别内查找最佳范围的逻辑(排序搜索、平衡树或其他任何内容(
您想合并所有相似的还是仅合并 2 个连续的?如果都相似,我建议您首先按类别对行进行排序,然后在其他 2 列上对行进行排序,并在一行中挤压相似的行。如果只是连续的 2,则检查下一个值是否在您设置的范围内,如果是,则合并它。在这里,您可以看到如何:
基于条件合并行熊猫数据帧
我不相信可以在没有循环的情况下进行数字比较,但您至少可以使其中的一部分更干净、更高效:
dfs = []
for seq in df.category.unique():
dfs[seq] = df[df.category == seq]
取而代之的是使用 df.groupby('category').apply(drop_duplicates).droplevel(0)
,其中 drop_duplicates
是包含第二个循环的函数。然后,将为每个类别单独调用该函数,其数据帧仅包含筛选的行。输出将合并回单个数据帧。数据帧将是一个多索引,其值为"类别"作为外部级别;这可以通过droplevel(0)
.
其次,在该类别中,您可以按两个数字列中的第一个进行排序,以实现另一个小的加速:
def drop_duplicates(df):
df = df.sort_values("sstart")
...
这将允许您在 sstart 列值超出范围时立即停止内部循环,而不是将每一行与其他每一行进行比较。