我有一个Pandas 数据框架,有三列, struct 如下:

Sample    Start  End
<string>  <int>  <int>

"开始"和"结束"中的值是较大字符串上的位置间隔(例如,从位置9000到11000).我的目标是将较大的字符串细分为10000个位置的窗口,并计算其中有多少个位置包含在数据帧的间隔中.

例如,窗口0:10000将包含1000个位置,窗口10000:20000将包含间隔9000:11000的其他1000个位置.

为此,我首先运行一个函数,将这些时间间隔拆分为多个窗口,这样,如果这是输入:

Sample    Start  End
A         2500   5000
A         9000   11000
A         18000  19500

这就是输出:


Sample    Start  End    W_start  W_end
A         2500   5000   0        10000
A         9000   10000  0        10000
A         10000  11000  10000    20000
A         18000  19500  10000    20000

这就是我使用的函数,其中df_sub是数据帧的一行,w_size是窗口大小(df_sub00):

def split_into_windows(df_sub, w_size):

    start, end = df_sub.Start, df_sub.End
    w_start = start - (start % w_size)
    w_end = w_start + w_size

    if (w_start <= start <= w_end) and (w_start <= end <= w_end):
        df_out = df_sub

    elif (w_start <= start <= w_end) and (end > w_end):
        out = []
        df_tmp = df_sub.copy()
        df_tmp.End = w_end
        out.append(df_tmp.copy())

        while (end > w_end):
            w_start += w_size
            w_end += w_size
            df_tmp.Start = max(start, w_start)
            df_tmp.End = min(end, w_end)
            out.append(df_tmp.copy())

        df_out = pd.DataFrame(out)

    return df_out

我用apply()调用函数:

df = df.apply(split_into_windows, axis=1, args=(w_size,))

但我有一个错误:

ValueError: Buffer has wrong number of dimensions (expected 1, got 2)

在网上我发现这个问题似乎与Pandas 合并有关,但我没有使用Pandas 合并.我认为这可能与以下事实有关:一些线路产生一个单一的输出序列,而另一些线路产生一个小的数据帧(分裂的数据帧).

请看这里:

Sample       A
Start     6928
End       9422

  Sample  Start    End
0      A   9939  10000
1      A  10000  11090

关于如何解决这个问题有什么建议吗?

要复制的最小数据集:https://file.io/iZ3fguCFlRbq

编辑#1:

我try 更改函数中的一行以获得一致的输出(即仅返回数据帧):

df_out = df_sub.to_frame().T

现在,apply()轮"有效",如中所示,不会抛出错误,但输出如下所示:

0  Sample  Start   End
0  A       0       6915
1  Sample  Start   End
0  A       6928    9422
2  Sample  Start   End
0  A       9939    10000
...

<class 'pandas.core.series.Series'>

编辑#2:

我不能使用.iterrows(),它需要太长的时间(估计数:周)和我操作的数据帧的大小.

编辑#3:

像这样使用multiprocessing让我度过了这一天,但它仍然是一个次优的解决方案,相比之下,我可以实现一个正常运行的apply()调用和一个并行应用程序,如pandarallelswifter.仍在寻找任何提示:)

pool = mp.Pool(processes=48)
q = mp.Manager().Queue()

start = time.time()
for index, row in df_test.iterrows():
    pool.apply_async(split_into_windows, args=(row, w_size, q))

pool.close()
pool.join()

out = []
while q.empty() == False:
    out.append(q.get())

df = pd.DataFrame(out)

推荐答案

如果我理解正确,这里有一个可能的解决方案:

import pandas as pd

window_step = 10000

# Get indices of the window for start and end (here, the end is inclusive).
df['start_loc'] = df['Start'] // window_step 
df['end_loc'] = (df['End']-1) // window_step

# Build the intervals for the W_start and W_end columns for each row.
intervals = [list((s*window_step, (s+1)*window_step) for s in range(r[0], r[1]+1))
            for r in zip(df['start_loc'], df['end_loc'])]

# Insert in df and explode the interval column to get extra rows.
df['interval'] = intervals
df = df.explode(column='interval')

# Split the interval in two columns.
df[['W_start', 'W_end']] =  pd.DataFrame(df['interval'].tolist(), index=df.index)

# Correct the starts and ends that are wrong because duplicated with explode.
wrong_ends = df['End'].to_numpy() > df['W_end'].to_numpy()
df.loc[wrong_ends, 'End'] = df.loc[wrong_ends, 'W_end']
wrong_starts = df['Start'].to_numpy() < df['W_start'].to_numpy()
df.loc[wrong_starts, 'Start'] = df.loc[wrong_starts, 'W_start']

df = df.drop(columns=['start_loc', 'end_loc', 'interval'])

print(df)
  Sample  Start    End  W_start  W_end
0      A   2500   5000        0  10000
1      A   9000  10000        0  10000
1      A  10000  11000    10000  20000
2      A  18000  19500    10000  20000

然后,从这里开始,计算每个窗口中包含的位置数:

df['included_positions'] = df['End'] - df['Start']

sample_win_cnt = df.groupby(['Sample', 'W_start', 'W_end']).sum().drop(columns=['Start', 'End'])
print(sample_win_cnt)
                      included_positions
Sample W_start W_end                    
A      0       10000                3500
       10000   20000                2500

这里我也按'Sample'分组.我不确定这是你想要的.如果没有,你也可以按'W_start''W_end'分组.

另一个示例的输出:

输入:

  Sample  Start    End
0      A   9939  10000
1      A  10000  11090

间隔结果:

  Sample  Start    End  W_start  W_end
0      A   9939  10000        0  10000
1      A  10000  11090    10000  20000

计数:

                      included_positions
Sample W_start W_end                    
A      0       10000                  61
       10000   20000                1090

我在数据帧上用>;100万行,它似乎能在不到一秒钟内计算出结果.

Python相关问答推荐

使用FASTCGI在IIS上运行Django频道

什么相当于pytorch中的numpy累积ufunc

如何让Flask 中的请求标签发挥作用

根据二元组列表在pandas中创建新列

当从Docker的--env-file参数读取Python中的环境变量时,每个\n都会添加一个\'.如何没有额外的?

如何请求使用Python将文件下载到带有登录名的门户网站?

Pandas计数符合某些条件的特定列的数量

driver. find_element无法通过class_name找到元素'""

我的字符串搜索算法的平均时间复杂度和最坏时间复杂度是多少?

OpenGL仅渲染第二个三角形,第一个三角形不可见

python sklearn ValueError:使用序列设置数组元素

如何使用正则表达式修改toml文件中指定字段中的参数值

使用tqdm的进度条

将CSS链接到HTML文件的问题

如何在Airflow执行日期中保留日期并将时间转换为00:00

如何写一个polars birame到DuckDB

如何在Python中解析特定的文本,这些文本包含了同一行中的所有内容,

如何在Python中创建仅包含完整天数的月份的列表

将像素信息写入文件并读取该文件

在Pandas 中以十六进制显示/打印列?