Как сделать новый массив, используя данный массив? - PullRequest
0 голосов
/ 13 февраля 2019

работа выполняется, функция вычисляет количество выбросов в массиве измерений.

функция вычисления медианы уже задана.

Если измерение находится за пределами диапазона[0,5 * медиана до 1,5 * медиана], тогда это выброс и, следовательно, должен быть отброшен.Поэтому я пытался сделать столько, сколько смогу.Мне просто интересно, как получить массив избавился от выбросов из массива источника.Я сделал новый массив для хранения числа в диапазоне.и возвращаемое значение - выделить данные.

task1_main.c

#include<stdio.h>
#include<stdlib.h>
#include "task1.c"

int main()
{
int i, size1, size2;

// reading the number of measurements in group1 
scanf("%d", &size1);        
float *measurements1 = malloc(size1*sizeof(float));
// reading the measurements in group1   
for(i=0; i<size1; i++)
scanf("%f", measurements1+i);

// reading the number of measurements in group2 
scanf("%d", &size2);        
float *measurements2 = malloc(size2*sizeof(float));
// reading the measurements in group1   
for(i=0; i<size2; i++)
scanf("%f", measurements2+i);



float median1 = sort_and_find_median(measurements1, size1);
int new_size1;
float *measurements1_wo_outliers = discard_outliers(measurements1, size1, median1, &new_size1);

float median2 = sort_and_find_median(measurements2, size2);
int new_size2;
float *measurements2_wo_outliers = discard_outliers(measurements2, size2, median2, &new_size2);

// writing measurements for group1 after discarding the outliers
printf("%d\n", new_size1);
for(i=0; i<new_size1; i++)
printf("%.2f\n", measurements1_wo_outliers[i]);

printf("\n");
// writing measurements for group2 after discarding the outliers
printf("%d\n", new_size2);
for(i=0; i<new_size2; i++)
printf("%.2f\n", measurements2_wo_outliers[i]);


free(measurements1);
free(measurements2);
free(measurements1_wo_outliers);
free(measurements2_wo_outliers);
return 0;
}

task1.c

// function to sort the array in ascending order
float sort_and_find_median(float *measurements , int size)
{
  int i=0 , j=0;
  float temp=0;

  for(i=0 ; i<size ; i++)
    {
      for(j=0 ; j<size-1 ; j++)
    {
      if(measurements[j]>measurements[j+1])
        {
          temp        = measurements[j];
          measurements[j]    = measurements[j+1];
          measurements[j+1]  = temp;
        }
    }
    }

  return measurements[size/2];
}

float *discard_outliers(float *measurements, int size, float median, int *new_size)
{

  //float number_of_outliers[0];
  int i= 0;
  for(i = 0; i<size; i++){
    if((measurements[i] < (0.5*median)) && (measurements[i] > (1.5*median))){
      number_of_outliers[i] = measurements[i];
    }

  }


  *new_size = size - number_of_outliers;
  //to creates a new array of length *newsize using malloc 
  *measurements_wo_outliers = malloc( (*new_size) * sizeof(float) );

}

Предположим, что в группе 1 и группе 2 соответственно 3 и 4 пациента.Пусть измерения будут {45,0, 23,15, 11,98} и {2,45, 11,0, 12,98, 77,80} для группы 1 и группы 2 соответственно.
Содержимое измерений .txt будет:

3

45,0

23,15

11,98

4

2,45

11,0

12,98

77.80

mesurements.txt:

25 23,0 21,5 27,6 2,5 19,23 21,0 23,5 24,6 19,5 19,23 26,01 22,5 24,6 20,15 18,23 19,73 22,25 26,6 45,5 5,23 18,0 24,5 23,26 22,5 18,93

20 11.12 10.32 9.91 14.32 12.32 20.37 13.32 11.57 2.32 13.32 11.22 12.32 10.91 8.32 14.56 10.16 35.32 12.91 12.58 13.32

и ожидаемые измерения приведены ниже:

22 18.00 18.23 18.93 19.23 19.23 19.50 19.73 20.15 21.00 21.5022,25 22,50 22,50 23,00 23,26 23.50 24,50 24,60 24,60 26,01 26,60 27.60

17 8,32 9,91 10,16 10,32 10,91 11,12 11,22 11,57 12,32 12,32 12,58 12,91 13,32 13,32 13,32 14,32 14,56

Ответы [ 2 ]

0 голосов
/ 13 февраля 2019

В дополнение к вашему текущему ответу у вас есть ряд проблем, но ваша проблема с идентификацией выброса заключается в том, что вы используете '&&' вместо '||', что препятствует обнаружению какого-либо выброса, поскольку ваше тестовое условие всегда оценивает FALSE Например,

