Частичная корреляция в Python - PullRequest
0 голосов
/ 07 сентября 2018

Я запустил матрицу корреляции:

sns.pairplot(data.dropna())
corr = data.dropna().corr()
corr.style.background_gradient(cmap='coolwarm').set_precision(2)

и похоже, что advisory_pct довольно (0.57) отрицательно коррелирует с all_brokerage_pct.Насколько я понимаю, я могу утверждать, что мы совершенно уверены, что «когда у консультанта низкий процент консультативных услуг в его портфеле, он имеет высокий процент всех брокерских услуг в своем портфеле».

Однако это "попарная корреляция, и мы не контролируем влияние остальных возможных переменных.

Я искал SO и не смог найти, как я могу запустить «частичную корреляцию», где матрица корреляции может обеспечить корреляцию между каждыми двумя переменными, при этом контролируя остальные переменные.Для этого предположим, что brokerage % + etf brokerage % + advisory % + all brokerage % = ~ 100% портфеля.

Существует ли такая функция?

enter image description here enter image description here

- РЕДАКТИРОВАТЬ - Запуск данных в соответствии с https://stats.stackexchange.com/questions/288273/partial-correlation-in-panda-dataframe-python:

dict = {'x1': [1, 2, 3, 4, 5], 'x2': [2, 2, 3, 4, 2], 'x3': [10, 9, 5, 4, 9], 'y' : [5.077, 32.330, 65.140, 47.270, 80.570]} 
data = pd.DataFrame(dict, columns=['x1', 'x2', 'x3', 'y'])

partial_corr_array = df.as_matrix()
data_int = np.hstack((np.ones((partial_corr_array.shape[0],1)), partial_corr_array))
print(data_int)
[[  1.      1.      2.     10.      5.077]
 [  1.      2.      2.      9.     32.33 ]
 [  1.      3.      3.      5.     65.14 ]
 [  1.      4.      4.      4.     47.27 ]
 [  1.      5.      2.      9.     80.57 ]]
arr = np.round(partial_corr(partial_corr_array)[1:, 1:], decimals=2)
print(arr)
[[ 1.    0.99  0.99  1.  ]
 [ 0.99  1.   -1.   -0.99]
 [ 0.99 -1.    1.   -0.99]
 [ 1.   -0.99 -0.99  1.  ]]
corr_df = pd.DataFrame(arr, columns = data.columns, index = data.columns)
print(corr_df)
    x1    x2    x3    y
x1  1.00  0.99  0.99  1.00
x2  0.99  1.00 -1.00 -0.99
x3  0.99 -1.00  1.00 -0.99
y   1.00 -0.99 -0.99  1.00

Эти корреляции не имеют большого смысла.Используя мои реальные данные, я получаю очень похожий результат, где все корреляции округляются до -1 ..

Ответы [ 2 ]

0 голосов
/ 30 октября 2018

Чтобы вычислить корреляцию между двумя столбцами в панде DataFrame при управлении одним или несколькими ковариатами (т. Е. Другими столбцами в фрейме данных), вы можете использовать функцию partal_corr функции Pingouin пакет ( отказ от ответственности, автором которого я являюсь ):

from pingouin import partial_corr
partial_corr(data=df, x='X', y='Y', covar=['covar1', 'covar2'], method='pearson')
0 голосов
/ 10 сентября 2018

AFAIK, нет официальной реализации частичной корреляции в scipy / numpy. Как указано @J. C. Rocamonde, функция с этого сайта статистики может использоваться для расчета частичной корреляции.

Я полагаю, что это оригинальный источник:

https://gist.github.com/fabianp/9396204419c7b638d38f

Примечание:

  1. Как обсуждалось на странице github, вы можете добавить столбец из них, чтобы добавить термин смещения к вашим подгонкам, если ваши данные не стандартизированы (судя по вашим данным, это не так).

  2. Если я не ошибаюсь, он вычисляет частичную корреляцию, контролируя все остальные переменные в матрице. Если вы просто хотите управлять одной переменной, вы можете изменить idx на индекс этой конкретной переменной.


Редактировать 1 (Как добавить + Что делать с df):

Если вы посмотрите на ссылку, они уже обсудили, как их добавить.

Чтобы проиллюстрировать, как это работает, я добавил еще один способ hstack, используя данные в ссылке:

data_int = np.hstack((np.ones((data.shape[0],1)), data)) 
test1 = partial_corr(data_int)[1:, 1:]
print(test1)

# You can also add it on the right, as long as you select the correct coefficients
data_int_2 = np.hstack((data, np.ones((data.shape[0],1)))) 
test2 = partial_corr(data_int_2)[:-1, :-1]
print(test2)

