interpolate locations, bunch of plots
This commit is contained in:
		
							parent
							
								
									0dcc79eee2
								
							
						
					
					
						commit
						63c59f4ce2
					
				
					 2 changed files with 602 additions and 111 deletions
				
			
		
							
								
								
									
										119
									
								
								foo.py
									
										
									
									
									
								
							
							
						
						
									
										119
									
								
								foo.py
									
										
									
									
									
								
							| 
						 | 
					@ -6,6 +6,13 @@ from math import floor
 | 
				
			||||||
from datetime import datetime
 | 
					from datetime import datetime
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def timestamp_range_seconds(start: pandas.Timestamp, end: pandas.Timestamp) -> typing.Iterator[pandas.Timestamp]:
 | 
				
			||||||
 | 
					    assert end >= start
 | 
				
			||||||
 | 
					    start = int(floor(start.timestamp()))
 | 
				
			||||||
 | 
					    end = int(floor(end.timestamp()))
 | 
				
			||||||
 | 
					    for second in range(start, end):
 | 
				
			||||||
 | 
					        yield pandas.Timestamp(second, unit='s')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def _add_accumulated_score(df: pandas.DataFrame):
 | 
					def _add_accumulated_score(df: pandas.DataFrame):
 | 
				
			||||||
    acc_col = pandas.Series([0.0]).repeat(len(df)).reset_index(drop=True)
 | 
					    acc_col = pandas.Series([0.0]).repeat(len(df)).reset_index(drop=True)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -16,33 +23,32 @@ def _add_accumulated_score(df: pandas.DataFrame):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    df['accumulated_score'] = acc_col
 | 
					    df['accumulated_score'] = acc_col
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
def load_score_log(path: str) -> pandas.DataFrame:
 | 
					def load_score_log(path: str) -> pandas.DataFrame:
 | 
				
			||||||
    return pandas.read_csv(path, sep=',',
 | 
					    return pandas.read_csv(path, sep=',',
 | 
				
			||||||
                           dtype={'score': int, 'sourcename': str, 'name': str, 'mapx': int, 'mapy': int},
 | 
					                           dtype={'score': int, 'sourcename': str, 'name': str, 'mapx': int, 'mapy': int},
 | 
				
			||||||
                           parse_dates=['when'], date_format='%d/%m/%Y %H:%M')
 | 
					                           parse_dates=['when'], date_format='%d/%m/%Y %H:%M')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def get_score_per(sourcename: str) -> float | None:
 | 
				
			||||||
def _calc_duration(row) -> int:
 | 
					    if sourcename == 'Capture':
 | 
				
			||||||
    score_per = None
 | 
					        return 1.0
 | 
				
			||||||
    if row['sourcename'] == 'Capture':
 | 
					    elif sourcename == 'Output Boost':
 | 
				
			||||||
        score_per = 1.0
 | 
					        return 0.1
 | 
				
			||||||
    elif row['sourcename'] == 'Output Boost':
 | 
					 | 
				
			||||||
        score_per = 0.1
 | 
					 | 
				
			||||||
    else:
 | 
					    else:
 | 
				
			||||||
        return 0
 | 
					        return None
 | 
				
			||||||
 | 
					 | 
				
			||||||
    return int(floor(row['score'] / score_per))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def _calc_event_start(row) -> pandas.Timestamp:
 | 
					 | 
				
			||||||
    return pandas.Timestamp(row['when'].timestamp() - row['seconds'], unit='s')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
