Быстрая связь между двумя пандами - PullRequest
0 голосов
/ 17 сентября 2018

Я хочу применить корреляцию Спирмена к двум фреймам данных панд с одинаковым количеством столбцов (корреляция каждой пары строк).

Моя цель - вычислить распределение корреляций Спирмена между каждой парой строк (r, s), где r - строка из первого кадра данных, а s - строка из второго кадра данных.

Мне известно, что на подобные вопросы уже давались ответы (см. this ). Однако этот вопрос отличается, потому что я хочу сравнить каждую строку первого кадра данных со ВСЕМИ строками во втором. Кроме того, это требует значительных вычислительных ресурсов и занимает несколько часов из-за размера моих данных. Я хочу распараллелить его и, возможно, переписать, чтобы ускорить.

Я пробовал с numba, но, к сожалению, он не работает (аналогично this ), потому что он, похоже, не распознает scipy spearmanr . Мой код следующий:

def corr(a, b):
    dist = []
    for i in range(a.shape[0]):
        for j in range(b.shape[0]):
            dist += [spearmanr(a.iloc[i, :], b.iloc[j, :])[0]]
    return dist

Ответы [ 2 ]

0 голосов
/ 17 сентября 2018

НОВЫЙ ОТВЕТ

from numba import njit
import pandas as pd
import numpy as np

@njit
def mean1(a):
  n = len(a)
  b = np.empty(n)
  for i in range(n):
    b[i] = a[i].mean()
  return b

@njit
def std1(a):
  n = len(a)
  b = np.empty(n)
  for i in range(n):
    b[i] = a[i].std()
  return b

@njit
def c(a, b):
    ''' Correlation '''
    n, k = a.shape
    m, k = b.shape

    mu_a = mean1(a)
    mu_b = mean1(b)
    sig_a = std1(a)
    sig_b = std1(b)

    out = np.empty((n, m))

    for i in range(n):
        for j in range(m):
            out[i, j] = (a[i] - mu_a[i]) @ (b[j] - mu_b[j]) / k / sig_a[i] / sig_b[j]

    return out

r = df_test.rank(1).values
df_test.T.corr('spearman') == c(r, r)

СТАРЫЙ ОТВЕТ

Выполнение корреляции Ранга Спирмена - это просто корреляция рангов.

Rank

Мы можем использовать argsort, чтобы получить звания. Хотя argsort из argsort действительно дает нам ранги, мы можем ограничить себя одним видом, назначив срез.

def rank(a):
  i, j = np.meshgrid(*map(np.arange, a.shape), indexing='ij')

  s = a.argsort(1)
  out = np.empty_like(s)
  out[i, s] = j

  return out

Корреляция

В случае корреляции рангов средние значения и стандартные отклонения предопределены размером второго измерения массива.

Вы можете выполнить то же самое без нумбы, но я предполагаю, что вы этого хотите.

from numba import njit

@njit
def c(a, b):
  n, k = a.shape
  m, k = b.shape

  mu = (k - 1) / 2
  sig = ((k - 1) * (k + 1) / 12) ** .5

  out = np.empty((n, m))

  a = a - mu
  b = b - mu

  for i in range(n):
    for j in range(m):
      out[i, j] = a[i] @ b[j] / k / sig ** 2

  return out

Для потомков мы могли бы вообще избежать внутреннего цикла, но это может иметь проблемы с памятью.

@njit
def c1(a, b):
  n, k = a.shape
  m, k = b.shape

  mu = (k - 1) / 2
  sig = ((k - 1) * (k + 1) / 12) ** .5

  a = a - mu
  b = b - mu

  return a @ b.T / k / sig ** 2

Демонстрация

np.random.seed([3, 1415])

a = np.random.randn(2, 10)
b = np.random.randn(2, 10)

rank_a = rank(a)
rank_b = rank(b)

c(rank_a, rank_b)

array([[0.32121212, 0.01818182],
       [0.13939394, 0.55151515]])

Если вы работали с DataFrame

da = pd.DataFrame(a)
db = pd.DataFrame(b)

pd.DataFrame(c(rank(da.values), rank(db.values)), da.index, db.index)


          0         1
0  0.321212  0.018182
1  0.139394  0.551515

Проверка

Мы можем сделать быструю проверку, используя pandas.DataFrame.corr

pd.DataFrame(a.T).corr('spearman') == c(rank_a, rank_a)

      0     1
0  True  True
1  True  True
0 голосов
/ 17 сентября 2018

Pandas имеет функцию corr с поддержкой spearman.Он работает со столбцами, поэтому мы можем просто транспонировать dataFrame.

Мы добавим df1 к df2 и вычислим корреляцию, повторяя каждую строку

len_df1 = df1.shape[0]
df2_index = df2.index.values.tolist()


df = df2.append(df1).reset_index(drop=True).T
values = {i: [df.iloc[:,df2_index+[i]].corr(method='spearman').values] for i in range(len_df1)}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...