У меня есть несколько больших файлов данных, из которых мне нужно извлечь некоторые комбинации столбца / строки. Мне удалось сделать это с помощью пары циклов, зная, что это будет очень медленно, и мне нужно значительно улучшить скорость.
Чтобы объяснить ситуацию, я подготовил следующий кадр данных:
time = ['2020-02-01T00:00:00', '2020-02-01T00:05:00', '2020-02-01T00:10:00','2020-02-01T00:15:00', '2020-02-02T00:00:00', '2020-02-02T00:05:00', '2020-02-02T00:10:00','2020-02-02T00:15:00', '2020-02-03T00:00:00', '2020-02-03T00:05:00', '2020-02-03T00:10:00']
testframe = pd.DataFrame(columns = ['datetime', 'point1', 'point2', 'point3'])
testframe['datetime'] = time
testframe['datetime'] = pd.to_datetime(testframe['datetime'])
testframe['point1'] = [1,1,1,np.nan,1,1,1,np.nan,1,1,1]
testframe['point2'] = [ np.nan, np.nan,2,2,2,np.nan, np.nan, np.nan,2,2,2]
testframe['point3'] = [3,3,np.nan,3,3,3,np.nan, np.nan, np.nan,2,2]
Моя цель состоит в том, чтобы иметь такой кадр данных: снимок экрана окончательного кадра данных
Вот как я поступил:
Я хочу сгруппировать его по дням, поэтому я используйте
df_temp['classes'] = 'classname'
# create ID for each timeseries = 24 h
df_temp['id_day'] = df_temp.groupby(df_temp.datetime.dt.date).ngroup() + 1
#reorganize columns
cols = df_temp.columns.tolist()
cols = cols[-2:] + cols[:-2]
df_temp = df_temp[cols]
На следующем шаге я проверил, сколько значений (не NaN) существует в день (на id_day), потому что мне интересны только дни с определенным количеством значений. Поэтому я использовал следующий код:
# Calculate values per day for each datapoint in dataframe
val_per_day = pd.DataFrame(columns = df_temp.columns.tolist()).drop(['classes', 'datetime'], axis = 1)
for point in df_temp.columns.tolist()[3:]: # [3:0] start with id and include all other columns
val_per_day[point] = df_temp[point].groupby(df_temp['id_day']).count()
val_per_day['id_day'] = val_per_day.index
val_per_day = val_per_day.reset_index(drop = True)
, который дает мне хороший фрейм данных, показывающий id_day, point1, point2, point3
и сколько значений эти точки содержат в день. До этого момента все достаточно ясно и работает достаточно быстро.
Теперь я отфильтровал этот список, чтобы выяснить, у каких дней больше значений, чем порог.
# set threshold
threshold =3
days_above_threshold = pd.DataFrame(columns = val_per_day.columns.to_numpy(), index = [1])
list_of_days = []
relevant_points_dic = {}
for point in val_per_day.columns.to_numpy()[1:]: # [1:], because 0 is column "id"
list_temp = val_per_day['id_day'][val_per_day[point] >= threshold].to_numpy()
if list_temp.size != 0:
relevant_points_dic.update( {point : list_temp})
else:
None
relevant_days = pd.DataFrame.from_dict(relevant_points_dic, orient='index', dtype = 'int').transpose()
Это дает мне фрейм данных с point1, point2, point3
в качестве столбцов и все "релевантные" дни в качестве значений.
Следующий шаг невероятно медленный и может быть улучшен:
I l oop через все столбцы исходный фрейм данных, проверьте, уместен ли id_day, и если да, я поместил данные в фрейм временных данных. наконец, я сопоставляю этот временный фрейм данных с последним фреймом данных (df). Код следующий:
df = pd.DataFrame()
counter = 1
#print("Begin loading " + str(len(relevant_days.columns.to_numpy())) + " points ...")
for point in relevant_days.columns.to_numpy()[:]:
df_new_temp = pd.DataFrame(columns = ['time', 'id_day', 'classes', 'name', 'value'])
if (counter % 20 == 0):
print("Loaded "+ str(counter) + " / " + str(len(relevant_days.columns.to_numpy())))
counter += 1
relevant_ids_raw =relevant_days[point].to_numpy()
relevant_ids_nan = np.isnan(relevant_ids_raw)
relevant_ids_not_nan = ~ relevant_ids_nan
relevant_ids = relevant_ids_raw[relevant_ids_not_nan]
df_new_temp['id_day'] = df_temp.loc[df_temp['id_day'].isin(relevant_ids), 'id_day']
df_new_temp['value'] = df_temp.loc[df_temp['id_day'].isin(relevant_ids), point]
df_new_temp['classes'] = df_temp.loc[df_temp['id_day'].isin(relevant_ids), 'classes']
df_new_temp['time'] = df_temp.loc[df_temp['id_day'].isin(relevant_ids), 'datetime']
df_new_temp['name'] = point
df = pd.concat([df, df_new_temp], ignore_index = True)
Проверяя его с помощью line_profiler, я получаю следующий журнал:
Line # Hits Time Per Hit % Time Line Contents
==============================================================
5 def copy_data(df_temp, relevant_days):
6
7 1 857.0 857.0 2.1 df = pd.DataFrame()
8 1 1.0 1.0 0.0 counter = 1
9 1 114.0 114.0 0.3 print("Begin loading " + str(len(relevant_days.columns.to_numpy())) + " points ...")
10 4 19.0 4.8 0.0 for point in relevant_days.columns.to_numpy()[:]:
11
12 3 15265.0 5088.3 38.1 df_new_temp = pd.DataFrame(columns = ['time', 'id_day', 'classes', 'name', 'value'])
13
14 3 4.0 1.3 0.0 if (counter % 20 == 0):
15 print("Loaded "+ str(counter) + " / " + str(len(relevant_days.columns.to_numpy())))
16 3 3.0 1.0 0.0 counter += 1
17
18 3 136.0 45.3 0.3 relevant_ids_raw =relevant_days[point].to_numpy()
19 3 14.0 4.7 0.0 relevant_ids_nan = np.isnan(relevant_ids_raw)
20 3 6.0 2.0 0.0 relevant_ids_not_nan = ~ relevant_ids_nan
21 3 12.0 4.0 0.0 relevant_ids = relevant_ids_raw[relevant_ids_not_nan]
22
23 3 7389.0 2463.0 18.4 df_new_temp['id_day'] = df_temp.loc[df_temp['id_day'].isin(relevant_ids), 'id_day']
24 3 4589.0 1529.7 11.4 df_new_temp['value'] = df_temp.loc[df_temp['id_day'].isin(relevant_ids), point]
25 3 3190.0 1063.3 8.0 df_new_temp['classes'] = df_temp.loc[df_temp['id_day'].isin(relevant_ids), 'classes']
26 3 3978.0 1326.0 9.9 df_new_temp['time'] = df_temp.loc[df_temp['id_day'].isin(relevant_ids), 'datetime']
27 3 656.0 218.7 1.6 df_new_temp['name'] = point
28
29 3 3638.0 1212.7 9.1 df = pd.concat([df, df_new_temp], ignore_index = True)
30
31
32 1 208.0 208.0 0.5 print("Finished loading data.")
33 1 1.0 1.0 0.0 return df
Что я уже пробовал:
-
Я пытался каким-то образом использовать .lo c, чтобы добавить данные из l oop в конечный кадр данных. Это не сработало для меня, так как в некоторых циклах у меня больше строк, чем в других, что вызывает некоторую pandas -индексную проблему.
Я пытался как-то поставить данные из l oop в список и сразу присоединить их в конце. Опять же, это вызвало проблемы с неравными размерами, а также со многими списками в списках, с которыми я не мог справиться (возможно, нехватка опыта).
Я пытался объяснить свои проблема, если есть какие-либо вопросы, я постараюсь ответить на них, и я ценю каждый намек на это дело. Этот код занял несколько часов для выполнения лишь небольшой части работы, поэтому любое ускорение приветствуется!