data_std = data.copy() 
data_std -= data.mean(axis=0)[np.newaxis, :] 
data_std /= data.std(axis=0)[np.newaxis, :] 
test3 = partial_corr(data_std)
print(test3)

Выход:

[[ 1.         -0.54341003 -0.14076948]
 [-0.54341003  1.         -0.76207595]
 [-0.14076948 -0.76207595  1.        ]]
[[ 1.         -0.54341003 -0.14076948]
 [-0.54341003  1.         -0.76207595]
 [-0.14076948 -0.76207595  1.        ]]
[[ 1.         -0.54341003 -0.14076948]
 [-0.54341003  1.         -0.76207595]
 [-0.14076948 -0.76207595  1.        ]]

А если вы хотите сохранить столбцы, самый простой способ - извлечь столбцы и вернуть их после вычисления:

# Assume that we have a DataFrame with columns x, y, z
data_as_df = pd.DataFrame(data, columns=['x','y','z'])
data_as_array = data_as_df.values
partial_corr_array = partial_corr(np.hstack((np.ones((data_as_array.shape[0],1)), data_as_array))
                                 )[1:,1:]
corr_df = pd.DataFrame(partial_corr_array, columns = data_as_df.columns)
print(corr_df)

Выход:

       x      y      z
0  1.000 -0.543 -0.141
1 -0.543  1.000 -0.762
2 -0.141 -0.762  1.000

Надеюсь, это полезно! Дайте мне знать, если что-то неясно!


Редактировать 2:

Я думаю, что проблема заключается в том, что в каждой из подгонок нет постоянного члена ... Я переписал код в sklearn, чтобы было проще добавить перехват:

def calculate_partial_correlation(input_df):
    """
    Returns the sample linear partial correlation coefficients between pairs of variables,
    controlling for all other remaining variables

    Parameters
    ----------
    input_df : array-like, shape (n, p)
        Array with the different variables. Each column is taken as a variable.

    Returns
    -------
    P : array-like, shape (p, p)
        P[i, j] contains the partial correlation of input_df[:, i] and input_df[:, j]
        controlling for all other remaining variables.
    """
    partial_corr_matrix = np.zeros((input_df.shape[1], input_df.shape[1]));
    for i, column1 in enumerate(input_df):
        for j, column2 in enumerate(input_df):
            control_variables = np.delete(np.arange(input_df.shape[1]), [i, j]);
            if i==j:
                partial_corr_matrix[i, j] = 1;
                continue
            data_control_variable = input_df.iloc[:, control_variables]
            data_column1 = input_df[column1].values
            data_column2 = input_df[column2].values
            fit1 = linear_model.LinearRegression(fit_intercept=True)
            fit2 = linear_model.LinearRegression(fit_intercept=True)
            fit1.fit(data_control_variable, data_column1)
            fit2.fit(data_control_variable, data_column2)
            residual1 = data_column1 - (np.dot(data_control_variable, fit1.coef_) + fit1.intercept_)
            residual2 = data_column2 - (np.dot(data_control_variable, fit2.coef_) + fit2.intercept_)
            partial_corr_matrix[i,j] = stats.pearsonr(residual1, residual2)[0]
    return pd.DataFrame(partial_corr_matrix, columns = input_df.columns, index = input_df.columns)

# Generating data in our minion world
test_sample = 10000;
Math_score = np.random.randint(100,600, size=test_sample) + 20 * np.random.random(size=test_sample)
Eng_score = np.random.randint(100,600, size=test_sample) - 10 * Math_score + 20 * np.random.random(size=test_sample)
Phys_score = Math_score * 5 - Eng_score + np.random.randint(100,600, size=test_sample) + 20 * np.random.random(size=test_sample)
Econ_score = np.random.randint(100,200, size=test_sample) + 20 * np.random.random(size=test_sample)
Hist_score = Econ_score + 100 * np.random.random(size=test_sample)

minions_df = pd.DataFrame(np.vstack((Math_score, Eng_score, Phys_score, Econ_score, Hist_score)).T, 
                          columns=['Math', 'Eng', 'Phys', 'Econ', 'Hist'])

calculate_partial_correlation(minions_df)

Выход:

----  ----------  -----------  ------------  -----------  ------------
Math   1          -0.322462     0.436887     0.0104036    -0.0140536
Eng   -0.322462    1           -0.708277     0.00802087   -0.010939
Phys   0.436887   -0.708277     1            0.000340397  -0.000250916
Econ   0.0104036   0.00802087   0.000340397  1             0.721472
Hist  -0.0140536  -0.010939    -0.000250916  0.721472      1
----  ----------  -----------  ------------  -----------  ------------

Пожалуйста, дайте мне знать, если это не работает!

...