ValueError с pandas применяет функцию, возвращающую вывод переменной формы

У меня есть фрейм данных pandas с тремя столбцами, структурированными следующим образом:

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

Значения в «Начале» и «Конце» представляют собой интервалы позиций в большей строке (например, от позиции 9000 до 11000). Моя цель - разделить большую строку на окна по 10000 позиций и подсчитать, сколько из них содержится в интервалах из моего фрейма данных.

Например, окно 0:10000 будет содержать 1000 позиций, а окно 10000:20000 будет содержать остальные 1000 позиций из интервала 9000:11000.

Для этого я сначала запускаю функцию для разделения этих интервалов на окна, так что если это ввод:

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 — размер окна (10000):

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)

Посмотрев в Интернете, я обнаружил, что эта проблема, похоже, связана с слиянием панд, но я не использую слияние панд. Я полагаю, что это может быть связано с тем, что некоторые строки создают одну серию выходных данных, а некоторые другие создают небольшой фрейм данных (разделенные).

Глянь сюда:

Sample       A
Start     6928
End       9422

  Sample  Start    End
0      A   9939  10000
1      A  10000  11090

Любые советы о том, как это исправить?

Минимальный набор данных для воспроизведения: https://file.io/iZ3fguCFlRbq

РЕДАКТИРОВАТЬ № 1:

Я попытался изменить строку в функции, чтобы иметь согласованный вывод (т.е. возвращать только кадры данных):

df_out = df_sub.to_frame().T

И теперь раунд apply() "работает", т.к. in не выдает ошибок, но вывод выглядит так:

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() и параллельного приложения pandas, такого как pandarallel или swifter. Все еще ищу любую подсказку :)

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)

Вы удалили файл, который вы связали? Или это было то же самое, что и образец, который вы вставили?

user2246849 13.05.2022 13:16

Кроме того, еще вопрос: какой конец интервала закрыт? т. е. входит ли 10 000 в число 0–10 000 или в число 10 000–20 000?

user2246849 13.05.2022 14:36

@ user2246849 у тебя ссылка не работает?

Macspider 13.05.2022 15:28

@user2246849 user2246849 это полуоткрытый интервал, начинающийся с 0, поэтому W_end = 10000 является первой исключенной позицией.

Macspider 13.05.2022 15:29
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
2
4
64
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Если я все правильно понимаю, то вот возможное решение:

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

Я протестировал его на DataFrame с> 1M строк, и, похоже, он вычислил результаты менее чем за секунду.

@ user2246849 идеально подходит. Я только думаю, что немного сложно следовать, когда дело доходит до определения интервалов.

Мое предложение здесь состоит в том, чтобы поиграть со строкой только для того, чтобы определить функцию, которая берет строку и возвращает интервалы. Я имею в виду, учитывая df, что вы берете x = df.iloc[1] и создаете функцию, которая возвращает [[0, 10_000], [10_000, 20_000]]

import pandas as pd


df = pd.DataFrame(
    {'Sample': {0: 'A', 1: 'A', 2: 'A'},
     'Start': {0: 2500, 1: 9000, 2: 18000},
     'End': {0: 5000, 1: 11000, 2: 19500}})


def get_intervals(x, window_step):
    out = [
        [i * window_step, 
        (i + 1) * window_step] 
     for i in range(
         x["Start"] // window_step, 
        (x["End"] - 1) // window_step + 1)]
    return out

И мы назначаем интервалы с применением

df["intervals"] = df.apply(
    lambda x: get_intervals(x, window_step), axis=1)

которые возвращаются

  Sample  Start    End                     intervals
0      A   2500   5000                  [[0, 10000]]
1      A   9000  11000  [[0, 10000], [10000, 20000]]
2      A  18000  19500              [[10000, 20000]]

Отныне вы можете следовать другому ответу.

Другие вопросы по теме