У меня есть датафрейм с 3 столбцами:

df:

x       y      z
334     290    3350.0
334     291    3350.5
334     292    3360.1
335     292    3360.1
335     292    3360.1
335     290    3351.0
335     290    3352.5
335     291    3333.1
335     291    3333.1
.
.

Я хотел бы проверить и проанализировать значения каждой строки от row = n до row = n+7 в новый фрейм данных на основе нескольких условий:

  1. df [n]! = df [n + 1]
  2. df [n]! = df [n + 3]
  3. df [n]! = df [n + 5]
  4. df ['x'] [n]
  5. df ['x'] [n]> df ['x'] [n + 3]

Если все они выполнены, я хочу написать новый фрейм данных:

df_new = pd.concat([df[n], df[n+1], df[n+2], df[n+3], 
df[n+4], df[n+5], df[n+6], df[n+7]])

Таким образом, алгоритм + вывод будет выглядеть так:

for df[n] = 0:
1) [334     290    3350.0] != [334     291    3350.5]  True
2) [334     290    3350.0] != [335     292    3360.1]  True
3) [334     290    3350.0] != [335     290    3351.0]  True
4) 335 < 334  False
5) 335 > 335  False

Поэтому в этом случае он будет пропускать первую итерацию, пока мы не пройдем всю длину кадра данных и не найдем совпадения.

df_new(first iteration) = df_new.concat([....]) = empty row values

Есть ли простой способ сделать это со скоростью в Pandas?

3
HelloToEarth 27 Май 2019 в 21:12

2 ответа

Лучший ответ

А. Получите соответствующие смены:

    n1 = df.shift(-1)
    n2 = df.shift(-2)
    n3 = df.shift(-3)
    n5 = df.shift(-5)

B. Удовлетворить условия 1, 2 и 3:

cond = (df != n1) & (df != n3) & (df != n5)

C. Удовлетворить условия 4, 5:

 cond['holder'] = (df.x < n2.x) & (df.x < n3.x)

D. Получить серию bool (мы хотим любую строку со всеми 'True'):

boolidx = cond.all(axis=1)

E. Используйте для получения результата:

df.loc[boolidx]
4
Sandalaphon 27 Май 2019 в 20:00

Я немного изменил ваши данные выборки, чтобы получить один положительный результат:

df = pd.DataFrame(data=[
    [ 334, 290, 3350.0 ],
    [ 334, 291, 3350.5 ],
    [ 334, 292, 3360.1 ],
    [ 335, 292, 3360.1 ],
    [ 335, 292, 3360.1 ],
    [ 333, 290, 3351.0 ],
    [ 335, 290, 3352.5 ],
    [ 335, 291, 3333.1 ],
    [ 335, 291, 3333.1 ]], columns=['x', 'y', 'z'])

Затем по соображениям эффективности я определил следующую функцию:

def roll_win(a, win):
    shape = (a.shape[0] - win + 1, win, a.shape[1])
    strides = (a.strides[0],) + a.strides
    return np.lib.stride_tricks.as_strided(a, shape=shape, strides=strides)

Он генерирует таблицу 3-D , где измерения 2nd и 3rd windows "из исходного массива Numpy a . Размер окна - win , скользя вертикально. Таким образом, обработка последовательных окон требует выполнения цикла первая ось сгенерированной таблицы (см. ниже).

Благодаря использованию функции as_strided она работает значительно быстрее, чем любой «обычный» цикл Python (сравните время выполнения с другими решениями).

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

Затем я вызываю эту функцию:

tbl = roll_win(df.values, 7)

Обратите внимание, что массив Numpy должен иметь тип элемента single , поэтому тип "обобщен" до float , потому что один исходный столбец имеет float .

Затем у нас есть подготовительные шаги для цикла обработки каждого скользящего окна:

res = []    # Result container
idx = 0     # Rolling window index

Остальная часть программы представляет собой цикл:

while idx < len(tbl):
    tt = tbl[idx]  # Get the current rolling window (2-D)
    r0 = tt[0]     # Row 0
    # Condition
    cond = not((r0 == tt[1]).all() and (r0 == tt[3]).all()\
        and (r0 == tt[5]).all()) and tt[0][0] < tt[2][0]\
        and tt[0][0] > tt[3][0]
    if cond:   # OK
        # print(idx)
        # print(tt)
        res.extend(tt)  # Add to result
        idx += 7        # Skip the current result
    else:      # Failed
        idx += 1        # Next loop for the next window

В «положительном» случае я решил запустить следующий цикл из строки после текущего соответствия ( idx + = 7 ), чтобы избежать возможного частично перекрывающиеся наборы исходных строк. Если вам не нужна эта функция, добавьте 1 do idx в обоих случаях.

Для демонстрации вы можете раскомментировать тестовые распечатки выше.

Осталось только создать целевой DataFrame из строк собраны в res :

df2 = pd.DataFrame(data=res, columns=['x', 'y', 'z'], dtype=int)

Обратите внимание, что dtype = int будет выполняться только для столбцов x и y , потому что значения в столбце z имеют дробную часть.

0
Valdi_Bo 27 Май 2019 в 22:07