Assume I have the LSH algorittm for shoe recommendation as follows:

from sklearn.metrics.pairwise import cosine_similarity
import numpy as np

def normalize(dataset):
"""Normalize a dataset by subtracting the mean and dividing by the standard deviation"""
# Calculate the mean and standard deviation of the dataset
mean = np.mean(dataset, axis=0)
std = np.std(dataset, axis=0)


# Subtract the mean and divide by the standard deviation
normalized_dataset = (dataset - mean) / std

return normalized_dataset
model_dev_data_ = model_dev_data1.apply(lambda x: float(x) if type(x) == int else x)
model_dev_data1_norm = normalize(model_dev_data_)

Remove duplicates
data = model_dev_data1_norm[~model_dev_data1_norm.index.duplicated(keep='first')]

import numpy as np
import pandas as pd
from sklearn.random_projection import SparseRandomProjection
from sklearn.metrics.pairwise import cosine_similarity
import time

class LSH:
def init(self, n_components, n_hashes):


    # Dimensionalty of projected data
    self.n_components = n_components
    
    # number of buckets
    self.n_hashes = n_hashes
    
    # https://scikit-learn.org/stable/modules/generated/sklearn.random_projection.SparseRandomProjection.html
    self.hash_tables = [SparseRandomProjection(n_components=n_components) for _ in range(n_hashes)]

def fit(self, data):
    if isinstance(data, pd.DataFrame):
        self.item_ids = data.index.tolist()
        data = data.values
    else:
        self.item_ids = np.arange(len(data))
    
    # initialize the hash buckets
    self.hash_buckets = [{} for _ in range(self.n_hashes)]
    for i, hash_table in enumerate(self.hash_tables):
        # For each hash table the function applies SparseRandomProjection. 
        # This step reduce the dimentionality of the data while tryigng to preserve the relative distance between points.
        # Purpose: approximate high-dimentional to low-dimensional data and similar items stays together.
        projections = hash_table.fit_transform(data)
        for j, projection in enumerate(projections):
            # After projecting data for each hash table, the funtion iterates over these projections.
            # For each data points projection, it creates a hash key. The key is generate by the sign of each component.
            # The idea is if two points are close in high-dimensional space, their projections must also be closed.
            # Ex projection: [ 0.72199595 -0.6867862  -0.5098184   0.55619763  4.24497602  1.08506354
            # -2.26010595 -2.79241899  6.01301565 -4.82428779  1.05509869 -2.29027826
            # -2.13740946  1.06065566 -1.57524544  0.58627834]
            # Ex hash_key: (1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1)
            hash_key = tuple(np.where(projection > 0, 1, 0))
            if hash_key not in self.hash_buckets[i]:
                # Create the item with the hash_key
                self.hash_buckets[i][hash_key] = []
            
            # Append to that specific hash_bucket, j, with the unique and shared hash_key 
            self.hash_buckets[i][hash_key].append(j)
    return self.hash_buckets

def query(self, point):
    if isinstance(point, pd.Series):
        point = point.to_numpy().reshape(1, -1)  # Correct way to handle Series
    elif isinstance(point, np.ndarray):
        point = point.reshape(1, -1)
    
    # To eliminate the chance of duplicates
    similar_items = set()
    for i, hash_table in enumerate(self.hash_tables):
        projection = hash_table.transform(point)
        hash_key = tuple(np.where(projection > 0, 1, 0)[0])
        similar_items.update(self.hash_buckets[i].get(hash_key, []))

    similar_item_ids = [self.item_ids[i] for i in similar_items]
    return similar_item_ids
def find_top_similar(data, query_input, similar_inputs, top_n=10):
if isinstance(data, pd.DataFrame):
query_item = data.loc[query_input].values.reshape(1, -1)
else:
query_item = data[query_input].reshape(1, -1)


similarities = []
for input_ in similar_inputs:
    if input_ != query_input:
        if isinstance(data, pd.DataFrame):
            item = data.loc[input_].values.reshape(1, -1)
        else:
            item = data[input_].reshape(1, -1)
        similarity_score = cosine_similarity(query_item, item)[0][0]
        similarities.append((input_, similarity_score))

sorted_similarities = sorted(similarities, key=lambda x: x[1], reverse=True)
top_similar_indices = [item[0] for item in sorted_similarities[:top_n]]
return top_similar_indices
Params
n_components = int(0.1 * n_features)
n_hashes = 15

DataFrame
lsh = LSH(n_components=n_components, n_hashes=n_hashes)
hash_buckets = lsh.fit(data)

top_n = 20
query_id = data.index[2]
start_time = time.time()
similar_ids = lsh.query(data.loc[query_id])
end_time = time.time()
print("Time taken to query similar items (DataFrame):", end_time - start_time, "seconds")

top_n_similar_ids = find_top_similar(data, query_id, similar_ids, top_n=top_n)
print(f"Top {top_n} Similar Items (DataFrame) for {query_id}:", top_n_similar_ids)
Now change the code so I can weight a subset of features more meaning that the algorithm must pay more attention to that subset. Highlight the code changes rathern that writing the whole thing.