if((measurements[i] < (0.5*median)) && (measurements[i] > (1.5*median))){

(элемент массива никогда не может быть одновременно меньше (0.5*median) и больше (1.5*median) одновременно)

Помимо того, что вы определили выбросы, как отмечено в комментариях и в ответе @ paddy's , вам не нужно копировать или размещать в своей функции удаления выбросов.Вместо этого удалите выбросы, перетасовав все элементы выше выброса на единицу, удалив выброс с помощью memmove, и перед возвратом из функции, если выбросы были удалены, вы можете (необязательно) realloc один раз в конце, чтобы урезать распределениеразмер.

(что на самом деле не нужно, если вы не работаете со встроенной системой с ограниченным объемом памяти или имеете миллионы элементов, с которыми вы имеете дело)

Уборка вашей функции удаления и передача адрес вашего массива из main(), чтобы разрешить перераспределение в функции без необходимости присваивания возврата, вы можете сделать что-то вроде:

/* remove outliers from array 'a' given 'median'.
 * takes address of array 'a', address of number of elements 'n',
 * and median 'median' to remove outliers. a is reallocated following
 * removal and n is updated to reflect the number of elements that
 * remain. returns pointer to reallocated array on success, NULL otherwise.
 */
double *rmoutliers (double **a, size_t *n, double median)
{
    size_t i = 0, nelem = *n;   /* index, save initial numer of elements */

    while (i < *n)  /* loop over all elements indentifying outliers */
        if ((*a)[i] < 0.5 * median || (*a)[i] > 1.5 * median) {
            if (i < *n - 1)     /* if not end, use memmove to remove */
                memmove (&(*a)[i], &(*a)[i+1], 
                        (*n - i + 1) * sizeof **a);
            (*n)--; /* decrement number of elements */
        }
        else        /* otherwise, increment index */
            i++;

    if (*n < nelem) {   /* if outliers removed */
        void *dbltmp = realloc (*a, *n * sizeof **a);   /* realloc */
        if (!dbltmp) {  /* validate reallocation */
            perror ("realloc-a");
            return NULL;
        }
        *a = dbltmp;    /* assign reallocated block to array */
    }

    return *a;      /* return array */
}

Далее, не сортируйте свою собственную функцию сортировки,Библиотека C предоставляет qsort, что на несколько порядков с меньшей вероятностью будет содержать ошибки, чем ваши (не говоря уже о порядках быстрее).Все, что вам нужно сделать, это написать qsort функцию сравнения, которая получает указатели на соседние элементы из вашего массива и затем возвращает -1, если первая сортируется перед вторым, 0, если элементы равны, и 1если второе сортирует перед первым.Для числовых сравнений вы можете вернуть результат в два неравенства, чтобы избежать потенциального переполнения / недополнения, например,

    /* qsort compare to sort numbers in ascending order without overflow */
    return (a > b) - (a < b);

Отметив, что a и b будут указателями на double (или * 1040)*) в вашем случае, для сравнения значений типа double, правильное приведение перед разыменованием будет выглядеть следующим образом:

/* qsort compare function for doubles (ascending) */
int cmpdbl (const void *a, const void *b)
{
    return (*((double *)a) > *((double *)b)) - 
            (*((double *)a) < *((double *)b));
}

Это единственная сложность при использовании qsort после этого для сортировки массива в порядке возрастания больше ничего не требуетсячем:

        qsort (array, n, sizeof *array, cmpdbl);    /* use qsort to sort */

(готово ...)

В целом это краткий пример, который просто читает ваши массивы как строки ввода (1024 chars max), а затем преобразует каждыйЗначение double с использованием sscanf, хранящим любое количество значений в динамически изменяемой переменной array перед сортировкой, захватом медианы и вызовом функции удаления, можно записать следующим образом.

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

#define MAXC 1024   /* max characters to read per-line (per-array) */
#define MAXD 8      /* initial number of doubles to allocate */

/* qsort compare function for doubles (ascending) */
int cmpdbl (const void *a, const void *b)
{
    return (*((double *)a) > *((double *)b)) - 
            (*((double *)a) < *((double *)b));
}

/* remove outliers from array 'a' given 'median'.
 * takes address of array 'a', address of number of elements 'n',
 * and median 'median' to remove outliers. a is reallocated following
 * removal and n is updated to reflect the number of elements that
 * remain. returns pointer to reallocated array on success, NULL otherwise.
 */
