title: Smoothing simulation data for a Chi-Square demonstration
body: <p>I am trying to teach my students about Chi-Square while trapped here at home. I have made a video that should be mostly helpful, however I have been having trouble making a graph with the specific properties of the Chi-Square distribution. The shape is right, however there is a lot of noise. This is simulation data, so it will never be perfectly smooth, however this is a bit much.</p>
  
  <p>I have been trying to smooth the data. I have gone as far as to round the data to the nearest tenth and perform a moving average (k = 3) in order to get a graph as presentable as this:</p>
  
  <p><a href="https://i.stack.imgur.com/TBr1I.png" rel="nofollow noreferrer">Chi-Squared Simulation df = 3, sample size = 100, samples = 100000, rounded and smoothed</a></p>
  
  <p><a href="https://i.stack.imgur.com/AYIRa.png" rel="nofollow noreferrer">Chi-Squared Simulation df = 3, sample size = 100, samples = 100000, not rounded, smoothed</a></p>
  
  <p>A few things I have noticed while working on this problem. First, the spikes and dips seem to occur at predictable locations. Second, without the rounding, the graph seems to alternate back and forth between a spike and dip regularly. I think it may be possible that this is be due to some sort of binary precision problem. I have tried to account for this by switching to using numpy for my operations and forcing the data to be float64. This had no effect.</p>
  
  <p>What I would like to know is either:</p>
  
  <ol>
  <li>If this problem is caused by binary precision, how can I properly mitigate that?</li>
  <li>If this cannot be solved in that way, is there a better smoothing operation I could use?</li>
  </ol>
  
  <p>Thank you for the assistance. Code is below.</p>
  
  <pre><code># Draw n samples of 25 and get Chi-Square list
  chiSqrList = []
  
  n = 100000
  sampleSize = 100
  
  j = 0
  
  while j &lt; n:
  
  
  redTotal = 0
  greenTotal = 0
  yellowTotal = 0
  blueTotal = 0
  
  i = 0
  
  while i &lt; sampleSize:
      x = random.random()
      if x &lt; redLim:
          redTotal += 1
      elif x &lt; greenLim:
          greenTotal += 1
      elif x &lt; yellowLim:
          yellowTotal +=1
      else:
          blueTotal += 1
  
      i += 1
  
  observedBalls = np.array([redTotal, greenTotal, yellowTotal, blueTotal], dtype=np.float64)
  expectedBalls = np.array([sampleSize*redBalls, sampleSize*greenBalls, sampleSize*yellowBalls, sampleSize*blueBalls], dtype=np.float64)
  
  
  chiSqr = 0
  
  
  chiSqr = np.power((observedBalls - expectedBalls), 2)/expectedBalls
  chiSqr = np.sum(chiSqr)
  
  chiSqr = round(chiSqr, 1)
  
  chiSqrList.append(chiSqr)
  
  j += 1
  
  # Make count data
  
  avgSqrDist = []
  count = []
  
  i = 0
  
  for value in chiSqrList:
      if len(avgSqrDist) == 0:
          avgSqrDist.append(value)
          count.append(1)
      elif avgSqrDist[i] != value:
          avgSqrDist.append(value)
          count.append(1)
          i += 1
      else:
          count[i] += 1
  
  # Smooth curve
  
  i = 0
  smoothAvgSqrDist = []
  smoothCount = []
  
  while i &lt; len(avgSqrDist)-2:
      smoothCount.append((count[i]+count[i+1]+count[i+2])/3)
      smoothAvgSqrDist.append(avgSqrDist[i+1])
      i += 1
  </code></pre>