import json import os from collections import defaultdict def load_and_merge_json_files(directory_path): """ Load and merge JSON files from a directory into a single structure, keeping each list from different files separate for each query. Args: directory_path (str): Path to the directory containing the JSON files. Returns: list: Merged list of dictionaries, keeping separate lists for each query. """ merged_queries = defaultdict(list) # Iterate through all files in the directory for filename in os.listdir(directory_path): if filename.endswith('.json'): file_path = os.path.join(directory_path, filename) try: with open(file_path, 'r') as f: json_data = json.load(f) # For each file, add the lists to the corresponding query for query_data in json_data: for query, rank_list in query_data.items(): if isinstance(rank_list, list): # Ensure rank_list is a list merged_queries[query].append(rank_list) else: print(f"Warning: Expected a list for query '{query}' but got {type(rank_list)}") except Exception as e: print(f"Error reading {filename}: {e}") # Convert defaultdict to a list of dictionaries return [{query: lists} for query, lists in merged_queries.items()] def reciprocal_rank_fusion(json_input, K=60, top_n=100): """ Fuse rank from multiple IR systems for multiple queries using Reciprocal Rank Fusion. Args: json_input (list): A list of dictionaries where keys are queries, and values are ranked document lists from different systems. K (int): A constant used in the RRF formula (default is 60). top_n (int): Number of top results to return for each query. Returns: list: A list of dictionaries with each query and its respective fused document rankings. """ query_fusion_results = [] # Iterate over each query in the JSON input for query_data in json_input: for query, list_of_ranked_docs in query_data.items(): rrf_map = defaultdict(float) # Fuse rankings for the query using RRF for rank_list in list_of_ranked_docs: for rank, doc in enumerate(rank_list, 1): rrf_map[doc] += 1 / (rank + K) # Sort the documents based on RRF scores in descending order sorted_docs = sorted(rrf_map.items(), key=lambda x: x[1], reverse=True) fused_rankings = [doc for doc, score in sorted_docs[:top_n]] # Keep only top N results # Store the results for the current query query_fusion_results.append({query: fused_rankings}) return query_fusion_results def save_to_json(output_data, output_file_path): """ Save the RRF results to a JSON file in the same format as the input. Args: output_data (list): The processed data to save. output_file_path (str): Path to the output JSON file. """ with open(output_file_path, 'w') as f: json.dump(output_data, f, indent=2) # # Example usage # directory_path = "Modified_1_2" # Replace with your directory path # output_file_path = "Modified_1_2/rrf_1_2_modified.json" # Replace with your desired output file path # # Load and merge JSON files # merged_input = load_and_merge_json_files(directory_path) # print(merged_input[0]["5xvggq"]) # # Perform RRF on the merged input, keeping only the top 100 results # combined_results = reciprocal_rank_fusion(merged_input, top_n=100) # # Save the combined results to a JSON file # save_to_json(combined_results, output_file_path) # print(f"Combined results saved to {output_file_path}") def reciprocal_rank_fusion_two(rank_list1, rank_list2, K=60, top_n=100): """ Perform Reciprocal Rank Fusion (RRF) for two ranking lists. Args: rank_list1 (list): First list of ranked documents. rank_list2 (list): Second list of ranked documents. K (int): A constant used in the RRF formula (default is 60). top_n (int): Number of top results to return (default is 100). Returns: list: Combined list of rankings after applying RRF. """ rrf_map = defaultdict(float) # Process the first ranking list for rank, doc in enumerate(rank_list1, 1): # Start ranks from 1 rrf_map[doc] += 1 / (rank + K) # Process the second ranking list for rank, doc in enumerate(rank_list2, 1): # Start ranks from 1 rrf_map[doc] += 1 / (rank + K) # Sort the documents based on RRF scores in descending order sorted_docs = sorted(rrf_map.items(), key=lambda x: x[1], reverse=True) # Return only the top N results return [doc for doc, score in sorted_docs[:top_n]] def reciprocal_rank_fusion_three(rank_list1, rank_list2, rank_list3, K=60, top_n=100): """ Perform Reciprocal Rank Fusion (RRF) for three ranking lists. Args: rank_list1 (list): First list of ranked documents. rank_list2 (list): Second list of ranked documents. rank_list3 (list): Third list of ranked documents. K (int): A constant used in the RRF formula (default is 60). top_n (int): Number of top results to return (default is 100). Returns: list: Combined list of rankings after applying RRF. """ rrf_map = defaultdict(float) # Process the first ranking list for rank, doc in enumerate(rank_list1, 1): # Start ranks from 1 rrf_map[doc] += 1 / (rank + K) # Process the second ranking list for rank, doc in enumerate(rank_list2, 1): # Start ranks from 1 rrf_map[doc] += 1 / (rank + K) # Process the third ranking list for rank, doc in enumerate(rank_list3, 1): # Start ranks from 1 rrf_map[doc] += 1 / (rank + K) # Sort the documents based on RRF scores in descending order sorted_docs = sorted(rrf_map.items(), key=lambda x: x[1], reverse=True) # Return only the top N results return [doc for doc, score in sorted_docs[:top_n]] def reciprocal_rank_fusion_six(rank_list1, rank_list2, rank_list3, rank_list4, rank_list5, rank_list6, K=60, top_n=100): """ Perform Reciprocal Rank Fusion (RRF) for six ranking lists. Args: rank_list1 (list): First list of ranked documents. rank_list2 (list): Second list of ranked documents. rank_list3 (list): Third list of ranked documents. rank_list4 (list): Fourth list of ranked documents. rank_list5 (list): Fifth list of ranked documents. rank_list6 (list): Sixth list of ranked documents. K (int): A constant used in the RRF formula (default is 60). top_n (int): Number of top results to return (default is 100). Returns: list: Combined list of rankings after applying RRF. """ rrf_map = defaultdict(float) # Process each ranking list for rank, doc in enumerate(rank_list1, 1): rrf_map[doc] += 1 / (rank + K) for rank, doc in enumerate(rank_list2, 1): rrf_map[doc] += 1 / (rank + K) for rank, doc in enumerate(rank_list3, 1): rrf_map[doc] += 1 / (rank + K) for rank, doc in enumerate(rank_list4, 1): rrf_map[doc] += 1 / (rank + K) for rank, doc in enumerate(rank_list5, 1): rrf_map[doc] += 1 / (rank + K) for rank, doc in enumerate(rank_list6, 1): rrf_map[doc] += 1 / (rank + K) # Sort the documents based on RRF scores in descending order sorted_docs = sorted(rrf_map.items(), key=lambda x: x[1], reverse=True) # Return only the top N results return [doc for doc, score in sorted_docs[:top_n]] def reciprocal_rank_fusion_multiple_lists(ranking_lists, K=60, top_n=100): """ Perform Reciprocal Rank Fusion (RRF) for multiple ranking lists for each query. Args: ranking_lists (list of list of dict): Each element is a list of dictionaries, where each dictionary contains query IDs and ranked lists. K (int): A constant used in the RRF formula (default is 60). top_n (int): Number of top results to return for each query (default is 100). Returns: dict: A dictionary with query IDs as keys and their combined rankings as values. """ combined_results = defaultdict(list) # Flatten all ranking lists into a single dictionary per query merged_rankings = defaultdict(list) for ranking_list in ranking_lists: for ranking_dict in ranking_list: for query_id, doc_list in ranking_dict.items(): merged_rankings[query_id].append(doc_list) # Apply RRF for each query for query_id, ranked_lists in merged_rankings.items(): rrf_map = defaultdict(float) # Process rankings for each system for rank_list in ranked_lists: for rank, doc in enumerate(rank_list, 1): # Start rank from 1 rrf_map[str(doc)] += 1 / (rank + K) # Sort documents based on their RRF scores in descending order sorted_docs = sorted(rrf_map.items(), key=lambda x: x[1], reverse=True) combined_results[query_id] = [doc for doc, score in sorted_docs[:top_n]] return dict(combined_results)