double *rmoutliers (double **a, size_t *n, double median)
{
    size_t i = 0, nelem = *n;   /* index, save initial numer of elements */

    while (i < *n)  /* loop over all elements indentifying outliers */
        if ((*a)[i] < 0.5 * median || (*a)[i] > 1.5 * median) {
            if (i < *n - 1)     /* if not end, use memmove to remove */
                memmove (&(*a)[i], &(*a)[i+1], 
                        (*n - i + 1) * sizeof **a);
            (*n)--; /* decrement number of elements */
        }
        else        /* otherwise, increment index */
            i++;

    if (*n < nelem) {   /* if outliers removed */
        void *dbltmp = realloc (*a, *n * sizeof **a);   /* realloc */
        if (!dbltmp) {  /* validate reallocation */
            perror ("realloc-a");
            return NULL;
        }
        *a = dbltmp;    /* assign reallocated block to array */
    }

    return *a;      /* return array */
}

int main (void) {

    char buf[MAXC];
    int arrcnt = 1;

    while (fgets (buf, MAXC, stdin)) {  /* read line of data into buf */
        int offset = 0, nchr = 0;
        size_t  n = 0, ndbl = MAXD, size;
        double  *array = malloc (ndbl * sizeof *array), /* allocate */
                dbltmp, median;

        if (!array) {   /* validate initial allocation */
            perror ("malloc-array");
            return 1;
        }
        /* parse into doubles, store in dbltmp (should use strtod) */
        while (sscanf (buf + offset, "%lf%n", &dbltmp, &nchr) == 1) {
            if (n == ndbl) {    /* check if reallocation requierd */
                void *tmp = realloc (array, 2 * ndbl * sizeof *array);
                if (!tmp) {     /* validate */
                    perror ("realloc-array");
                    break;
                }
                array = tmp;    /* assign reallocated block */
                ndbl *= 2;      /* update allocated number of doubles */
            }
            array[n++] = dbltmp;    /* assign to array, increment index */
            offset += nchr;     /* update offset in buffer */
        }

        qsort (array, n, sizeof *array, cmpdbl);    /* use qsort to sort */
        median = array[n / 2];                      /* get median */

        /* output original array and number of values */
        printf ("\narray[%d] - %zu values\n\n", arrcnt++, n);
        for (size_t i = 0; i < n; i++) {
            if (i && i % 10 == 0)
                putchar ('\n');
            printf (" %5.2f", array[i]);
        }
        printf ("\n\nmedian: %5.2f\n\n", median);

        size = n;   /* save orginal number of doubles in array in size */
        if (!rmoutliers (&array, &n, median))   /* remove outliers */
            return 1;

        if (n < size) { /* check if outliers removed */
            printf ("%zu outliers removed - %zu values\n\n", size - n, n);
            for (size_t i = 0; i < n; i++) {
                if (i && i % 10 == 0)
                    putchar ('\n');
                printf (" %5.2f", array[i]);
            }
            printf ("\n\n");
        }
        else    /* otherwise warn no outliers removed */
            fputs ("warning: no outliers found.\n\n", stderr);

        free (array);   /* don't forget to free what you allocate */
    }
}

( примечание: вы действительно должны использовать strtod, поскольку sscanf не обеспечивает обработки ошибок, кроме сообщения об успешном / неудачном преобразовании, но это на другой день или оставлено вам как упражнение)

Пример входного файла

Примечание. Я не использовал информацию size: X в моем файле данных.Это было не нужно.Я просто использовал схему динамического размещения для определения размера массивов по мере необходимости.Формат входного файла, который я использовал, содержал значения измерений для каждого массива в отдельной строке, например

23.0 21.5 27.6 2.5 19.23 21.0 23.5 24.6 19.5 19.23 26.01 22.5 24.6 20.15 ... 18.93
11.12 10.32 9.91 14.32 12.32 20.37 13.32 11.57 2.32 13.32 11.22 12.32 ... 13.32

Пример использования / Вывод

$ ./bin/rmoutliers <dat/outlierdata.txt

array[1] - 25 values

  2.50  5.23 18.00 18.23 18.93 19.23 19.23 19.50 19.73 20.15
 21.00 21.50 22.25 22.50 22.50 23.00 23.26 23.50 24.50 24.60
 24.60 26.01 26.60 27.60 45.50

median: 22.25

3 outliers removed - 22 values

 18.00 18.23 18.93 19.23 19.23 19.50 19.73 20.15 21.00 21.50
 22.25 22.50 22.50 23.00 23.26 23.50 24.50 24.60 24.60 26.01
 26.60 27.60


array[2] - 20 values

  2.32  8.32  9.91 10.16 10.32 10.91 11.12 11.22 11.57 12.32
 12.32 12.58 12.91 13.32 13.32 13.32 14.32 14.56 20.37 35.32

median: 12.32

3 outliers removed - 17 values

  8.32  9.91 10.16 10.32 10.91 11.12 11.22 11.57 12.32 12.32
 12.58 12.91 13.32 13.32 13.32 14.32 14.56

