Обеспокоена эта примерная структура данных pandas:
Measurement Trigger Valid
0 2.0 False True
1 4.0 False True
2 3.0 False True
3 0.0 True False
4 100.0 False True
5 3.0 False True
6 2.0 False True
7 1.0 True True
Всякий раз, когда Trigger
равно True
, я хочу рассчитать сумму и среднее значение последних 3 (начиная с текущего) действительных измерений. Измерения считаются действительными, если столбец Valid
равен True
. Итак, давайте проясним, используя два примера в приведенном выше кадре данных:
Index 3
: следует использовать индексы2,1,0
. ОжидаетсяSum = 9.0, Mean = 3.0
Index 7
: следует использовать индексы7,6,5
. ОжидаетсяSum = 6.0, Mean = 2.0
Я пробовал pandas.rolling
и создавал новые, смещенные столбцы, но безуспешно. См. следующий отрывок из моих тестов (которые должны запускаться напрямую):
import unittest
import pandas as pd
import numpy as np
from pandas.util.testing import assert_series_equal
def create_sample_dataframe_2():
df = pd.DataFrame(
{"Measurement" : [2.0, 4.0, 3.0, 0.0, 100.0, 3.0, 2.0, 1.0 ],
"Valid" : [True, True, True, False, True, True, True, True],
"Trigger" : [False, False, False, True, False, False, False, True],
})
return df
def expected_result():
return pd.DataFrame({"Sum" : [np.nan, np.nan, np.nan, 9.0, np.nan, np.nan, np.nan, 6.0],
"Mean" :[np.nan, np.nan, np.nan, 3.0, np.nan, np.nan, np.nan, 2.0]})
class Data_Preparation_Functions(unittest.TestCase):
def test_backsummation(self):
N_SUMMANDS = 3
temp_vars = []
df = create_sample_dataframe_2()
for i in range(0,N_SUMMANDS):
temp_var = "M_{0}".format(i)
df[temp_var] = df["Measurement"].shift(i)
temp_vars.append(temp_var)
df["Sum"] = df[temp_vars].sum(axis=1)
df["Mean"] = df[temp_vars].mean(axis=1)
df.loc[(df["Trigger"]==False), "Sum"] = np.nan
df.loc[(df["Trigger"]==False), "Mean"] = np.nan
assert_series_equal(expected_result()["Sum"],df["Sum"])
assert_series_equal(expected_result()["Mean"],df["Mean"])
def test_rolling(self):
df = create_sample_dataframe_2()
df["Sum"] = df[(df["Valid"] == True)]["Measurement"].rolling(window=3).sum()
df["Mean"] = df[(df["Valid"] == True)]["Measurement"].rolling(window=3).mean()
df.loc[(df["Trigger"]==False), "Sum"] = np.nan
df.loc[(df["Trigger"]==False), "Mean"] = np.nan
assert_series_equal(expected_result()["Sum"],df["Sum"])
assert_series_equal(expected_result()["Mean"],df["Mean"])
if __name__ == '__main__':
suite = unittest.TestLoader().loadTestsFromTestCase(Data_Preparation_Functions)
unittest.TextTestRunner(verbosity=2).run(suite)
Любая помощь или решение приветствуется. Спасибо и ура!
РЕДАКТИРОВАТЬ: Уточнение: это результирующий кадр данных, который я ожидаю:
Measurement Trigger Valid Sum Mean
0 2.0 False True NaN NaN
1 4.0 False True NaN NaN
2 3.0 False True NaN NaN
3 0.0 True False 9.0 3.0
4 100.0 False True NaN NaN
5 3.0 False True NaN NaN
6 2.0 False True NaN NaN
7 1.0 True True 6.0 2.0
EDIT2: Еще одно уточнение:
Я действительно не просчитался, а скорее не изложил свои намерения настолько ясно, насколько мог бы. Вот еще одна попытка использования того же фрейма данных:
Давайте сначала посмотрим на столбец Trigger
: мы находим первое True
в индексе 3 (зеленый прямоугольник). Таким образом, индекс 3 — это точка, с которой мы начинаем поиск. В индексе 3 нет допустимых измерений (столбец Valid
равен False
; красный прямоугольник). Итак, начинаем двигаться дальше в прошлое, пока не накопим три строки, где Valid
это True
. Это происходит для индексов 2,1 и 0. Для этих трех индексов мы вычисляем сумму и среднее значение столбца Measurement
(синий прямоугольник):
- СУММА: 2,0 + 4,0 + 3,0 = 9,0
- СРЕДНИЙ: (2,0 + 4,0 + 3,0) / 3 = 3,0
Теперь мы начинаем следующую итерацию этого маленького алгоритма: снова ищем следующее True
в столбце Trigger
. Находим его по индексу 7 (зеленый прямоугольник). Существует также допустимое измерение в индексе 7, поэтому мы включаем его на этот раз. Для нашего расчета используем индексы 7,6 и 5 (зеленый прямоугольник), и таким образом получаем:
- СУММА: 1,0 + 2,0 + 3,0 = 6,0
- СРЕДНЕЕ: (1,0 + 2,0 + 3,0) / 3 = 2,0
Я надеюсь, что это проливает больше света на эту небольшую проблему.