Результаты нечетного коэффициента корреляции Пирсона - PullRequest
2 голосов
/ 28 января 2010

У меня есть задание в моем классе программирования C, чтобы написать программу для получения коэффициента корреляции 2 наборов действительных чисел. Мне дали уравнения, и они ссылались на Википедию, поэтому я дважды проверил уравнения там. Вот ссылка на уравнение, которое кажется довольно стандартным из моих исследований:

alt text

Я написал программу, но когда я запускал ее, я получал числа больше 1 для моих результатов, что, как я знал, было неверным. Я просмотрел свой код несколько раз, но не смог найти ничего неуместного, поэтому я попытался разделить на n в конце вместо n-1, это дало мне значения с ожидаемым диапазоном от -1 до 1, поэтому я протестировал это зависит от значений данных, которые я нашел в Интернете, а также от калькулятора коэффициента корреляции (http://easycalculation.com/statistics/correlation.php), и теперь я получаю правильные результаты для всех чисел, которые я ввел. Я не могу понять, почему это так, поэтому подумал, что я мог бы получить небольшую помощь с этим здесь. Вот мой код для программы: если есть что-то еще, что выделяет меня, я бы хотел услышать несколько советов, но в основном я пытаюсь понять, почему я получаю правильные результаты с тем, что появляется быть неправильным уравнением.

Затем он прочитает значения для обоих массивов (x и y), а затем вычислит коэффициент корреляции между двумя наборами чисел.

#include <stdio.h>
#include <stdlib.h>
#include <math.h>

int main(void) {
   int n;  /* value to determine array length */
   /* declare variables to hold results for each equation for x and y
   initialize all to zero to prepare for summation */
   float r = 0.0, xbar = 0.0, ybar = 0.0, sx = 0.0, sy = 0.0;

   /*get number n input from user */
   printf("Please enter a number n: ");
   scanf("%d", &n);

   if( n < 1) {
      printf("n must be a positive number.\nPlease enter a new value: ");
      scanf("%d", &n);
      if( n < 1) {
         printf("Invalid input, exiting...\n");
         return 0;
      }
   }

   /*initialize arrays x and y with length of n */
   float x[n], y[n];
   /*use for loop to read in values of x*/
   int i;
   for(i = 0; i < n; ++i) {
      printf("Please enter a number for x: ");
      scanf("%f", &x[i]);
   }
   /*use for loop to read in values of y*/
   for(i = 0; i < n; ++i) {
      printf("Please enter a number for y: ");
      scanf("%f", &y[i]);
   }

   /*compute xbar */
   for(i = 0; i < n; ++i) {
      xbar += x[i];
   }
   xbar /= n;
   /*compute ybar*/
   for(i = 0; i < n; ++i) {
      ybar += y[i];
   }
   ybar /= n;

   /* compute standard deviation of x*/
   for(i = 0; i < n; ++i) {
      sx += (x[i] - xbar) * (x[i] - xbar);
   }
   sx = sqrt((sx / n));
   /* compute standard deviation of y */
   for(i = 0; i < n; ++i) {
      sy += (y[i] - ybar) * (y[i] - ybar);
   }
   sy = sqrt((sy / n));

   /*compute r, the correlation coefficient between the two arrays */
   for( i = 0; i < n; ++i ) {
      r += (((x[i] - xbar)/sx) * ((y[i] - ybar)/sy));
   }
   r /= (n); /* originally divided by n-1, but gave incorrect results
   dividing by n instead produces the desired output */
   /* print results */ 
   printf("The correlation coefficient of the entered lists is: %6.4f\n", r);
   return 0;

}

(похоже, что мое форматирование кода не работает, очень сожалею об этом. Пробовал использовать теги и кнопку, но не могу понять. Похоже, что я получил его несколько лучше, чем раньше.)

1 Ответ

7 голосов
/ 28 января 2010

Вы рассчитываете свое стандартное отклонение как:

sx = sqrt((sx / n));

и аналогично для sy.

Используемое вами уравнение использует n-1 в знаменателе для вычисления этого ( причина : есть n-1 степеней свободы, поэтому вы должны разделить на n-1). Итак, ваши sx и sy на самом деле sx' и sy', где sx' = sx*sqrt(n-1)/sqrt(n) и sy' = sy*sqrt(n-1)/sqrt(n). Итак, sx' * sy' = sx * sy * (n-1)/n. Поскольку в знаменателе указан sx*sy, ваш расчет отклоняется с коэффициентом n/(n-1). Разделив это на n, вы получите коэффициент, который вам нужен вне суммирования.

Таким образом, если вы изменили свой код для расчета стандартного отклонения выборки (разделите на n-1), вы можете, наконец, разделить на n-1, и ваш код получит ожидаемый результат. Для эффективности, поскольку деление все равно будет отменено, вы можете сохранить некоторые вычисления и повысить свою точность, просто не деля на n-1 в вычислениях sx и sy, а затем пропустив и окончательное деление:

sx = sqrt((sx / n));
sy = sqrt((sy / n));

стать

sx = sqrt(sx);
sy = sqrt(sy);

и

r /= (n);

уходит совсем.

Редактировать : Так как вы спросили ...

  1. Нет смысла использовать float, если нет необходимости. double дает вам гораздо лучшую точность.
  2. По умолчанию stdout - это буферизованная строка в большинстве систем, поэтому ваш запрос может не отображаться до вашего звонка на scanf(). Чтобы убедиться, что ваш запрос отображается, выполните fflush(stdout); после вашего printf() вызова.
  3. Очень трудно безопасно использовать scanf(). Для чтения чисел scanf() имеет неопределенное поведение, когда кто-то вводит число, которое не находится в диапазоне типа данных. Кроме того, это плохо для случаев, когда кто-то вводит нецелое число в ответ на ваше приглашение. В вашем случае вы можете сделать n допустимым в качестве параметра командной строки, а затем использовать strtol(argv[1]) для анализа числа. Если вы все равно хотите прочитать с stdin, используйте комбинацию fgets() + sscanf() или fgets() + strtol().
  4. Вы можете уменьшить количество циклов в вашей программе. Для одного вы можете вычислить xbar и ybar в одном цикле. Более того, вы можете написать функцию double avg(double *data, int n), которая вычисляет среднее значение n значений, а затем выполнить: xbar=avg(x, n);, ybar=avg(y, n);.
  5. Аналогично, вы можете определить функцию double std(double *data, int n), а затем использовать ее для вычисления sx и sy.
  6. Наконец, хотя все в порядке, у вас слишком много скобок: sqrt((sx / n)); лучше записать как sqrt(sx / n);. r /= (n); скобки тоже не нужны.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...