Hi there,

Bug #24064 (submitted by [EMAIL PROTECTED]) requests a standard deviation
function for PHP.  I realise that any of you could implement this in 10
minutes, but according to the bug database it is still Open so I figured I
would give it a try myself!

There are probably a dozen errors in the code and/or places where it could
be better optimised, but I'm hoping one of you might be able to help with
that.  So, the attached diff file implements the function array_std_dev(),
to calculate standard deviation using the deviation method.

With the function in place, standard deviation is calculated like this:

<?php
  $scores = array(18,5,7,18,3,2,10);
  print array_std_dev($score);
  // prints 6.6833125519211
?>

My first attempt at implementing this was using an extra array to buffer the
deviations - this was more out of curiosity to see how the array stuff
works.  Sadly, it caused PHP to segfault and I couldn't figure out why - can
any of you help me spot the brain fart?  (I've attached the offending code
in bad_stddev_code.txt)

Yours,


Paul

PS: I'm not on the internals list, so I would appreciate it if you would CC
me on your reply.
/* {{{ proto mixed array_std_dev(array input)
   Returns the standard deviation of the array entries */
PHP_FUNCTION(array_std_dev)
{
        zval **input,
                 **entry,
                 *entry_n,
                 *squareddev;
        int argc = ZEND_NUM_ARGS();
        HashPosition pos;
        double dval;
        double total;
        double mean;
        int numelements = 0; // note this is calcuated by hand, not using 
zend_hash_num_elements()
        double deviation;

        if (argc != 1 || zend_get_parameters_ex(argc, &input) == FAILURE) {
                WRONG_PARAM_COUNT;
        }

        if (Z_TYPE_PP(input) != IS_ARRAY) {
                php_error_docref(NULL TSRMLS_CC, E_WARNING, "The argument should be an 
array");
                return;
        }

        ZVAL_LONG(return_value, 0);
        total = 0;

        // step one: sum the values of the array
        for (zend_hash_internal_pointer_reset_ex(Z_ARRVAL_PP(input), &pos);
                 zend_hash_get_current_data_ex(Z_ARRVAL_PP(input), (void **)&entry, 
&pos) == SUCCESS;
                 zend_hash_move_forward_ex(Z_ARRVAL_PP(input), &pos)) {

                if (Z_TYPE_PP(entry) == IS_ARRAY || Z_TYPE_PP(entry) == IS_OBJECT)
                        continue;

                entry_n = *entry;
                zval_copy_ctor(entry_n);
                convert_scalar_to_number(entry_n TSRMLS_CC);
                convert_to_double(entry_n);
                total += Z_DVAL_P(entry_n);

                // this is incremented by hand so that it doesn't count object and 
array elements as an element
                numelements++;
        }

        // step two: calculate the mean of the input array
    mean = total / numelements;

        // step three: get the deviation from the mean of each number in the input 
array, and add it to the squareddev array
        MAKE_STD_ZVAL(squareddev);
        array_init(squareddev);

        for (zend_hash_internal_pointer_reset_ex(Z_ARRVAL_PP(input), &pos);
                zend_hash_get_current_data_ex(Z_ARRVAL_PP(input), (void **)&entry, 
&pos) == SUCCESS;
                zend_hash_move_forward_ex(Z_ARRVAL_PP(input), &pos)) {

                if (Z_TYPE_PP(entry) == IS_ARRAY || Z_TYPE_PP(entry) == IS_OBJECT)
                        continue;

                entry_n = *entry;
                zval_copy_ctor(entry_n);
                convert_scalar_to_number(entry_n TSRMLS_CC);
                convert_to_double(entry_n);

                Z_DVAL_P(entry_n) -= mean;

                zend_hash_next_index_insert(Z_ARRVAL_P(squareddev), entry_n, 
sizeof(zval *), NULL);

        }

        // step four: sum the squared deviation array
        total = 0;

        for (zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(squareddev), &pos);
                 zend_hash_get_current_data_ex(Z_ARRVAL_P(squareddev), (void 
**)&entry, &pos) == SUCCESS;
                 zend_hash_move_forward_ex(Z_ARRVAL_P(squareddev), &pos)) {

                entry_n = *entry;
                zval_copy_ctor(entry_n);
                convert_scalar_to_number(entry_n TSRMLS_CC);
                convert_to_double(entry_n);
                total += Z_DVAL_P(entry_n);
        }

        // step five: divide the sum of the squared deviation array by the number of 
elements - 1
        total /= numelements - 1;
        Z_DVAL_P(return_value) = sqrt(total);

}
/* }}} */
-- 
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php

Reply via email to