( примечание: в любом коде, который динамически выделяет память, вы должны запускать программу через программу проверки ошибок памяти, такую ​​как valgrind для Linux, в других ОС есть аналогичные инструменты. Это просто, просто запустите add valgrind дляначало вашей команды, например, valgrind ./bin/rmoutliers <dat/outlierdata.txt и подтвердите, что вы освободили всю выделенную память и что ошибок памяти нет.)

Просмотрите все и дайте мне знать, если у вас есть вопросы.

Использование памяти / проверка ошибок

В своем комментарии вы, похоже, обеспокоены тем, что то, что я делаю, может привести к утечке памяти - это не так.Как уже упоминалось в вопросе, вы можете проверить использование памяти и проверить наличие ошибок памяти с помощью таких инструментов, как valgrind, например,

$ valgrind ./bin/rmoutliers <dat/outlierdata.txt
==28383== Memcheck, a memory error detector
==28383== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==28383== Using Valgrind-3.12.0 and LibVEX; rerun with -h for copyright info
==28383== Command: ./bin/rmoutliers
==28383==

array[1] - 25 values

  2.50  5.23 18.00 18.23 18.93 19.23 19.23 19.50 19.73 20.15
 21.00 21.50 22.25 22.50 22.50 23.00 23.26 23.50 24.50 24.60
 24.60 26.01 26.60 27.60 45.50

median: 22.25

3 outliers removed - 22 values

 18.00 18.23 18.93 19.23 19.23 19.50 19.73 20.15 21.00 21.50
 22.25 22.50 22.50 23.00 23.26 23.50 24.50 24.60 24.60 26.01
 26.60 27.60


array[2] - 20 values

  2.32  8.32  9.91 10.16 10.32 10.91 11.12 11.22 11.57 12.32
 12.32 12.58 12.91 13.32 13.32 13.32 14.32 14.56 20.37 35.32

median: 12.32

3 outliers removed - 17 values

  8.32  9.91 10.16 10.32 10.91 11.12 11.22 11.57 12.32 12.32
 12.58 12.91 13.32 13.32 13.32 14.32 14.56

==28383==
==28383== HEAP SUMMARY:
==28383==     in use at exit: 0 bytes in 0 blocks
==28383==   total heap usage: 8 allocs, 8 frees, 1,208 bytes allocated
==28383==
==28383== All heap blocks were freed -- no leaks are possible
==28383==
==28383== For counts of detected and suppressed errors, rerun with: -v
==28383== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

Если вы заметили выше, было выделено "8 выделений и8 освобождений " связано с памятью, используемой выше, например:

==28383==   total heap usage: 8 allocs, 8 frees, 1,208 bytes allocated

Вы также можете подтвердить, что вся память была освобождена и в следующей строке не было утечек:

==28383== All heap blocks were freed -- no leaks are possible

И, наконец, вы можете подтвердить, что не было ошибок памяти, связанных с использованием памяти во время программывыполнение:

==28383== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

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

0 голосов
/ 13 февраля 2019

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

Сначала я заметил, что ваша логика проверки выбросов неверна.Измерение не может быть меньше 0.5*median И больше 1.5*median ... Если median не отрицательно.Давайте разберемся с этим, разрешив оба:

// Choose stable lower and upper bounds
const float low =  (median < 0.f ? 1.5f : 0.5f) * median;
const float high = (median < 0.f ? 0.5f : 1.5f) * median;

Это гарантирует, что low <= high всегда (за исключением случаев, когда low или high в конечном итоге станут NaN).

Теперь вам нужно удалить выбросы.Самый простой способ сделать это - сохранить второй индекс, в котором будет записано, сколько не выпадающих значений вы уже видели.Пройдитесь по массиву, и если какой-либо выброс будет найден, вы также будете перемешивать значения по ходу.

// Remove outliers
int num_clean = 0;
for(int i = 0; i < size; i++)
{
    float value = measurements[i];
    if(value >= low && value <= high)
    {
        ++num_clean;
        if (i != num_clean)
            measurements[num_clean] = value;
    }
}

В конце этого num_clean представляет количество оставшихся значений.Вам решать, изменять размер массива или нет.Вы можете использовать следующую логику:

// Resize array
if (num_clean < size)
{
    float *new_measurements = realloc(measurements, num_clean * sizeof float);
    if (new_measurements)
        measurements = new_measurements;
    *new_size = num_clean;
}

Обратите внимание, что вам может потребоваться дополнительная обработка в случае, если num_clean заканчивается как 0. Вы должны решить, освобождать ли ваш массив или нет.Выше также есть тихая обработка случая, когда realloc терпит неудачу - мы сохраним исходный указатель массива, но обновим new_size.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...