2012-05-24 2 views
1

У меня есть матрица в SAS IML. Для каждой пары строк (скажем, векторы A и B), я хочу, чтобы вычислить cosine similarity,Вычислить сходство косинусов в SAS/IML

A . B/(||A|| x ||B||).

Таким образом, результат должен быть квадратной матрицей с таким же количеством строк, что и исходная матрица.

Если я передаю вектор функции Евклида, я возвращаю вектор, поэтому функция действует отдельно для каждого элемента вектора. Действительно, документация SAS says:

При вызове функции Base SAS с матрицей аргумента, функция, как правило, действуют поэлементно на каждом элементе Teh [так] матрицы.

Это странно - зачем кому-то рассчитывать сводную статистику для каждого элемента вектора? Они всегда будут просто возвращать элементы. Есть ли способ получить евклидову норму для вектора?

Мой код ниже. Независимо от евклидовой нормы, существует ли более эффективный способ сделать это?

proc iml; 
use fundstr; 
read all var _all_ into wgts; 

nrows=nrow(wgts); 
d=j(nrows,nrows,0); 

do i = 1 to nrows; 
    do j = i to nrows; 

    tmp = wgts[i,]*wgts[j,]`; /** need to divide by norms each vector **/ 
    d[i,j] = tmp; 
    d[j,i] = tmp; 

    end; 
end; 
quit; 

ответ

2

Использовать матричные операции и рассматривать эту проблему как (A/|| A ||) * (B/|| B ||).

Первый шаг состоит в том, чтобы разделить каждую строку по его евклидовой норме, которая является просто sqrt (ssq (wgts [i,])). Вы можете использовать оператор сокращения суммы квадратов (##), чтобы вычислить это для всех строк сразу, не создавая цикл: sqrt (wgts [, ##]); (см. http://blogs.sas.com/content/iml/2012/05/23/compute-statistics-for-each-row-by-using-subscript-operators/ для пояснения и примеры операторов сокращения индексов.)

Парное произведение строк строк эквивалентно матричному умножению A * A`, где A - масштабированная матрица. Сведя все это вместе приводит к решению:

wgts = ranuni(j(5,5));   
norm = sqrt(wgts[ ,##]); /* Euclidean norm */ 
A = wgts/norm; 
d = A*A`; 
print d; 

Если вы хотите сравнить это с (неэффективного) решения, которое использует петли, здесь:

nrows=nrow(wgts); 
d=j(nrows,nrows,0); 
do i = 1 to nrows; 
    normi = sqrt(wgts[i,##]); 
    do j = i to nrows; 
     normj = sqrt(wgts[j,##]); 
     tmp = wgts[i,]*wgts[j,]`/(normi * normj); 
     d[i,j] = tmp; 
     d[j,i] = tmp; 
    end; 
end; 
print d; 

Кстати, вы будете будем рады услышать, что в следующем выпуске SAS/IML исправлена ​​опечатка в документе :-)

1

Чтобы предоставить ссылку, я думаю, что this article Рик, вероятно, хорошо читать для вас. Метод преобразования векторов в строку с разделителями-запятыми довольно удобен.