def test_chunk_halfcourt(self): # 1) check if all event satisfies required length event_length_th = 30 for e in self.event_dfs: result, _ = preprocessing.remove_non_eleven(e, event_length_th, verbose=True) df = pd.DataFrame({'moments': result}) result = preprocessing.chunk_shotclock(e, event_length_th, verbose=False) df = pd.DataFrame({'moments': result}) result = preprocessing.chunk_halfcourt(df, event_length_th, verbose=False) result_len = [len(i) for i in result] count = sum(np.array(result_len) >= event_length_th) self.assertEqual(count, len(result)) self.assertEqual(type(result[0]), list) half_court = 94 / 2. for i in result: for j in i: # 2) the players must either be on the left court or the right self.assertTrue( sum(np.array(j[5])[1:, 2] >= half_court) == 10 or sum(np.array(j[5])[1:, 2] <= half_court) == 10)
def test_flatten(self): half_court = 94/2. court_width = 50. # corresponding features col index player_x_ind = [0, 2, 4, 6, 8, 10, 12, 14, 16, 18] player_y_ind = [1, 3, 5, 7, 9, 11, 13, 15, 17, 19] bball_x_ind = [20] bball_y_ind = [21] bball_z_ind = [22] qtr_ind = [23] time_left_ind = [24] sc_ind = [25] def check_features(f_moment): # print(type(f_moment)) tol = 5.7 # a) the player positions shall all be greater than zero # note: tol here was originally tested for 0, but there can be cases where the player runs off court self.assertTrue(sum(f_moment[player_x_ind] >= -tol) == 10 and sum(f_moment[player_x_ind] <= half_court)==10, msg = '\033[91m player x coordinates: {}\033[00m'.format(f_moment[player_x_ind])) self.assertTrue(sum(f_moment[player_y_ind] >= -tol) == 10 and sum(f_moment[player_y_ind] <= court_width+tol)==10, msg = '\033[91m player y coordinates: {}\033[00m'.format(f_moment[player_y_ind])) # b) bball positions self.assertTrue(f_moment[bball_x_ind] >= -tol and f_moment[bball_x_ind] <= half_court + tol, msg='\033[91m bball x: {}\033[00m'.format(f_moment[bball_x_ind])) self.assertTrue(f_moment[bball_y_ind] >= 0 and f_moment[bball_y_ind] <= court_width + tol, msg='\033[91m bball y: {}\033[00m'.format(f_moment[bball_y_ind])) self.assertTrue(f_moment[bball_z_ind] >= 0, msg='bball z: {}'.format(f_moment[bball_z_ind])) # c) quarter number self.assertTrue(f_moment[qtr_ind] >= 1 and f_moment[qtr_ind] <= 4., msg='\033[91m quarter number: {}\033[00m'.format(f_moment[qtr_ind])) # d) time left to the end of the period in seconds (12 mins per period) self.assertTrue(f_moment[time_left_ind] >= 0 and f_moment[sc_ind] <= 12.*60, msg='\033[91m time left: {}\033[00m'.format(f_moment[time_left_ind])) # e) shot clock self.assertTrue(f_moment[sc_ind] >= 0 and f_moment[sc_ind] <= 24., msg='\033[91m shot clock: {}\033[00m'.format(f_moment[sc_ind])) event_length_th = 30 for k, e in enumerate(self.event_dfs): result, _ = preprocessing.remove_non_eleven(e, event_length_th, verbose=True) df = pd.DataFrame({'moments': result}) result = preprocessing.chunk_shotclock(e, event_length_th, verbose=False) df = pd.DataFrame({'moments': result}) result = preprocessing.chunk_halfcourt(df, event_length_th, verbose=False) df = pd.DataFrame({'moments': result}) result = preprocessing.reorder_teams(df, self.game_ids[k]) df = pd.DataFrame({'moments': result}) flattened, team_ids = preprocessing.flatten_moments(df) # team_id # 1) the team_ids should not be the same but share the same several front digits [self.assertTrue(team_id[0] != team_id[1], msg='\033[91m team ids: {} | {}\033[00m'.format(team_id[0], team_id[1])) for team_id in team_ids] [self.assertTrue(str(team_id[0])[:8] == str(team_id[0])[:8] == '16106127',\ msg='\033[91m team ids: {} | {}\033[00m'.format(team_id[0], team_id[1])) for team_id in team_ids] # 2) check the values from the features [check_features(j) for i in flattened for j in i]
def test_chunk_shotclock(self): # 1) check if all event satisfies required length event_length_th = 30 for e in self.event_dfs: result, _ = preprocessing.remove_non_eleven(e, event_length_th, verbose=True) df = pd.DataFrame({'moments': result}) result = preprocessing.chunk_shotclock(df, event_length_th, verbose=False) result_len = [len(i) for i in result] count = sum(np.array(result_len) >= event_length_th) self.assertEqual(count, len(result)) self.assertEqual(type(result[0]), list) for i in result: # 2) check if None is in the shot clock from result self.assertNotIn(None, [j[3] for j in i]) # 3) check if the shot clock is in right order self.assertTrue(i[0][3] > i[-1][3]) # 4) check if the first two and last two shotclock value are different self.assertTrue(i[0][3] != i[1][3]) self.assertTrue(i[-1][3] != i[-2][3])
def test_ohe(self): def check_ohe(moments): one_hots = moments[:, 129:] # each row should only be two 1s from the one hot encoding of two teams self.assertEqual(sum(np.sum(one_hots, axis=1) == 2), len(moments)) # there should be two columns that contain 1s self.assertEqual(sum(np.sum(one_hots, axis=0) != 0), 2) event_length_th = 30 for k, e in enumerate(self.event_dfs): result, _ = preprocessing.remove_non_eleven(e, event_length_th, verbose=True) df = pd.DataFrame({'moments': result}) result = preprocessing.chunk_shotclock(e, event_length_th, verbose=False) df = pd.DataFrame({'moments': result}) result = preprocessing.chunk_halfcourt(df, event_length_th, verbose=False) df = pd.DataFrame({'moments': result}) result = preprocessing.reorder_teams(df, self.game_ids[k]) df = pd.DataFrame({'moments': result}) flattened, team_ids = preprocessing.flatten_moments(df) df = pd.DataFrame({'moments': flattened}) static_result = preprocessing.create_static_features(df) df = pd.DataFrame({'moments': copy.deepcopy(static_result)}) fs = 1/25. dynamic_result = preprocessing.create_dynamic_features(df, fs) OHE = preprocessing.OneHotEncoding() result = OHE.add_ohs(dynamic_result, team_ids) [check_ohe(ms) for ms in result]
def test_dynamic_features(self): def check_dynamics(static_moments, dynamic_moments): pxy = static_moments[:, :23] next_pxy = copy.deepcopy(pxy[1:, :23]) fs = 1/25. # time difference between each frame vel = ((next_pxy-pxy[:-1])/fs) # we have shifted the velocity to be v2=x2-x1 vel_list = vel.tolist() dynamic_list = dynamic_moments[:, 106:129].tolist() self.assertEqual(len(vel_list), len(dynamic_list)) [self.assertListEqual(vel_list[i], dynamic_list[i]) for i in range(len(vel_list))] event_length_th = 30 for k, e in enumerate(self.event_dfs): result, _ = preprocessing.remove_non_eleven(e, event_length_th, verbose=True) df = pd.DataFrame({'moments': result}) result = preprocessing.chunk_shotclock(e, event_length_th, verbose=False) df = pd.DataFrame({'moments': result}) result = preprocessing.chunk_halfcourt(df, event_length_th, verbose=False) df = pd.DataFrame({'moments': result}) result = preprocessing.reorder_teams(df, self.game_ids[k]) df = pd.DataFrame({'moments': result}) flattened, _ = preprocessing.flatten_moments(df) df = pd.DataFrame({'moments': flattened}) static_result = preprocessing.create_static_features(df) df = pd.DataFrame({'moments': copy.deepcopy(static_result)}) fs = 1/25. dynamic_result = preprocessing.create_dynamic_features(df, fs) self.assertEqual(len(static_result), len(dynamic_result)) [check_dynamics(static_result[i], dynamic_result[i]) for i in range(len(static_result))]
def test_reorder_teams(self): # now we want to reorder the team position based on meta data court_index = pd.read_csv('../meta_data/court_index.csv') court_index = dict(zip(court_index.game_id, court_index.court_position)) # 1) 42 teams mapping self.assertEqual(len(court_index), 42) half_court = 94 / 2. event_length_th = 25 for k, e in enumerate(self.event_dfs): # remove non eleven result, team_ids = preprocessing.remove_non_eleven(e, event_length_th, verbose=False) df = pd.DataFrame({'moments': result}) # chunk at 24s, None or stopped clocks result = preprocessing.chunk_shotclock(e, event_length_th, verbose=False) df = pd.DataFrame({'moments': result}) # chunk based on half court result = preprocessing.chunk_halfcourt(df, event_length_th, verbose=False) df = pd.DataFrame({'moments': result}) # 2) the homeid is always the first 5 players home_id, away_id = team_ids['home_id'], team_ids['away_id'] for i in result: for j in i: self.assertEqual(j[5][1][0], home_id) self.assertEqual(j[5][-1][0], away_id) n_events = len(result) result = preprocessing.reorder_teams(df, self.game_ids[k]) df = pd.DataFrame({'moments': result}) # 3) the returned number of events should not change self.assertEqual(n_events, len(result)) for i in result: for j in i: # 4) all players should be already normalized to left half court self.assertEqual(sum(np.array(j[5])[1:, 2] <= half_court), 10) # hardcode test cases: if k == '0021500196': correct = [[1610612761, 2449, 9.8067, 28.92548, 0.0], [1610612761, 201960, 24.3734, 21.43063, 0.0], [1610612761, 200768, 6.86594, 20.4174, 0.0], [1610612761, 201942, 17.89619, 25.21197, 0.0], [1610612761, 202687, 10.1885, 24.11858, 0.0], [1610612746, 1718, 34.33032, 10.36747, 0.0], [1610612746, 200755, 32.26942, 26.02228, 0.0], [1610612746, 101108, 18.86048, 7.87816, 0.0], [1610612746, 201599, 35.95785, 20.65878, 0.0], [1610612746, 201933, 25.66964, 40.3966, 0.0]] self.assertListEqual(result[0][0][5][1:], correct)
def test_remove_non_eleven(self): # 1) check if all event satisfies required length event_length_th = 30 for e in self.event_dfs: result, _ = preprocessing.remove_non_eleven(e, event_length_th, verbose=True) result_len = [len(i) for i in result] count = sum(np.array(result_len) >= event_length_th) self.assertEqual(count, len(result)) # 2) check type is list self.assertEqual(type(result[0]), list)
def setUpClass(cls): # directories main_dir = '../../' game_dir = main_dir + 'data/' Data = utilities.LoadData(main_dir, game_dir) # models_path = './models/' cls.game_ids = ['0021500196', '0021500024'] event_dfs = [ pd.DataFrame(Data.load_game(g)['events']) for g in cls.game_ids ] results = [] hsls = [] event_length_th = 30 for k, e in enumerate(event_dfs): result, _ = preprocessing.remove_non_eleven(e, event_length_th, verbose=True) df = pd.DataFrame({'moments': result}) result = preprocessing.chunk_shotclock(e, event_length_th, verbose=False) df = pd.DataFrame({'moments': result}) result = preprocessing.chunk_halfcourt(df, event_length_th, verbose=False) df = pd.DataFrame({'moments': result}) result = preprocessing.reorder_teams(df, cls.game_ids[k]) df = pd.DataFrame({'moments': result}) flattened, team_ids = preprocessing.flatten_moments(df) df = pd.DataFrame({'moments': flattened}) static_result = preprocessing.create_static_features(df) df = pd.DataFrame({'moments': copy.deepcopy(static_result)}) fs = 1 / 25. dynamic_result = preprocessing.create_dynamic_features(df, fs) OHE = preprocessing.OneHotEncoding() result = OHE.add_ohs(dynamic_result, team_ids) df = pd.DataFrame({'moments': result}) results.append(result) hsls.append(hidden_role_learning.HiddenStructureLearning(df)) cls.results = results cls.hsls = hsls
def test_static_features(self): # index related to polar displacement with bball r_ball_ind = list(range(26, 36)) cos_ball_ind = list(range(36, 46)) sin_ball_ind = list(range(46, 56)) theta_ball_ind = list(range(56, 66)) # hoop r_hoop_ind = list(range(66, 76)) cos_hoop_ind = list(range(76, 86)) sin_hoop_ind = list(range(86, 96)) theta_hoop_ind = list(range(96, 106)) diag = np.sqrt(50**2 + 94**2) def check_static(f_moment): # a1) the player displacements to the ball self.assertTrue(sum(f_moment[r_ball_ind] > 0) == 10 and sum(f_moment[r_ball_ind] <= diag)==10, msg = '\033[91m player r ball displacements: {}\033[00m'.format(f_moment[r_ball_ind])) # a2) the player displacements to the hoop self.assertTrue(sum(f_moment[r_hoop_ind] > 0) == 10 and sum(f_moment[r_hoop_ind] <= diag)==10, msg = '\033[91m player r hoop displacements: {}\033[00m'.format(f_moment[r_hoop_ind])) # b1) the player cos to the ball self.assertTrue(sum(f_moment[cos_ball_ind] >= -1) == 10 and sum(f_moment[cos_ball_ind] <= 1)==10, msg = '\033[91m player r ball cos: {}\033[00m'.format(f_moment[cos_ball_ind])) # b2) the player cos to the hoop self.assertTrue(sum(f_moment[cos_hoop_ind] >= -1) == 10 and sum(f_moment[cos_hoop_ind] <= 1)==10, msg = '\033[91m player r hoop cos: {}\033[00m'.format(f_moment[cos_hoop_ind])) # c1) the player sin to the ball self.assertTrue(sum(f_moment[sin_ball_ind] >= -1) == 10 and sum(f_moment[sin_ball_ind] <= 1)==10, msg = '\033[91m player r ball sine: {}\033[00m'.format(f_moment[sin_ball_ind])) # c2) the player sin to the hoop self.assertTrue(sum(f_moment[sin_hoop_ind] >= -1) == 10 and sum(f_moment[sin_hoop_ind] <= 1)==10, msg = '\033[91m player r hoop sine: {}\033[00m'.format(f_moment[sin_hoop_ind])) # d1) the player theta to the ball self.assertTrue(sum(f_moment[theta_ball_ind] >= 0) == 10 and sum(f_moment[theta_ball_ind] <= np.pi)==10, msg = '\033[91m player r ball theta: {}\033[00m'.format(f_moment[theta_ball_ind])) # d2) the player sin to the ball self.assertTrue(sum(f_moment[theta_hoop_ind] >= 0) == 10 and sum(f_moment[theta_hoop_ind] <= np.pi)==10, msg = '\033[91m player r hoop theta: {}\033[00m'.format(f_moment[theta_hoop_ind])) # e1) cos^2 + sin^2 = 1 ball e_tol = 1e-5 self.assertTrue(sum((f_moment[cos_ball_ind]**2 + f_moment[sin_ball_ind]**2 - 1) < e_tol) == 10, msg = '\033[91m sin^2 + cos^2 =1 ball: {}\033[00m'.format(f_moment[cos_ball_ind]**2 + f_moment[sin_ball_ind]**2)) # e2) hoop self.assertTrue(sum((f_moment[cos_hoop_ind]**2 + f_moment[sin_hoop_ind]**2 - 1) < e_tol) == 10, msg = '\033[91m sin^2 + cos^2 =1 hoop: {}\033[00m'.format(f_moment[cos_hoop_ind]**2 + f_moment[sin_hoop_ind]**2)) event_length_th = 30 for k, e in enumerate(self.event_dfs): result, _ = preprocessing.remove_non_eleven(e, event_length_th, verbose=True) df = pd.DataFrame({'moments': result}) result = preprocessing.chunk_shotclock(e, event_length_th, verbose=False) df = pd.DataFrame({'moments': result}) result = preprocessing.chunk_halfcourt(df, event_length_th, verbose=False) df = pd.DataFrame({'moments': result}) result = preprocessing.reorder_teams(df, self.game_ids[k]) df = pd.DataFrame({'moments': result}) flattened, _ = preprocessing.flatten_moments(df) df = pd.DataFrame({'moments': flattened}) result = preprocessing.create_static_features(df) [check_static(i[j]) for i in result for j in range(i.shape[0])]