Нужен более быстрый способ объединения данных - PullRequest
1 голос
/ 05 марта 2020

У меня есть несколько больших файлов данных, из которых мне нужно извлечь некоторые комбинации столбца / строки. Мне удалось сделать это с помощью пары циклов, зная, что это будет очень медленно, и мне нужно значительно улучшить скорость.

Чтобы объяснить ситуацию, я подготовил следующий кадр данных:

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 в список и сразу присоединить их в конце. Опять же, это вызвало проблемы с неравными размерами, а также со многими списками в списках, с которыми я не мог справиться (возможно, нехватка опыта).

Я пытался объяснить свои проблема, если есть какие-либо вопросы, я постараюсь ответить на них, и я ценю каждый намек на это дело. Этот код занял несколько часов для выполнения лишь небольшой части работы, поэтому любое ускорение приветствуется!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...