def extend_score_log(scores: pandas.DataFrame):
 | 
					def extend_score_log(scores: pandas.DataFrame):
 | 
				
			||||||
    scores.sort_values('when', inplace=True)
 | 
					    scores.sort_values('when', inplace=True)
 | 
				
			||||||
    _add_accumulated_score(scores)
 | 
					    _add_accumulated_score(scores)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _calc_duration(row) -> int:
 | 
				
			||||||
 | 
					        score_per = get_score_per(row['sourcename'])
 | 
				
			||||||
 | 
					        if score_per is None:
 | 
				
			||||||
 | 
					            return 1
 | 
				
			||||||
 | 
					        return int(floor(row['score'] / score_per))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _calc_event_start(row) -> pandas.Timestamp:
 | 
				
			||||||
 | 
					        return pandas.Timestamp(row['when'].timestamp() - row['seconds'], unit='s')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    scores['seconds'] = scores.apply(_calc_duration, axis=1)
 | 
					    scores['seconds'] = scores.apply(_calc_duration, axis=1)
 | 
				
			||||||
    scores['when_start'] = scores.apply(_calc_event_start, axis=1)
 | 
					    scores['when_start'] = scores.apply(_calc_event_start, axis=1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -70,14 +76,12 @@ def generate_station_stats(score_log: pandas.DataFrame) -> pandas.DataFrame:
 | 
				
			||||||
    total_boosts['totalboostduration'] = total_boosts['score'].apply(lambda x: 10 * x)
 | 
					    total_boosts['totalboostduration'] = total_boosts['score'].apply(lambda x: 10 * x)
 | 
				
			||||||
    total_boosts.rename(columns={'score': 'totalboostscore'}, inplace=True)
 | 
					    total_boosts.rename(columns={'score': 'totalboostscore'}, inplace=True)
 | 
				
			||||||
    summary = pandas.merge(summary, total_boosts, **common_join_args)
 | 
					    summary = pandas.merge(summary, total_boosts, **common_join_args)
 | 
				
			||||||
    assert len(summary) == station_count
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # add max boosts
 | 
					    # add max boosts
 | 
				
			||||||
    max_boosts = boosts.max()
 | 
					    max_boosts = boosts.max()
 | 
				
			||||||
    max_boosts['maxboostduration'] = max_boosts['score'].apply(lambda x: 10 * x)
 | 
					    max_boosts['maxboostduration'] = max_boosts['score'].apply(lambda x: 10 * x)
 | 
				
			||||||
    max_boosts.rename(columns={'score': 'maxboostscore'}, inplace=True)
 | 
					    max_boosts.rename(columns={'score': 'maxboostscore'}, inplace=True)
 | 
				
			||||||
    summary = pandas.merge(summary, max_boosts, **common_join_args)
 | 
					    summary = pandas.merge(summary, max_boosts, **common_join_args)
 | 
				
			||||||
    assert len(summary) == station_count
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    visits = score_log[(score_log['sourcename'] == 'Visit') | (score_log['sourcename'] == 'First Visit')][
 | 
					    visits = score_log[(score_log['sourcename'] == 'Visit') | (score_log['sourcename'] == 'First Visit')][
 | 
				
			||||||
        ['name', 'score']].groupby('name')
 | 
					        ['name', 'score']].groupby('name')
 | 
				
			||||||
| 
						 | 
					@ -85,19 +89,16 @@ def generate_station_stats(score_log: pandas.DataFrame) -> pandas.DataFrame:
 | 
				
			||||||
    # add total visits (count)
 | 
					    # add total visits (count)
 | 
				
			||||||
    summary = pandas.merge(summary, visits.count(), **common_join_args)
 | 
					    summary = pandas.merge(summary, visits.count(), **common_join_args)
 | 
				
			||||||
    summary.rename(columns={'score': 'totalvisits'}, inplace=True)
 | 
					    summary.rename(columns={'score': 'totalvisits'}, inplace=True)
 | 
				
			||||||
    assert len(summary) == station_count
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    captures = score_log[score_log['sourcename'] == 'Capture'][['name', 'score']].groupby('name')
 | 
					    captures = score_log[score_log['sourcename'] == 'Capture'][['name', 'score']].groupby('name')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # add captures (count)
 | 
					    # add captures (count)
 | 
				
			||||||
    summary = pandas.merge(summary, captures.count(), **common_join_args)
 | 
					    summary = pandas.merge(summary, captures.count(), **common_join_args)
 | 
				
			||||||
    summary.rename(columns={'score': 'captures'}, inplace=True)
 | 
					    summary.rename(columns={'score': 'captures'}, inplace=True)
 | 
				
			||||||
    assert len(summary) == station_count
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # add max held duration (max capture score)
 | 
					    # add max held duration (max capture score)
 | 
				
			||||||
    summary = pandas.merge(summary, captures.max(), **common_join_args)
 | 
					    summary = pandas.merge(summary, captures.max(), **common_join_args)
 | 
				
			||||||
    summary.rename(columns={'score': 'maxheldduration'}, inplace=True)
 | 
					    summary.rename(columns={'score': 'maxheldduration'}, inplace=True)
 | 
				
			||||||
    assert len(summary) == station_count
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # add total held duration (sum capture score)
 | 
					    # add total held duration (sum capture score)
 | 
				
			||||||
    summary = pandas.merge(summary, captures.sum(), **common_join_args)
 | 
					    summary = pandas.merge(summary, captures.sum(), **common_join_args)
 | 
				
			||||||
| 
						 | 
					@ -119,20 +120,23 @@ def generate_score_per_second(score_log: pandas.DataFrame) -> pandas.DataFrame:
 | 
				
			||||||
        mapx: int
 | 
					        mapx: int
 | 
				
			||||||
        mapy: int
 | 
					        mapy: int
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def row_to_scoreseconds(row) -> typing.Iterator[ScoreSecond]:
 | 
					 | 
				
			||||||
        score_per = row.score / row.seconds
 | 
					 | 
				
			||||||
        for elapsed in range(0, row.seconds):
 | 
					 | 
				
			||||||
            timestamp = pandas.Timestamp(row.when_start.timestamp() + elapsed, unit='s')
 | 
					 | 
				
			||||||
            yield ScoreSecond(name=row.name, sourcename=row.sourcename, mapx=row.mapx, mapy=row.mapy, when=timestamp,
 | 
					 | 
				
			||||||
                              score=score_per, once=False, event_start=(elapsed == 0))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def gen_scoreseconds() -> typing.Iterator[ScoreSecond]:
 | 
					    def gen_scoreseconds() -> typing.Iterator[ScoreSecond]:
 | 
				
			||||||
        for row in score_log.itertuples():
 | 
					        for row in score_log.itertuples():
 | 
				
			||||||
            if row.seconds == 0:  # one-off
 | 
					            # TODO: the code below should work with 0s now
 | 
				
			||||||
 | 
					            if row.seconds < 2:  # one-off
 | 
				
			||||||
                yield ScoreSecond(name=row.name, sourcename=row.sourcename, mapx=row.mapx, mapy=row.mapy, when=row.when,
 | 
					                yield ScoreSecond(name=row.name, sourcename=row.sourcename, mapx=row.mapx, mapy=row.mapy, when=row.when,
 | 
				
			||||||
                                  score=row.score, once=True, event_start=True)
 | 
					                                  score=row.score, once=True, event_start=True)
 | 
				
			||||||
            else:
 | 
					                continue
 | 
				
			||||||
                yield from row_to_scoreseconds(row)
 | 
					
 | 
				
			||||||
 | 
					            once = row.seconds == 1
 | 
				
			||||||
 | 
					            score_per = get_score_per(row.sourcename)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            event_start = True
 | 
				
			||||||
 | 
					            for when in timestamp_range_seconds(row.when_start, row.when):
 | 
				
			||||||
 | 
					                yield ScoreSecond(when=when, once=once, event_start=event_start,
 | 
				
			||||||
 | 
					                                  name=row.name, sourcename=row.sourcename, mapx=row.mapx, mapy=row.mapy,
 | 
				
			||||||
 | 
					                                  score=score_per, )
 | 
				
			||||||
 | 
					                event_start = False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    scoreseconds = pandas.DataFrame(gen_scoreseconds())
 | 
					    scoreseconds = pandas.DataFrame(gen_scoreseconds())
 | 
				
			||||||
    scoreseconds.sort_values(by=['when'], inplace=True)
 | 
					    scoreseconds.sort_values(by=['when'], inplace=True)
 | 
				
			||||||
| 
						 | 
					@ -140,3 +144,56 @@ def generate_score_per_second(score_log: pandas.DataFrame) -> pandas.DataFrame:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    _add_accumulated_score(scoreseconds)
 | 
					    _add_accumulated_score(scoreseconds)
 | 
				
			||||||
    return scoreseconds
 | 
					    return scoreseconds
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def get_known_player_locations(score_log):
 | 
				
			||||||
 | 
					    locations = score_log[score_log['mapx'] != 0][['name', 'when_start', 'mapx', 'mapy']].copy()
 | 
				
			||||||
 | 
					    locations.rename(columns={'when_start': 'when'}, inplace=True)
 | 
				
			||||||
 | 
					    locations.sort_values(by=['when'], inplace=True)
 | 
				
			||||||
 | 
					    locations.reset_index(drop=True, inplace=True)
 | 
				
			||||||
 | 
					    return locations
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def interpolate_player_locations(locations: pandas.DataFrame) -> pandas.DataFrame:
 | 
				
			||||||
 | 
					    from dataclasses import dataclass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    IGNORED_GAP_SECONDS = 60 * 60 * 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @dataclass
 | 
				
			||||||
 | 
					    class LocationSecond:
 | 
				
			||||||
 | 
					        when: pandas.Timestamp
 | 
				
			||||||
 | 
					        mapx: int
 | 
				
			||||||
 | 
					        mapy: int
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def interpolate_locations():
 | 
				
			||||||
 | 
					        skipped = False
 | 
				
			||||||
 | 
					        for pair in locations.rolling(window=2, closed='right'):
 | 
				
			||||||
 | 
					            if not skipped:
 | 
				
			||||||
 | 
					                skipped = True
 | 
				
			||||||
 | 
					                continue
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            left = pair.iloc[0]
 | 
				
			||||||
 | 
					            right = pair.iloc[1]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            start = left['when']
 | 
				
			||||||
 | 
					            end = right['when']
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            seconds = end.timestamp() - start.timestamp()
 | 
				
			||||||
 | 
					            if seconds > IGNORED_GAP_SECONDS:
 | 
				
			||||||
 | 
					                end = start
 | 
				
			||||||
 | 
					                seconds = 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            x = left['mapx']
 | 
				
			||||||
 | 
					            y = left['mapy']
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if seconds < 0.1:
 | 
				
			||||||
 | 
					                x_increment = 0
 | 
				
			||||||
 | 
					                y_increment = 0
 | 
				
			||||||
 | 
					            else:
 | 
				
			||||||
 | 
					                x_increment = (right['mapx'] - left['mapx']) / seconds
 | 
				
			||||||
 | 
					                y_increment = (right['mapy'] - left['mapy']) / seconds
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            for elapsed, timestamp in enumerate(timestamp_range_seconds(start, end)):
 | 
				
			||||||
 | 
					                yield LocationSecond(when=timestamp, mapx=x, mapy=y)
 | 
				
			||||||
 | 
					                x += x_increment
 | 
				
			||||||
 | 
					                y += y_increment
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return pandas.DataFrame(interpolate_locations())
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										594
									
								
								notebook.ipynb
									
										
									
									
									
								
							
							
						
						
									
										594
									
								
								notebook.ipynb
									
										
									
									
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue