Re-calculate similarity ranking/sorting without re-sorting - python

I have some code which calculates the nearest neighbors amongst some vectors (values).
However, the values of these vectors are dependent on weights. Each column of the vectors has a different weight at every iteration.
Just for the sake of the example, at the code below I try to find everytime the nearest neighbor of the last vector (vector[3]).
That's a very simplified version of my code:
from sklearn.neighbors import NearestNeighbors
knn = NearestNeighbors(n_neighbors=1)
values = [
[2, 5, 1],
[4, 2, 3],
[1, 5, 2],
[4, 5, 4]
]
weights = [
[1, 3, 1],
[0.5, 2, 1],
[3, 1, 2]
]
# weights set No1
new_values = []
for line in values:
new_values.append([a*b for a,b in zip(line,weights[0])])
knn.fit(new_values)
print(knn.kneighbors(new_values[3]))
# weights set No2
new_values = []
for line in values:
new_values.append([a*b for a,b in zip(line,weights[1])])
knn.fit(new_values)
print(knn.kneighbors(new_values[3]))
# weights set No3
new_values = []
for line in values:
new_values.append([a*b for a,b in zip(line,weights[2])])
knn.fit(new_values)
print(knn.kneighbors(new_values[3]))
(Obviously I could have a for loop for the different weights sets but I just wanted to point the repetition of the matter)
My question is, is there any way that I can avoid using the KNN 3 times but just use it once at the beginning to do the initial similarity ranking/sorting and then just do some re-calculations?
In different words, is there any way to reduce the computation complexity of this code in terms of calling the KNN fewer times?
PS
I know that there are KNN implementations which are much faster than the ScikitLearn one but that's not really the point; the point is more on using KNN just once instead of N=3 times or something like that.

assuming calling the KNN fewer times means the number of times the KNN is fit, yes it's possible. if calling the KNN means the number of times kneighbors is invoked, that might be difficult due to how relative distances aren't preserved under affine transformations.
This solution runs in O(wk log n) time compared to the original O(wn) time with w being the number of weights.
what you're doing is
taking the input points
scaling its dimensions (projecting the input points into a new coordinate space)
building a knn model from the scaled inputs
classifying the target based on the scaled input.
However, consider
taking the input points
building a knn model from the scaled inputs
inverse scaling the target point (projecting the target into the original coordinate space)
classifying the inverse scaled target based on the input
the result of this process would be that steps 1 and 2 could be reused for each target point. weights with value 0 will require special handling.
this would look would be something like:
from sklearn.neighbors import NearestNeighbors
knn = NearestNeighbors(n_neighbors=1, algorithm="kd_tree")
values = [
[2, 5, 1],
[4, 2, 3],
[1, 5, 2],
[4, 5, 4]
]
weights = [
[1, 3, 1],
[0.5, 2, 1],
[3, 1, 2]
]
targets = [
[4, 15, 4], # values[3] * weights[0]
[2.0, 10, 4], # values[3] * weights[1]
[12, 5, 8] # values[3] * weights[2]
]
knn.fit(values)
# weights set No1
print(knn.kneighbors([[a/b for a, b in zip(targets[0], weights[0])]]))
# weights set No2
print(knn.kneighbors([[a/b for a, b in zip(targets[1], weights[1])]]))
# weights set No3
print(knn.kneighbors([[a/b for a, b in zip(targets[2], weights[2])]]))

Related

The sklearn.manifold.TSNE gives different results for same input vectors

I give TSNE a list of vectors, some of these vectors are exactly the same. But the output of fit() function can be different for each!
IS this expected behavior? How can i assure each input vector will be mapped to same output vector?
Exclamation,
I cannot tell for sure, but I even noticed that the first entry in the input list of vectors always gets different unexpected value.
Consider the following simple example.
Notice that the first three vectors are the same, the random_state is static but the first three 2D vectors in the output can be different from each others.
from sklearn import manifold
import numpy as np
X= np.array([ [2, 1, 3, 5],
[2, 1, 3, 5],
[2, 1, 3, 5],
[2, 1, 3, 5],
[12, 1, 3, 5],
[87, 22, 3, 5],
[3, 23, 9, 5],
[43, 87, 3, 5],
[121, 65, 3, 5]])
m = manifold.TSNE(
n_components=2,
perplexity=0.666666,
verbose=0,
random_state=42,
angle=.99,
init='pca',
metric='cosine',
n_iter=1000)
X_emedded = m.fit_transform(X)
# The following might fail
assert( sum(X_emedded[1] - X_emedded[2] ) == 0)
assert( sum(X_emedded[0] - X_emedded[1] ) == 0)
Update....
sklearn.version is '1.2.0'
t-SNE, as presenter by van der Maaten and Hinton 2008 is a technique to "visualizes high-dimensional data by giving each
datapoint a location in a two or three-dimensional map".
There is no guarantee that two identical points are mapped to the same low dimensional point. As a matter of fact it almost never happens as one can see with Algorithm 1 in (Maaten and Hinton 2008). The points in the low dimensional space are obtained with a gradient descent minimizing a cost function after a random initialisation.

How can I use weighted labels in the knn algorithm?

I am working on my own implementation of the weighted knn algorithm.
To simplify the logic, let's represent this as a predict method, which takes three parameters:
indices - matrix of nearest j neighbors from the training sample for object i (i=1...n, n objects in total). [i, j] - index of object from the training sample.
For example, for 4 objects and 3 neighbors:
indices = np.asarray([[0, 3, 1],
[0, 3, 1],
[1, 2, 0],
[5, 4, 3]])
distances - matrix of distances from j nearest neighbors from the training sample to object i. (i=1...n, n objects in total). For example, for 4 objects and 3 neighbors:
distances = np.asarray([[ 4.12310563, 7.07106781, 7.54983444],
[ 4.89897949, 6.70820393, 8.24621125],
[ 0., 1.73205081, 3.46410162],
[1094.09368886, 1102.55022561, 1109.62245832]])
labels - vector with true labels of classes for each object j of training sample. For example:
labels = np.asarray([0, 0, 0, 1, 1, 2])
Thus, the function signature is:
def predict(indices, distances, labels):
....
# return [np.bincount(x).argmax() for x in labels[indices]]
return predict
In the commentary you can see the code that returns the prediction for the "non-weighted" knn-method, which does not use distances. Can you please show, how predictions can be calculated with using the distance matrix? I found the algorithm, but now I'm completely stumped becase I don't know how to realize it with numpy.
Thank you!
This should work:
# compute inverses of distances
# suppress division by 0 warning,
# replace np.inf with a very large number
with np.errstate(divide='ignore'):
dinv = np.nan_to_num(1 / distances)
# an array with distinct class labels
distinct_labels = np.array(list(set(labels)))
# an array with labels of neighbors
neigh_labels = labels[indices]
# compute the weighted score for each potential label
weighted_scores = ((neigh_labels[:, :, np.newaxis] == distinct_labels) * dinv[:, :, np.newaxis]).sum(axis=1)
# choose the label with the highest score
predictions = distinct_labels[weighted_scores.argmax(axis=1)]

train test split in python but consider patient information?

I'm wondering if there's an easy way to do train test splitting (mainly interested in crossvalidation) in python such that I don't end up with data points from the same patient in both train and test? That is, I'd like to first split the patients into train and test then the observations accordingly.
Is there a functionality for this kind of scenario or do I have to code it manually?
Sklearn GroupKFold should solve this task. K-fold iterator variant with non-overlapping groups. The same group will not appear in two different folds:
from sklearn.model_selection import GroupKFold
X = np.array([[1, 2], [3, 4], [5, 6], [7, 8]])
y = np.array([1, 2, 3, 4])
groups = np.array([0, 0, 2, 2])
group_kfold = GroupKFold(n_splits=2)
group_kfold.get_n_splits(X, y, groups)

How to calculate the steepness of a trend in python

I am using the regression slope as follows to calculate the steepness (slope) of the trend.
Scenario 1:
For example, consider I am using sales figures (x-axis: 1, 4, 6, 8, 10, 15) for 6 days (y-axis).
from sklearn.linear_model import LinearRegression
regressor = LinearRegression()
X = [[1], [4], [6], [8], [10], [15]]
y = [1, 2, 3, 4, 5, 6]
regressor.fit(X, y)
print(regressor.coef_)
This gives me 0.37709497
Scenario 2:
When I run the same program for a different sale figure (e.g., 1, 2, 3, 4, 5, 6) I get the results as 1.
However, you can see that sales is much productive in scenario 1, but not in scenario 2. However, the slope I get for scenario 2 is higher than scenario 1.
Therefore, I am not sure if the regression slope captures what I require. Is there any other approach I can use instead to calculate the sleepness of the trend slope.
I am happy to provide more details if needed.
I believe the problem is your variables are switched. If you want to track sales performance over time, you should perform the regression the other way around. You can invert the slopes you've calculated to get the correct values, which will show higher sales performance in case 1.
1 / 0.377 = 2.65
Here is a visualization of your data:
import matplotlib.pyplot as plt
days = [1,2,3,4,5,6]
sales1 = [1,4,6,8,10,15]
sales2 = [1,2,3,4,5,6]
df = pd.DataFrame({'days': days, 'sales1': sales1, 'sales2': sales2})
df = df.set_index('days')
df.plot(marker='o', linestyle='--')

How to perform multivariable linear regression with scikit-learn?

Forgive my terminology, I'm not an ML pro. I might use the wrong terms below.
I'm trying to perform multivariable linear regression. Let's say I'm trying to work out user gender by analysing page views on a web site.
For each user whose gender I know, I have a feature matrix where each row represents a web site section, and the second element whether they visited it, e.g.:
male1 = [
[1, 1], # visited section 1
[2, 0], # didn't visit section 2
[3, 1], # visited section 3, etc
[4, 0]
]
So in scikit, I am building xs and ys. I'm representing a male as 1, and female as 0.
The above would be represented as:
features = male1
gender = 1
Now, I'm obviously not just training a model for a single user, but instead I have tens of thousands of users whose data I'm using for training.
I would have thought I should create my xs and ys as follows:
xs = [
[ # user1
[1, 1],
[2, 0],
[3, 1],
[4, 0]
],
[ # user2
[1, 0],
[2, 1],
[3, 1],
[4, 0]
],
...
]
ys = [1, 0, ...]
scikit doesn't like this:
from sklearn import linear_model
clf = linear_model.LinearRegression()
clf.fit(xs, ys)
It complains:
ValueError: Found array with dim 3. Estimator expected <= 2.
How am I supposed to supply a feature matrix to the linear regression algorithm in scikit-learn?
You need to create xs in a different way. According to the docs:
fit(X, y, sample_weight=None)
Parameters:
X : numpy array or sparse matrix of shape [n_samples, n_features]
Training data
y : numpy array of shape [n_samples, n_targets]
Target values
sample_weight : numpy array of shape [n_samples]
Individual weights for each sample
Hence xs should be a 2D array with as many rows as users and as many columns as web site sections. You defined xs as a 3D array though. In order to reduce the number of dimensions by one you could get rid of the section numbers through a list comprehension:
xs = [[visit for section, visit in user] for user in xs]
If you do so, the data you provided as an example gets transformed into:
xs = [[1, 0, 1, 0], # user1
[0, 1, 1, 0], # user2
...
]
and clf.fit(xs, ys) should work as expected.
A more efficient approach to dimension reduction would be that of slicing a NumPy array:
import numpy as np
xs = np.asarray(xs)[:,:,1]

Categories

Resources