Uh oh!
There was an error while loading. Please reload this page.
This repository was archived by the owner on Apr 11, 2023. It is now read-only.
- Notifications
You must be signed in to change notification settings - Fork 407
Expand file tree
/
Copy pathrelevanceeval.py
More file actions
Latest commit
129 lines (102 loc) · 5.84 KB
/
Copy pathrelevanceeval.py
File metadata and controls
129 lines (102 loc) · 5.84 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
#!/usr/bin/env python
"""
Usage:
relevanceeval.py [options] RELEVANCE_ANNOTATIONS_CSV_PATH MODEL_PREDICTIONS_CSV
Standalone relevance evaluation script that outputs evaluation statistics for a set of predictions of a given model.
The input formats of the files is described below.
The model predictions MODEL_PREDICTIONS_CSV file has the following format:
A comma-separated file with (at least) the fields and headers "language", "query", "url". Each row represents
a single result for a given query and a given programming language.
* language: the programming language for the given query, e.g. "python"
* query: the textual representation of the query, e.g. "int to string"
* url: the unique GitHub URL to the returned results, e.g. "https://github.com/JamesClonk/vultr/blob/fed59ad207c9bda0a5dfe4d18de53ccbb3d80c91/cmd/commands.go#L12-L190"
The order of the rows imply the ranking of the results in the search task. For example, if in row 5 there is
an entry for the Python query "read properties file" and then in row 60 appears another result for the
Python query "read properties file", then the URL in row 5 is considered to be ranked higher than the
URL in row 60 for that query and language.
Options:
--debug Run in debug mode, falling into pdb on exceptions.
-h --help Show this screen.
"""
fromcollectionsimportdefaultdict
fromtypingimportDict, List
importnumpyasnp
importpandasaspd
fromdocoptimportdocopt
fromdpu_utils.utilsimportrun_and_debug
defload_relevances(filepath: str) ->Dict[str, Dict[str, Dict[str, float]]]:
relevance_annotations=pd.read_csv(filepath)
per_query_language=relevance_annotations.pivot_table(
index=['Query', 'Language', 'GitHubUrl'], values='Relevance', aggfunc=np.mean)
# Map language -> query -> url -> float
relevances=defaultdict(lambda: defaultdict(dict)) # type: Dict[str, Dict[str, Dict[str, float]]]
for (query, language, url), relevanceinper_query_language['Relevance'].items():
relevances[language.lower()][query.lower()][url] =relevance
returnrelevances
defload_predictions(filepath: str, max_urls_per_language: int=300) ->Dict[str, Dict[str, List[str]]]:
prediction_data=pd.read_csv(filepath)
# Map language -> query -> Ranked List of URL
predictions=defaultdict(lambda: defaultdict(list))
for_, rowinprediction_data.iterrows():
predictions[row['language'].lower()][row['query'].lower()].append(row['url'])
forquery_datainpredictions.values():
forquery, ranked_urlsinquery_data.items():
query_data[query] =ranked_urls[:max_urls_per_language]
returnpredictions
defcoverage_per_language(predictions: Dict[str, List[str]],
relevance_scores: Dict[str, Dict[str, float]], with_positive_relevance: bool=False) ->float:
"""
Compute the % of annotated URLs that appear in the algorithm's predictions.
"""
num_annotations=0
num_covered=0
forquery, url_datainrelevance_scores.items():
urls_in_predictions=set(predictions[query])
forurl, relevanceinurl_data.items():
ifnotwith_positive_relevanceorrelevance>0:
num_annotations+=1
ifurlinurls_in_predictions:
num_covered+=1
returnnum_covered/num_annotations
defndcg(predictions: Dict[str, List[str]], relevance_scores: Dict[str, Dict[str, float]],
ignore_rank_of_non_annotated_urls: bool=True) ->float:
num_results=0
ndcg_sum=0
forquery, query_relevance_annotationsinrelevance_scores.items():
current_rank=1
query_dcg=0
forurlinpredictions[query]:
ifurlinquery_relevance_annotations:
query_dcg+= (2**query_relevance_annotations[url] -1) /np.log2(current_rank+1)
current_rank+=1
elifnotignore_rank_of_non_annotated_urls:
current_rank+=1
query_idcg=0
fori, ideal_relevanceinenumerate(sorted(query_relevance_annotations.values(), reverse=True), start=1):
query_idcg+= (2**ideal_relevance-1) /np.log2(i+1)
ifquery_idcg==0:
# We have no positive annotations for the given query, so we should probably not penalize anyone about this.
continue
num_results+=1
ndcg_sum+=query_dcg/query_idcg
returnndcg_sum/num_results
defrun(arguments):
relevance_scores=load_relevances(arguments['RELEVANCE_ANNOTATIONS_CSV_PATH'])
predictions=load_predictions(arguments['MODEL_PREDICTIONS_CSV'])
languages_predicted=sorted(set(predictions.keys()))
# Now Compute the various evaluation results
print('% of URLs in predictions that exist in the annotation dataset:')
forlanguageinlanguages_predicted:
print(f'\t{language}: {coverage_per_language(predictions[language], relevance_scores[language])*100:.2f}%')
print('% of URLs in predictions that exist in the annotation dataset (avg relevance > 0):')
forlanguageinlanguages_predicted:
print(f'\t{language}: {coverage_per_language(predictions[language], relevance_scores[language], with_positive_relevance=True) *100:.2f}%')
print('NDCG:')
forlanguageinlanguages_predicted:
print(f'\t{language}: {ndcg(predictions[language], relevance_scores[language]):.3f}')
print('NDCG (full ranking):')
forlanguageinlanguages_predicted:
print(f'\t{language}: {ndcg(predictions[language], relevance_scores[language], ignore_rank_of_non_annotated_urls=False):.3f}')
if__name__=='__main__':
args=docopt(__doc__)
run_and_debug(lambda: run(args), args['--debug'])