Найти матрицу корреляции

У меня довольно большая матрица (около 50K строк), и я хочу напечатать коэффициент корреляции между каждой строкой в матрица. Я написал такой код на Python:

for i in xrange(rows): # rows are the number of rows in the matrix. 
    for j in xrange(i, rows):
        r = scipy.stats.pearsonr(data[i,:], data[j,:])
        print r  

Обратите внимание, что я использую функцию pearsonr , доступную в модуле scipy ( http://docs.scipy.org/doc/scipy /reference/generated/scipy.stats.pearsonr.html).

Мой вопрос: Есть ли более быстрый способ сделать это? Могу ли я использовать какой-нибудь метод матричного разбиения?

Спасибо!

11
задан kennytm 9 August 2010 в 10:14
поделиться

3 ответа

Вы пробовали просто использовать numpy.corrcoef ? Поскольку вы не используете p-значения, он должен делать именно то, что вы хотите, с минимальными усилиями. (Если я не помню точно, что такое R pearson, что вполне возможно.)

Просто быстро проверяя результаты на случайных данных, он возвращает то же самое, что и приведенный выше код @Justin Peel, и работает примерно в 100 раз быстрее.

Например, тестирование чего-либо с 1000 строками и 10 столбцами случайных данных ...:

import numpy as np
import scipy as sp
import scipy.stats

def main():
    data = np.random.random((1000, 10))
    x = corrcoef_test(data)
    y = justin_peel_test(data)
    print 'Maximum difference between the two results:', np.abs((x-y)).max()
    return data

def corrcoef_test(data):
    """Just using numpy's built-in function"""
    return np.corrcoef(data)

def justin_peel_test(data):
    """Justin Peel's suggestion above"""
    rows = data.shape[0]

    r = np.zeros((rows,rows))
    ms = data.mean(axis=1)

    datam = np.zeros_like(data)
    for i in xrange(rows):
        datam[i] = data[i] - ms[i]
    datass = sp.stats.ss(datam,axis=1)
    for i in xrange(rows):
        for j in xrange(i,rows):
            r_num = np.add.reduce(datam[i]*datam[j])
            r_den = np.sqrt(datass[i]*datass[j])
            r[i,j] = min((r_num / r_den), 1.0)
            r[j,i] = r[i,j]
    return r

data = main()

дает максимальную абсолютную разницу ~ 3,3e-16 между двумя результатами

И тайминги:

In [44]: %timeit corrcoef_test(data)
10 loops, best of 3: 71.7 ms per loop

In [45]: %timeit justin_peel_test(data)
1 loops, best of 3: 6.5 s per loop

numpy. corrcoef должен делать именно то, что вы хотите, и это намного быстрее.

6
ответ дан 3 December 2019 в 07:36
поделиться

Новое решение

Посмотрев на ответ Джо Кингтона, я решил изучить код corrcoef () и был вдохновлен им на реализацию следующей реализации.

ms = data.mean(axis=1)[(slice(None,None,None),None)]
datam = data - ms
datass = np.sqrt(scipy.stats.ss(datam,axis=1))
for i in xrange(rows):
    temp = np.dot(datam[i:],datam[i].T)
    rs = temp / (datass[i:]*datass[i])

Каждый проходной цикл генерирует коэффициенты Пирсона между строкой i и строками i до последней строки. Это очень быстро. Это как минимум в 1,5 раза быстрее, чем при использовании только corrcoef () , потому что при этом не вычисляются избыточные коэффициенты и некоторые другие вещи. Это также будет быстрее и не вызовет проблем с памятью с матрицей из 50 000 строк, потому что тогда вы можете либо сохранить каждый набор r, либо обработать их перед созданием другого набора. Не сохраняя никаких долгосрочных значений r, я смог заставить приведенный выше код работать на наборе случайным образом сгенерированных данных размером 50 000 x 10 менее чем за минуту на моем довольно новом ноутбуке.

Старое решение

Во-первых, я бы не рекомендовал выводить буквы r на экран. Для 100 строк (10 столбцов) разница составляет 19,79 секунды при печати и 0,301 секунды без использования кода. Просто сохраните r и используйте их позже, если хотите, или обработайте их по ходу дела, например, ищите некоторые из самых больших r.

Во-вторых, вы можете получить некоторую экономию, отказавшись от избыточного расчета некоторых количеств. Коэффициент Пирсона вычисляется в scipy с использованием некоторых величин, которые вы можете предварительно вычислить, а не вычислять каждый раз, когда используется строка. Кроме того, вы не используете p-значение (которое также возвращается pearsonr () , так что давайте его тоже поцарапаем. Используя приведенный ниже код:

r = np.zeros((rows,rows))
ms = data.mean(axis=1)

datam = np.zeros_like(data)
for i in xrange(rows):
    datam[i] = data[i] - ms[i]
datass = scipy.stats.ss(datam,axis=1)
for i in xrange(rows):
    for j in xrange(i,rows):
        r_num = np.add.reduce(datam[i]*datam[j])
        r_den = np.sqrt(datass[i]*datass[j])
        r[i,j] = min((r_num / r_den), 1.0)

, я получаю ускорение примерно в 4 раза.8x по сравнению с прямым scipy кодом, когда я удалил материал с p-значением - 8,8x, если я оставлю там материал с p-значением (я использовал 10 столбцов с сотнями строк). Я также проверил, дает ли он те же результаты. Это небольшое улучшение, но оно может помочь.

В конечном итоге вы столкнулись с проблемой, заключающейся в том, что вы вычисляете (50000) * (50001) / 2 = 1 250 025 000 коэффициентов Пирсона (если я правильно считаю). Это много. Кстати, на самом деле нет необходимости вычислять коэффициент Пирсона каждой строки с самим собой (он будет равен 1), но это только избавляет вас от вычисления 50 000 коэффициентов Пирсона. С приведенным выше кодом я ожидаю, что для выполнения ваших вычислений потребуется около 4 1/4 часа, если у вас есть 10 столбцов для ваших данных, на основе моих результатов для небольших наборов данных.

Вы можете добиться улучшения, перенеся приведенный выше код в Cython или что-то подобное. Я ожидаю, что вы, возможно, получите 10-кратное улучшение по сравнению с обычным Scipy, если вам повезет. Кроме того, как предлагает pyInTheSky, вы можете выполнять некоторую многопроцессорность.

10
ответ дан 3 December 2019 в 07:36
поделиться

вы можете использовать модуль python multiprocess, разбить ваши строки на 10 наборов, буферизировать результаты и затем распечатать их (это ускорит процесс только на многоядерной машине)

http://docs.python. org/library/multiprocessing.html

btw: вам также придется превратить ваш сниппет в функцию, а также подумать, как сделать пересборку данных. если каждый подпроцесс будет иметь список вроде этого ...[startcord,stopcord,buff] ... может сработать неплохо

def myfunc(thelist):
    for i in xrange(thelist[0]:thelist[1]):
    ....
    thelist[2] = result
0
ответ дан 3 December 2019 в 07:36
поделиться
Другие вопросы по тегам:

Похожие вопросы: