ПРОБЛЕМА: у меня есть фрейм данных, показывающий, какие задания выбрали ученики и какие оценки они за них получили. Я пытаюсь определить, какие подмножества заданий были выполнены большинством учащихся и общее количество баллов, заработанных за них. Метод, который я использую, очень медленный, поэтому мне интересно, какой самый быстрый способ.
Мои данные имеют такую структуру:
ЦЕЛЕВОЙ ВЫХОД: Для каждой возможной комбинации заданий я пытаюсь получить количество выполненных заданий и сумму баллов, заработанных за каждое отдельное задание подмножеством учащихся, выполнивших именно эту комбинацию заданий:
ЧТО Я ПРОБЫЛ: Во-первых, я использую itertools для создания комбинаций заданий, а затем перебираю фреймворк данных, чтобы классифицировать каждого ученика по тому, какие комбинации заданий они выполнили:
for combo in itertools.product(list_of_assignment_names, repeat=20):
for i, row in starting_data.iterrows():
ifor = str(combo)
ifor_val = 'no'
for item in combo:
if row[str(item)]>0:
ifor_val = 'yes'
starting_data.at[i,ifor] = ifor_val
Затем я создаю второй фрейм данных (assignmentcombostats), в котором каждая комбинация представлена в виде строки для подсчета количества студентов, выполнивших каждую комбинацию:
numberofstudents =[]
for combo in assignmentcombostats['combo']:
column = str(combo)
number = len(starting_data[starting_data[column] == 'yes'])
numberofstudents.append(number)
assignmentcombostats['numberofstudents'] = numberofstudents
Это работает, но очень медленно.
РЕСУРСЫ: я просмотрел несколько ресурсов -
Можете ли вы дать мне представление, как это будет работать здесь?
Я попробовал навести порядок Ответ Брайана
Настройка: (составляет набор данных из 20 000 учащихся и 10 заданий)
import itertools
import pandas as pd
import numpy as np
# Bigger random sample data
def make_data(rows, cols, nans, non_nans):
df = pd.DataFrame()
df["student"] = list(range(rows))
for i in range(1,cols+1):
a = np.random.randint(low=1-nans, high=non_nans, size=(rows)).clip(0).astype(float)
a[ a <= 0 ] = np.nan
df[f"a{i:02}"] = a
return df
rows = 20000
cols = 10
df = make_data(rows, cols, 50, 50)
# dummy columns, makes aggregates easier
df["students"] = 1
df["combo"] = ""
Трансформация:
# create a list of all possible assignment combinations (ignore first and last two)
assignments = df.columns[1:-2].tolist()
combos = []
for r in range(1, len(assignments)+1):
new_combos = list(itertools.combinations(assignments, r))
combos += new_combos
# create a list to hold the results
results = list(range(len(combos)))
# ignore the student identifier column
df_source = df.iloc[:, 1:]
# iterate over the combinations and compute the results
for ix, combo in enumerate(combos):
# filter the dataframe for students who have completed this combo
df_filter = df.loc[ df[ list(combo) ].notnull().all(axis=1) ]
# aggregate the results to a single row (sum of the dummy students column counts the rows)
df_agg = df_filter.groupby("combo", as_index=False).sum().reset_index(drop=True)
# store the assignment comination in the results
df_agg["combo"] = ",".join(combo)
# add the results to the list
results[ix] = df_agg
# create a new dataframe from the results list
combo_stats_df = pd.concat(results).reset_index(drop = True)
В этой демонстрации требуется ~6 секунд, чтобы вернуть ~1000 строк результатов.
Для 20 заданий это ~1 000 000 строк результатов, то есть ~6000 секунд (более 1,5 часов).
Даже на моем рабочем столе обработка 1000 комбинаций занимает ~2 секунды, то есть ~0,5 часа на ~1 000 000 комбинаций из 20 заданий.
Сначала я пытался написать его без цикла, но процесс был остановлен из-за использования слишком большого объема памяти. Мне нравится головоломка, она помогает мне учиться, поэтому я подумаю, есть ли способ избежать петли, оставаясь в памяти.
Вы пытались использовать какие-либо собственные функции панд? groupby и т.д.? Pandas по своей сути является столбчатым, поэтому итерация по его строкам очень медленная, а не так, как предполагается использовать pandas...