修剪/温莎化标准差

1 投票
3 回答
3621 浏览
提问于 2025-04-17 11:06

有什么高效的方法可以计算一个列表的修剪标准差温莎标准差呢?

我不介意使用numpy,但是如果我必须单独复制一份列表,那样会很慢。

3 个回答

1

为了得到一个不偏的修剪平均值,你需要考虑列表中项目的分数部分,具体的说明可以在这里找到,另外在这里也有一些相关的内容。我写了一个函数来实现这个功能:

def percent_tmean( data, pcent ):
   # make sure data is a list
   dc = list( data )
   # find the number of items
   n = len(dc)
   # sort the list
   dc.sort()
   # get the proportion to trim
   p = pcent / 100.0
   k = n*p
   # print "n = %i\np = %.3f\nk = %.3f" % ( n,p,k )
   # get the decimal and integer parts of k
   dec_part, int_part = modf( k )
   # get an index we can use
   index = int(int_part)
   # trim down the list
   dc = dc[ index: index * -1 ]
   # deal with the case of trimming fractional items
   if dec_part != 0.0:
       # deal with the first remaining item
       dc[ 0 ] = dc[ 0 ] * (1 - dec_part)
       # deal with last remaining item
       dc[ -1 ] = dc[ -1 ] * (1 - dec_part)
   return sum( dc ) / ( n - 2.0*k )

我还制作了一个iPython Notebook来演示这个功能。

我的函数可能会比已经发布的那些慢一些,但它会给出不偏的结果。

1

这就是生成器函数的用途。

SD需要进行两次遍历,还要一个计数。因此,你需要对基础集合中的一些迭代器进行“分流”。

所以。

trimmed = ( x for x in the_list if low <= x < high )
sum_iter, len_iter, var_iter = itertools.tee( trimmed, 3 )
n = sum( 1 for x in len_iter)
mean = sum( sum_iter ) / n
sd = math.sqrt( sum( (x-mean)**2 for x in var_iter ) / (n-1) )

像这样的东西可能会在不复制任何东西的情况下实现你想要的效果。

1

这会生成两个副本,但你可以试试看,因为速度应该非常快。

def trimmed_std(data, low, high):
    tmp = np.asarray(data)
    return tmp[(low <= tmp) & (tmp < high)].std()

你需要进行排名顺序修剪吗(比如修剪5%)?

更新:

如果你需要按百分位数修剪,我想到的最好方法是先对数据进行排序。像这样应该可以工作:

def trimmed_std(data, percentile):
    data = np.array(data)
    data.sort()
    percentile = percentile / 2.
    low = int(percentile * len(data))
    high = int((1. - percentile) * len(data))
    return data[low:high].std(ddof=0)

显然,你可以不使用numpy来实现这个功能,但即使考虑到将列表转换为数组的时间,使用numpy的速度也比我能想到的任何方法都要快。

撰写回答