import pandas as pd
import numpy as np
import datetime as date
import itertools
player_list = ['player' + str(x) for x in range(1,71)]
data = pd.DataFrame({'Names': player_list*1000,\
'Ob1' : np.random.rand(70000),\
'Ob2' : np.random.rand(70000) ,\
'Ob3' : np.random.rand(70000)})
data['Test'] = np.where(data['Ob2'] > 0.5, np.where(data['Ob3'] - data['Ob1'] < 0.1, 1 - (data['Ob3'] - data['Ob1']), 0), 0)
comboNames = list(itertools.combinations(data.Names.unique(), 2))
DataFrameDict = {elem : pd.DataFrame for elem in comboNames}
for key in DataFrameDict.keys():
DataFrameDict[key] = data[:][data.Names.isin(key)]
DataFrameDict[key] = DataFrameDict[key].sort_values(['Ob1'])
headers = ['Player1','Player2','Score','Count']
summary = pd.DataFrame(([tbl[0], tbl[1], DataFrameDict[tbl]['Test'].sum(),
DataFrameDict[tbl]['Test'].astype(bool).sum(axis=0)] for tbl in DataFrameDict),
columns=headers).sort_values(['Score'], ascending=[False])
Я старался сохранить как можно больше вашего кода.Я изменил вашу функцию, чтобы использовать np.where вместо apply, и добавил столбец test перед созданием dict, потому что, как я выразил в своем комментарии, нет смысла применять на этом этапе.
With %%timeit
Я получил 26,2 с ± 1,15 с за цикл (среднее ± стандартное отклонение из 7 циклов, по 1 циклу каждый)
РЕДАКТИРОВАТЬ:
Это так быстрокак я мог это сделать:
%%timeit
player_list = ['player' + str(x) for x in range(1,71)]
data = pd.DataFrame({'Names': player_list*1000,\
'Ob1' : np.random.rand(70000),\
'Ob2' : np.random.rand(70000) ,\
'Ob3' : np.random.rand(70000)})
# Calculating the individual total test score for each row in data
data['test'] = np.where(data['Ob2'] > 0.5, np.where(data['Ob3'] - data['Ob1'] < 0.1, 1 - (data['Ob3'] - data['Ob1']), 0), 0)
# The goal of this function is to get the sum, and count of the test score for each player
def ScoreAndCount(row):
score = row.sum()
count = row.astype(bool).sum()
return score, count
# Applying the function above, I group by each player and
# get the total sum of test and the total count for each player.
df = data.groupby('Names')['test'].apply(ScoreAndCount).reset_index()
df = pd.concat([df['Names'], df.test.apply(pd.Series).rename(columns = {0: 'Score', 1:'Count'})], axis = 1)
# Using itertools I create a dataframe Summary that has two columns covering
# every single matchup between player, and label the columns Player1 and Player2
summary = pd.DataFrame(itertools.combinations(data.Names.unique(), 2), columns = ['Player1', 'Player2'])
# Below ,I merge summary with my dataframe that contains the total score and count
# for each player. Every single time there is a player1 in the Player1 column it
# will merge the their total score and count, the same is then done for the
# players in the Player2 column. After these merges I have 6 columns. The two
# player columns, and the total scores and counts for both individuals.
summary = summary.merge(df, left_on = 'Player1', right_on = 'Names')\
.merge(df, left_on = 'Player2', right_on = 'Names')\
.drop(columns = ['Names_x', 'Names_y'])
# Below, I add the players 'Score' and 'Count' columns to get the total score
# and total count per iteration. Then I clean the df dropping the columns that
# are not needed, and sorting by score.
summary['Score'] = summary['Score_x'] + summary['Score_y']
summary['Count'] = summary['Count_x'] + summary['Count_y']
summary.drop(columns = ['Score_x','Count_x', 'Score_y','Count_y'], inplace = True)
summary.sort_values('Score', ascending = False)
157 ms ± 1.19 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
Моя цель состояла в том, чтобы не использовать циклы или дикты для дальнейшего увеличения скорости.
Моя функция ScoreAndCount возвращает то, что будет счетом и счетом для каждогоигрок.Pd.concat возвращает результат этой функции и добавляет его к нашему начальному df.
Затем я взял комбо ittertools и создал его собственный фрейм данных с именем summary.Затем я объединил оба столбца player1 и player2 сводного df со столбцом names в оригинальном df.
Затем я сложил оценки и подсчеты для игроков, отбросил ненужные столбцы и отсортировал.Я закончил с 157 мс на цикл.Самыми медленными шагами были бы конкат и слияние, но я не мог найти способ обойти их и еще больше увеличить скорость.
EDIT3
We 'Собираемся установить начальное число и использовать одни и те же данные df для обоих тестов:
np.random.seed(0)
player_list = ['player' + str(x) for x in range(1,71)]
data = pd.DataFrame({'Names': player_list*10,\
'Ob1' : np.random.rand(700),\
'Ob2' : np.random.rand(700) ,\
'Ob3' : np.random.rand(700)})
data.head()
Names Ob1 Ob2 Ob3
0 player1 0.548814 0.373216 0.313591
1 player2 0.715189 0.222864 0.365539
2 player3 0.602763 0.080532 0.201267
3 player4 0.544883 0.085311 0.487148
4 player5 0.423655 0.221396 0.990369
Далее мы будем использовать ваш точный код и проверять разницу между player1 и player2.
def points(row):
val = 0
if row['Ob2'] > 0.5:
foo = row['Ob3'] - row['Ob1']
if foo < 0.1:
val = 1 - foo
else:
val = 0
return val
#create list of unique pairs
comboNames = list(itertools.combinations(data.Names.unique(), 2))
DataFrameDict = {elem : pd.DataFrame for elem in comboNames}
for key in DataFrameDict.keys():
DataFrameDict[key] = data[:][data.Names.isin(key)]
DataFrameDict[key] = DataFrameDict[key].sort_values(['Ob1'])
#Add test calculated column
for tbl in DataFrameDict:
DataFrameDict[tbl]['Test'] = DataFrameDict[tbl].apply(points, axis=1)
DataFrameDict[('player1', 'player2')].head()
Names Ob1 Ob2 Ob3 Test
351 player2 0.035362 0.013509 0.384273 0.0
630 player1 0.062636 0.305047 0.571550 0.0
561 player2 0.133461 0.758194 0.964210 0.0
211 player2 0.216897 0.056877 0.417333 0.0
631 player2 0.241902 0.557987 0.983555 0.0
Далее мы сделаем то, что вы делаете в сводке, и возьмите сумму тестового столбца, это будет счет, сгенерированный для player1 и player2
DataFrameDict[('player1', 'player2')]['Test'].sum()
8.077455441105938
Таким образом, мы в итоге получим 8.0774.Теперь, если то, что я говорю, верно, если мы выполним мой код в Edit2, мы получим 8.077 для счета между player1 и player2.
data['test'] = np.where(data['Ob2'] > 0.5, np.where(data['Ob3'] - data['Ob1'] < 0.1, 1 - (data['Ob3'] - data['Ob1']), 0), 0)
def ScoreAndCount(row):
score = row.sum()
count = row.astype(bool).sum()
return score, count
df = data.groupby('Names')['test'].apply(ScoreAndCount).reset_index()
df = pd.concat([df['Names'], df.test.apply(pd.Series).rename(columns = {0: 'Score', 1:'Count'})], axis = 1)
summary = pd.DataFrame(itertools.combinations(data.Names.unique(), 2), columns = ['Player1', 'Player2'])
summary = summary.merge(df, left_on = 'Player1', right_on = 'Names')\
.merge(df, left_on = 'Player2', right_on = 'Names')\
.drop(columns = ['Names_x', 'Names_y'])
summary['Score'] = summary['Score_x'] + summary['Score_y']
summary['Count'] = summary['Count_x'] + summary['Count_y']
summary.drop(columns = ['Score_x','Count_x', 'Score_y','Count_y'], inplace = True)
summary = summary.sort_values('Score', ascending = False)
Теперь мы проверим ряд с player1 и player2
summary[(summary['Player1'] == 'player1')&(summary['Player2'] == 'player2')]
Player1 Player2 Score Count
0 player1 player2 8.077455 6.0
Как вы можете видеть, я вычислил тот же счет от player1 player2 до моего edit2, как вы сделали в своем коде.