【问题标题】:Standard Deviation for SQLiteSQLite 的标准差
【发布时间】:2010-02-19 17:30:28
【问题描述】:

我搜索了 SQLite 文档,但找不到任何东西,但我也在 Google 上搜索了一些结果。

SQLite 有没有内置的标准差函数?

【问题讨论】:

    标签: sqlite function standard-deviation


    【解决方案1】:

    您可以在 SQL 中计算方差:

    create table t (row int);
    insert into t values (1),(2),(3);
    SELECT AVG((t.row - sub.a) * (t.row - sub.a)) as var from t, 
        (SELECT AVG(row) AS a FROM t) AS sub;
    0.666666666666667
    

    但是,您仍然需要计算平方根才能得到标准差。

    【讨论】:

    • 你也可以group by使用这种技术...SELECT AVG((t.num - sub.a) * (t.num - sub.a)) as var from t, (SELECT name, AVG(t.num) AS a FROM t group by name) AS sub where t.name = sub.name group by sub.name
    • 需要注意的是,这个算法计算的是有偏差的方差。方差应始终计算为 $\frac{1}{n-1} \sum(X_i - \bar{X})$,但您要除以 $n$ 而不是 $n-1$。由于 $n \rightarrow \infty$ 这种差异变得可以忽略不计,但人们会想知道为什么您的差异与任何标准软件都不匹配......
    • 对不起,我以为这会渲染乳胶。用简单的英语来说,这种计算方差的方式是不正确的,但并不是绝对可怕的。对于大量观察,此版本与通常方法之间的差异将相当小,但您将不得不解释为什么您的方差与标准软件输出不匹配。您可以告诉统计学家您正在使用有偏差的方差版本,但我发现,如果您的方差与标准软件不匹配,不太倾向于量化的人会感到不舒服。
    【解决方案2】:

    SQLite 支持的聚合函数在这里:

    http://www.sqlite.org/lang_aggfunc.html

    STDEV 不在列表中。

    但是,this page 中的模块 extension-functions.c 包含 STDEV 函数。

    【讨论】:

      【解决方案3】:

      sqlite 中仍然没有内置的 stdev 函数。但是,您可以定义(如 Alix 所做的那样)用户定义的聚合器函数。这是一个完整的 Python 示例:

      import sqlite3
      import math
      
      class StdevFunc:
          def __init__(self):
              self.M = 0.0
              self.S = 0.0
              self.k = 1
      
          def step(self, value):
              if value is None:
                  return
              tM = self.M
              self.M += (value - tM) / self.k
              self.S += (value - tM) * (value - self.M)
              self.k += 1
      
          def finalize(self):
              if self.k < 3:
                  return None
              return math.sqrt(self.S / (self.k-2))
      
      with sqlite3.connect(':memory:') as con:
      
          con.create_aggregate("stdev", 1, StdevFunc)
      
          cur = con.cursor()
      
          cur.execute("create table test(i)")
          cur.executemany("insert into test(i) values (?)", [(1,), (2,), (3,), (4,), (5,)])
          cur.execute("insert into test(i) values (null)")
          cur.execute("select avg(i) from test")
          print("avg: %f" % cur.fetchone()[0])
          cur.execute("select stdev(i) from test")
          print("stdev: %f" % cur.fetchone()[0])
      

      这将打印:

      avg: 3.000000
      stdev: 1.581139
      

      与 MySQL 比较:http://sqlfiddle.com/#!2/ad42f3/3/0

      【讨论】:

      • 我是 OP,以防你没有注意到。
      • 啊,我不知道你回答了自己的问题。
      • 非常有用的解决方案!但我认为某处有一个小错误,因为 [1, 2, 3, 4, 5] 的标准差是 1.41 而不是 1.58
      • 标准差有两个版本。这与 MySQL 实现相匹配。如果您愿意,可以为其他版本的 stdev 编辑它。我认为您需要将 self.k-2 更改为 self.k-1 。
      • 我尝试在您的方法中实现 stddev 函数,但出现错误“用户定义聚合的 'step' 方法引发错误”,有什么建议吗?
      【解决方案4】:

      使用方差公式 V(X) = E(X^2) - E(X)^2。在 SQL sqlite 中

      SELECT AVG(col*col) - AVG(col)*AVG(col) FROM table
      

      要获得标准差,您需要取平方根 V(X)^(1/2)

      【讨论】:

      • 很好的捷径,但该方差公式只是渐近正确的。并不能真正取代正确的实现。
      • 这对我有用。与 MS Excel 协调。
      • 我怎样才能把 sqrt 放在这个查询中?
      【解决方案5】:

      我将Welford's method(与extension-functions.c 相同)实现为SQLite UDF:

      $db->sqliteCreateAggregate('stdev',
          function (&$context, $row, $data) // step callback
          {
              if (isset($context) !== true) // $context is null at first
              {
                  $context = array
                  (
                      'k' => 0,
                      'm' => 0,
                      's' => 0,
                  );
              }
      
              if (isset($data) === true) // the standard is non-NULL values only
              {
                  $context['s'] += ($data - $context['m']) * ($data - ($context['m'] += ($data - $context['m']) / ++$context['k']));
              }
      
              return $context;
          },
          function (&$context, $row) // fini callback
          {
              if ($context['k'] > 0) // return NULL if no non-NULL values exist
              {
                  return sqrt($context['s'] / $context['k']);
              }
      
              return null;
          },
      1);
      

      这在 PHP 中($db 是 PDO 对象)但移植到另一种语言应该很简单。

      SQLite soooo 很酷。

      【讨论】:

      • 我相信你想在最后除以 n-1,而不是 n。也就是说,使用($context['k'] - 1)。您链接到的有关 Welford 方法的帖子也已更新以反映此更正。
      • @ahfoss:请注意,另一篇文章从 1 开始 k,而我从 0 开始。他也使用后增量运算符,而我使用前增量运算符。
      • 其他帖子有一个错误。现在已修复,但您的帖子也有同样的问题。
      • @alex.forencich:ahfoss 提到的不是同一件事吗?
      • 可能。取决于您要实现的 stdev 版本。在 Excel 中,STDEV(1,2,3,4,5) = 1.58。您的代码将返回 1.41。
      【解决方案6】:

      一个小技巧

      select ((sum(value)*sum(value) - sum(value * value))/((count(*)-1)*(count(*)))) 
      from the_table ;
      

      那么剩下的就是在外面计算sqrt了。

      【讨论】:

      • 我认为你弄错了。对于值 1 和 3,stdev² 应该是 2。你的计算是 3。但是如果我理解你试图做什么(内联计算平均值),它应该是这样的:SELECT (SUM(value*value) - SUM(value)*SUM(value)/COUNT(*)) / (COUNT(*)-1)
      • 哎呀,错字了!是的,select ((count(*)*sum(value * value) - (sum(value)*sum(value))/((count(*)-1)*(count(*)))) from ... ; == 你的。谢谢~
      【解决方案7】:

      不,我搜索了同样的问题,最后不得不使用我的应用程序 (PHP) 进行计算

      【讨论】:

        【解决方案8】:

        在 python 函数中添加了一些错误检测

        class StdevFunc:
            """
            For use as an aggregate function in SQLite
            """
            def __init__(self):
                self.M = 0.0
                self.S = 0.0
                self.k = 0
        
            def step(self, value):
                try:
                    # automatically convert text to float, like the rest of SQLite
                    val = float(value) # if fails, skips this iteration, which also ignores nulls
                    tM = self.M
                    self.k += 1
                    self.M += ((val - tM) / self.k)
                    self.S += ((val - tM) * (val - self.M))
                except:
                    pass
        
            def finalize(self):
                if self.k <= 1: # avoid division by zero
                    return none
                else:
                    return math.sqrt(self.S / (self.k-1))
        

        【讨论】:

          【解决方案9】:

          您没有说明要计算哪个版本的标准差,但可以使用 sum() 和 count() 聚合函数的组合来计算任一版本的方差(标准差平方)。

          select  
          (count(val)*sum(val*val) - (sum(val)*sum(val)))/((count(val)-1)*(count(val))) as sample_variance,
          (count(val)*sum(val*val) - (sum(val)*sum(val)))/((count(val))*(count(val))) as population_variance
          from ... ;
          

          仍然需要取它们的平方根以获得标准偏差。

          【讨论】:

            【解决方案10】:
            #!/usr/bin/python
            # -*- coding: utf-8 -*-
            #Values produced by this script can be verified by follwing the steps
            #found at https://support.microsoft.com/en-us/kb/213930 to Verify
            #by chosing a non memory based database.
            import sqlite3
            import math
            import random
            import os
            import sys
            import traceback
            import random
            
            class StdevFunc:
                def __init__(self):
                    self.M = 0.0    #Mean
                    self.V = 0.0    #Used to Calculate Variance
                    self.S = 0.0    #Standard Deviation
                    self.k = 1      #Population or Small 
            
                def step(self, value):
                    try:
                        if value is None:
                            return None
            
                        tM = self.M
                        self.M += (value - tM) / self.k
                        self.V += (value - tM) * (value - self.M)
                        self.k += 1
                    except Exception as EXStep:
                        pass
                        return None    
            
                 def finalize(self):
                    try:
                        if ((self.k - 1) < 3):
                            return None
            
                        #Now with our range Calculated, and Multiplied finish the Variance Calculation
                        self.V = (self.V / (self.k-2))
            
                        #Standard Deviation is the Square Root of Variance
                        self.S = math.sqrt(self.V)
            
                        return self.S
                    except Exception as EXFinal:
                        pass
                        return None 
            
            def Histogram(Population):
                try:
                    BinCount = 6 
                    More = 0
            
                    #a = 1          #For testing Trapping
                    #b = 0          #and Trace Back
                    #c = (a / b)    #with Detailed Info
            
                    #If you want to store the Database
                    #uncDatabase = os.path.join(os.getcwd(),"BellCurve.db3")
                    #con = sqlite3.connect(uncDatabase)
            
                    #If you want the database in Memory
                    con = sqlite3.connect(':memory:')    
            
                    #row_factory allows accessing fields by Row and Col Name
                    con.row_factory = sqlite3.Row
            
                    #Add our Non Persistent, Runtime Standard Deviation Function to the Database
                    con.create_aggregate("Stdev", 1, StdevFunc)
            
                    #Lets Grab a Cursor
                    cur = con.cursor()
            
                    #Lets Initialize some tables, so each run with be clear of previous run
                    cur.executescript('drop table if exists MyData;') #executescript requires ; at the end of the string
                    cur.execute("create table IF NOT EXISTS MyData('ID' INTEGER PRIMARY KEY   AUTOINCREMENT, 'Val' FLOAT)")
                    cur.executescript('drop table if exists Bins;')   #executescript requires ; at the end of the string
                    cur.execute("create table IF NOT EXISTS Bins('ID' INTEGER PRIMARY KEY   AUTOINCREMENT, 'Bin' UNSIGNED INTEGER, 'Val' FLOAT, 'Frequency' UNSIGNED BIG INT)")
            
                    #Lets generate some random data, and insert in to the Database
                    for n in range(0,(Population)):
                        sql = "insert into MyData(Val) values ({0})".format(random.uniform(-1,1))
                        #If Whole Number Integer greater that value of 2, Range Greater that 1.5
                        #sql = "insert into MyData(Val) values ({0})".format(random.randint(-1,1))
                        cur.execute(sql)
                        pass
            
                    #Now let’s calculate some built in Aggregates, that SQLite comes with
                    cur.execute("select Avg(Val) from MyData")
                    Average = cur.fetchone()[0]
                    cur.execute("select Max(Val) from MyData")
                    Max = cur.fetchone()[0]
                    cur.execute("select Min(Val) from MyData")
                    Min = cur.fetchone()[0]
                    cur.execute("select Count(Val) from MyData")
                    Records = cur.fetchone()[0]
            
                    #Now let’s get Standard Deviation using our function that we added
                    cur.execute("select Stdev(Val) from MyData")
                    Stdev = cur.fetchone()[0]
            
                    #And Calculate Range
                    Range = float(abs(float(Max)-float(Min)))
            
                    if (Stdev == None):
                        print("================================   Data Error ===============================")
                        print("                 Insufficient Population Size, Or Bad Data.")   
                        print("*****************************************************************************")
                    elif (abs(Max-Min) == 0):
                        print("================================   Data Error ===============================")
                        print(" The entire Population Contains Identical values, Distribution Incalculable.")
                        print("******************************************************************************")            
                    else:  
                        Bin = []        #Holds the Bin Values
                        Frequency = []  #Holds the Bin Frequency for each Bin
            
                        #Establish the 1st Bin, which is based on (Standard Deviation * 3) being subtracted from the Mean
                        Bin.append(float((Average - ((3 * Stdev)))))
                        Frequency.append(0)
            
                        #Establish the remaining Bins, which is basically adding 1 Standard Deviation
                        #for each interation, -3, -2, -1, 1, 2, 3             
                        for b in range(0,(BinCount) + 1):
                            Bin.append((float(Bin[(b)]) + Stdev))
                            Frequency.append(0)
            
                        for b in range(0,(BinCount) + 1):
                            #Lets exploit the Database and have it do the hard work calculating distribution
                            #of all the Bins, with SQL's between operator, but making it left inclusive, right exclusive.
                            sqlBinFreq = "select count(*) as Frequency from MyData where val between {0} and {1} and Val < {2}". \
                                         format(float((Bin[b])), float(Bin[(b + 1)]), float(Bin[(b + 1)]))
            
                            #If the Database Reports Values that fall between the Current Bin, Store the Frequency to a Bins Table. 
                            for rowBinFreq in cur.execute(sqlBinFreq):
                                Frequency[(b + 1)] = rowBinFreq['Frequency']
                                sqlBinFreqInsert = "insert into Bins (Bin, Val, Frequency) values ({0}, {1}, {2})". \
                                               format(b, float(Bin[b]), Frequency[(b)])
                                cur.execute(sqlBinFreqInsert)
            
                            #Allthough this Demo is not likley produce values that
                            #fall outside of Standard Distribution
                           #if this demo was to Calculate with real data, we want to know
                           #how many non-Standard data points we have. 
                           More = (More + Frequency[b])
            
                        More = abs((Records - More))
            
                        #Add the More value
                        sqlBinFreqInsert = "insert into Bins (Bin, Val, Frequency) values ({0}, {1}, {2})". \
                                        format((BinCount + 1), float(0), More)
                        cur.execute(sqlBinFreqInsert)
            
                        #Now Report the Analysis
                        print("================================ The Population ==============================")
                        print("             {0} {1} {2} {3} {4} {5}". \
                          format("Size".rjust(10, ' '), \
                                 "Max".rjust(10, ' '), \
                                 "Min".rjust(10, ' '), \
                                 "Mean".rjust(10, ' '), \
                                 "Range".rjust(10, ' '), \
                                 "Stdev".rjust(10, ' ')))
                        print("Aggregates:  {0:10d} {1:10.4f} {2:10.4f} {3:10.4f} {4:10.4f} {5:10.4f}". \
                          format(Population, Max, Min, Average, Range, Stdev))
                         print("================================= The Bell Curve =============================")  
            
                        LabelString = "{0} {1}  {2}  {3}". \
                                  format("Bin".ljust(8, ' '), \
                                         "Ranges".rjust(8, ' '), \
                                         "Frequency".rjust(8, ' '), \
                                         "Histogram".rjust(6, ' '))
            
                        print(LabelString)
                        print("------------------------------------------------------------------------------")
            
                        #Let's Paint a Histogram
                        sqlChart = "select * from Bins order by Bin asc"
                        for rowChart in cur.execute(sqlChart):
                            if (rowChart['Bin'] == 7):
                                #Bin 7 is not really a bin, but where we place the values that did not fit into the
                                #Normal Distribution. This script was tested against Excel's Bell Curve Example
                                #https://support.microsoft.com/en-us/kb/213930
                                #and produces the same results. Feel free to test it.
                                BinName = "More"
                                ChartString = "{0:<6} {1:<10} {2:10.0f}". \
                                        format(BinName, \
                                                "", \
                                                More)
                            else:
                                #Theses are the actual bins where values fall within the distribution.
                                BinName = (rowChart['Bin'] + 1)
                                #Scale the Chart
                                fPercent = ((float(rowChart['Frequency']) / float(Records) * 100))
                                iPrecent = int(math.ceil(fPercent))
            
                                ChartString = "{0:<6} {1:10.4f} {2:10.0f}  {3}". \
                                          format(BinName, \
                                                 rowChart['Val'], \
                                                 rowChart['Frequency'], \
                                                 "".rjust(iPrecent, '#'))
                            print(ChartString)
            
                        print("******************************************************************************")
            
                        #Commit to Database
                        con.commit()
            
                        #Clean Up
                        cur.close()
                        con.close()
            
                except Exception as EXBellCurve:
                    pass
                    TraceInfo = traceback.format_exc()       
                    raise Exception(TraceInfo)  
            

            【讨论】:

            • “SQLite 有内置的标准差函数吗?”的问题这如何回答这个问题?
            猜你喜欢
            • 2014-02-27
            • 2017-01-15
            • 1970-01-01
            • 2011-11-12
            • 2022-11-01
            • 1970-01-01
            • 2014-08-26
            • 2011-01-16
            • 1970-01-01
            相关资源
            最近更新 更多