Potassium sleep experiments

2 self-experiments on potassium citrate effects on sleep: harm to sleep when taken daily or in the morning
experiments, biology, statistics, Zeo, R, Bayes
2012-12-212016-11-15 finished certainty: highly likely importance: 4


Potas­sium and mag­ne­sium are min­er­als that many Amer­i­cans are defi­cient in. I tried using potas­sium cit­rate and imme­di­ately noticed diffi­culty sleep­ing. A short ran­dom­ized (but not blind­ed) self­-ex­per­i­ment of ~4g potas­sium taken through­out the day con­firmed large neg­a­tive effects on my sleep. A longer fol­lowup ran­dom­ized and blinded self­-ex­per­i­ment used stan­dard­ized doses taken once a day early in the morn­ing, and also found some harm to sleep, and I dis­con­tin­ued potas­sium use entire­ly.

In Octo­ber 2012, I bought some potas­sium cit­rate on a lark after not­ing that the daily RDA and my diet sug­gested that I was mas­sively defi­cient. (I chose potas­sium cit­rate rather than potas­sium chlo­ride because they are priced sim­i­larly and no infor­ma­tion sug­gested they differed mean­ing­ful­ly, but I don’t really like the idea of chlo­rine, so I went with cit­rate.) The first night I slept ter­ri­bly, tak­ing what felt like hours to fall asleep and then wak­ing up fre­quent­ly—­due to either the potas­sium or a fan left on; the sec­ond night with potas­si­um, I turned off the fan but slept poorly again. My sus­pi­cions were aroused. I began record­ing sleep data.

Potassium day use

Background

Part­way through the process, I searched Google Scholar and Pubmed (hu­man tri­als) for “potas­sium sleep”; I checked the first 70 results of both. A gen­eral Google search turned up mostly spec­u­la­tion on the rela­tion­ship of potas­sium defi­ciency and sleep. The only use­ful cita­tion was “Potas­sium affects acti­graph-i­den­ti­fied sleep”, Dren­nan et al 1991; likely aren’t as good as a Zeo, and n = 6, but the study is directly rel­e­vant. Only 2 acti­graph results reached sta­tis­ti­cal sig­nifi­cance: a small improve­ment in sleep effi­ciency (the per­cent­age of time spent lay­ing in bed and actu­ally sleep­ing) and a big­ger ben­e­fit in “WASO” (time awake dur­ing sleep time; this prob­a­bly drove the sleep effi­cien­cy).

Data

The first night (10/12) involved falling asleep in 30 min­utes rather than my usual 19.6±11.9, wak­ing up 12 times (5.9±3.4), and spend­ing ~90 min­utes awake (18.1±16.2) The next day (10/13) I took a sim­i­lar dose and dou­ble-checked the fan before bed: 25 min­utes to fall asleep, 10 awak­en­ings, 35 min­utes awake, but I woke fairly rest­ed. So it seems like the fan was only partly to blame. The third day (10/14) I omit­ted any potas­si­um: 21/8/29. Fourth (10/15) on again with an evening dose: 54/7/24. Fifth (10/16), off: 16/2/6. Sixth (10/17), on with a halved dose: 33/3/6. Sev­enth (10/18), off: 17/6/7. Eighth (10/20), half: 33/6/15.

At this point I began ran­dom­iz­ing con­sump­tion between on and off; since this is pre­lim­i­nary, I did­n’t bother with blind­ing potas­sium con­sump­tion. Ninth (10/21), on: 25/7/9. Tenth (10/22), on: 18/8/10. 11th (10/23), off: 26/4/10. 12th (10/24), off: 33/7/16. 13th (10/25), on: 32/7/13. 14th (10/26), on: 21/5/8. 15th, on: 34/2/1. 16th, off: 16/7/15. 17th, on: 29/8/20. 18th, on: 17/10/17. 19th, off: 36/9/24. 20th (11/1), on: 21/4/19. 21st (11/2), off: 29/7/16. 22nd (11/3), on: 26/7/10. 23rd (11/4), on: 16/4/11. 24th (11/5), off: 21/4/17. 25th (11/6), on: 19/9/24.

11 Nov, on: 15/3/08. 13 Nov, off: 11/8/21. 14 Nov, off: 18/8/22. 15 Nov, on: 30/8/16. 16 Nov, off: 20/7/12. 17 Nov, on: 34/8/20. 18 Nov, on: 12/8/22. 19 Nov, off: 24/8/14. 20 Nov, on: 26/4/39. 21 Nov, off: 15/6/14. 22 Nov, on: 26/8/29. 23 Nov, on: 23/4/8. 24 Nov, off: 24/3/5. 25 Nov, on: 27/7/15. 26 Nov, on: 30/10/17. 27 Nov, off: 42/12/13. 28 Nov, off: 40/11/42. 29 Nov, off: 19/14/50. 30 Nov, off: 32/8/39. (Here I counted the sam­ple-sizes and real­ized the off days were dras­ti­cally under­-rep­re­sent­ed, reduc­ing sta­tis­ti­cal pow­er; so I have elim­i­nated ran­dom­iza­tion and gone off potas­si­um.) 1 Dec, off: 28/10/15. 2 Dec, off: 37/8/20. 3 Dec, off: 36/6/18. 4 Dec, off: 19/9/33. 5 Dec, off: 25/8/27. 6 Dec, off: 30/13/45. (Now bal­anced, resum­ing ran­dom­iza­tion.) 7 Dec, on: 31/9/60. 8 Dec, off: 22/9/23. 9 Dec, off: 11/5/21. 10 Dec, on: 30/4/10. 11 Dec, on: 22/9/50. 13 Dec, off: 20/5/6. 14 Dec, off: 33/13/25. 15 Dec, on: 26/11/22. 16 Dec, off: 33/12/28. 17 Dec, off: 42/9/31. 18 Dec, off: 31/9/61. 19 Dec, on: 23/8/18.

Analysis

Sleep disturbances

If potas­sium was dis­turb­ing my sleep, I did­n’t nec­es­sar­ily want to wait for any one met­ric of wake­ful­ness to reach sig­nifi­cance; rather, I wanted to com­bine them into a sin­gle met­ric of sleep prob­lems: time to fall asleep (la­ten­cy), num­ber of awak­en­ings, and time spent awake. (With all 3, higher is worse.) Num­ber of awak­en­ings tends to vary over a smaller range than time to fall asleep or time spent awake—a nor­mal value for the for­mer might be 5, rather than 30 for the lat­ter; to com­pen­sate for that, we con­vert each met­ric into a stan­dard devi­a­tion indi­cat­ing how unusual eg. 10 awak­en­ings is and whether it is more unusual than it tak­ing 15 min­utes to fall asleep. Then we can do a stan­dard test. To graph the data at each step, start­ing with graph­ing all the data on an over­lap­ping chart1 (this is not per day):

Plot­ting raw data of on and off-potas­sium nights, in a loosely chrono­log­i­cal order

Nights off potas­sium are col­ored blue and nights on potas­sium are red; it looks like red dots are higher than blues, over­all, but the trend is not clear. So we con­vert each indi­vid­ual dat­a­point to its respec­tive stan­dard devi­a­tion2:

Plot­ting data of on and off-potas­sium nights, stan­dard­ized into stan­dard devi­a­tions

The trend has become much clear­er, but the final step is to add each day’s scores to get an over­all mea­sure3:

Final score per night

Now the differ­ent has become dra­mat­ic: one can almost draw a line sep­a­rat­ing both groups with­out any errors. As one would expect given this graph­i­cal evi­dence, a Bayesian two-group test reports that there is ~0 chance that the true effect size is 0, and the most likely effect size is a dis­may­ing d = −1.14:

Com­par­i­son of sleep dis­tur­bances in nights on and off potas­sium cit­rate

A two-sam­ple test agrees:5 p = 0.0002168. (There is no need for mul­ti­ple cor­rec­tion in this instance.) This con­firms my sub­jec­tive impres­sion.

Mood/productivity

A sec­ondary ques­tion is whether potas­sium deliv­ered any wak­ing ben­e­fits. I write down at the end of each day my rat­ing 2–4 how happy and/or pro­duc­tive I felt that day. Does this self­-rat­ing show any effect? Here’s a plot of each day col­ored by whether it was a potas­sium day:

library(ggplot2); qplot(data=pot, y=MP, color=Potassium)

There is lit­tle vis­i­ble effect, and the for­mal Bayesian6 analy­sis is as weak as the sleep dis­tur­bances are strong:

postInfo = BESTplot(off, on, mcmcChain)

So there is no appar­ent ben­e­fit from the potas­si­um.

Conclusion

This exper­i­ment was hastily done and has sev­eral weak­ness­es, some I men­tioned before; in ascend­ing order of impor­tance:

  1. Non-U­ni­form Dosage:

    Num­ber of dosages var­ied from day to day as was con­ve­nient and doses were mea­sured approx­i­mately with a spoon (since 4 grams is a pretty sub­stan­tial amount, after all). Here is another objec­tion I don’t think mat­ters: lower than aver­age doses may con­tribute to an under­es­ti­mate of the effect size… but that implies that the effect size is even more extreme than −1.1! We are inter­ested in prob­lems that would shrink the effect size back to 0, not imply that it’s even worse than −1.1.

  2. Incom­plete Ran­dom­iza­tion:

    As cov­ered in the data sec­tion, there was a severe imbal­ance in sam­ple size for each con­di­tion, so I stopped ran­dom­iza­tion for about a week. Intu­itive­ly, I don’t think there was any­thing spe­cial about that week in regard to get­ting very good sleep (as would be nec­es­sary to con­tribute to an over­es­ti­mated effect size), but if any­one dis­agreed, it would not be hard to exclude those days and use the rest.

  3. No Blind­ing:

    I am not sure how much this mat­ters. I had no expec­ta­tion that potas­sium would affect my sleep at all, one user specifi­cally denied any effect, the only study sug­gested I’d find improve­ments, I did not want to find a neg­a­tive effect much less such a severe effect, and the sheer strength of the effect over a mul­ti­-month period is a bit more than I would expect from any expectancy or placebo effect.

  4. Non-U­ni­form Tim­ing:

    Of the issues, this is the most impor­tant. If potas­sium has some stim­u­lat­ing effects as anec­dotes claim, then tim­ing may be caus­ing all the sleep dis­tur­bances and not potas­sium per se. It might be exactly like vit­a­min D in this respect: taken in the evening, it badly dam­ages sleep but taken in the morn­ing, it does noth­ing or it improves sleep.

If I were to do a fol­lowup exper­i­ment, it would be blinded & ran­dom­ized as usu­al, with con­sis­tent doses (elim­i­nat­ing objec­tions 1–3), but more impor­tant­ly, the dose would be con­sumed upon awak­en­ing.

I am not sure I will bother with a fol­lowup exper­i­ment. Potas­sium is not of par­tic­u­lar inter­est to me, my exist­ing sup­ply is low after months of con­sump­tion, I observed no sub­jec­tive improve­ments on con­sump­tion, and so I am not inclined to run the risk of dam­ag­ing more months of sleep. Other peo­ple can do that.

Potassium morning use

As it hap­pened, I man­aged to retrieve my pil­l-mak­ing machine and spare gel cap­sules, and I do hate to waste per­fectly good potas­sium cit­rate pow­der, so I decided to do a morn­ing exper­i­ment. I made 3×24 potas­sium pills and 3×24 brown rice pills (out of flour); I take one set of 3 pills each morn­ing, ran­domly pick­ing. This pro­ce­dure addresses all 4 issues, and will answer the ques­tion about whether potas­si­um’s sleep dis­tur­bance is due to a tim­ing issue like that of caffeine and vit­a­min D. Analy­sis will be the same as before: 3 met­rics of sleep dis­tur­bance, and then daily self­-rat­ing. (I did­n’t devise a paired-blocks setup since my marked con­tain­ers were in use else­where; as often hap­pens I ran out of one set of pills first, the rice placebo pills, on 2013-02-10, and made another batch of 24 rice placebo pills. The last potas­sium pill was 2013-02-21.)

Analysis

Sub­jec­tive­ly, I noticed noth­ing on what turned out to be the potas­sium days, unlike in the first exper­i­ment.

Sleep disturbances

Run­ning the analy­sis the same way as before, we get a small increase in sleep dis­tur­bances (d = 0.15, higher is worse) but the effect could eas­ily be noth­ing7:

Potas­sium morn­ing exper­i­ment, sleep prob­lems

I sus­pect there really is an under­ly­ing causal effect: the first exper­i­ment indi­cated a large increase in sleep dis­tur­bances, and a much smaller one is in line with my expec­ta­tions of the effect of a smaller stan­dard­ized dose first thing upon wak­ing.

But prac­ti­cally speak­ing, this small dis­tur­bance would be accept­able if it came with some ben­e­fit.

Mood/productivity

The results look almost iden­ti­cal to before8:

Potas­sium morn­ing exper­i­ment, MP effect

Conclusion

A much high­er-qual­ity exper­i­ment with more favor­able con­di­tions for potas­sium showed a result con­sis­tent with some harm to my sleep, and no ben­e­fit. Since potas­sium showed harm in both cir­cum­stances with no sug­ges­tion of ben­e­fit, I will not con­tinue using potas­si­um.


  1. The gen­er­at­ing R code (see later analy­sis foot­note for defi­n­i­tions of data vari­ables like offtimeawake etc):

    plot(c(1:32), offtimeawake, col="blue",
         xlab="nth", ylab="latency/awakenings/awake (raw)")
    points(c(1:32), offlatency, col="blue")
    points(c(1:32), offawakenings, col="blue")
    points(c(1:30), ontimeawake, col="red")
    points(c(1:30), onlatency, col="red")
    points(c(1:30), onawakenings, col="red")
    ↩︎
  2. After run­ning zscore on each data vari­able, we repeat the pre­vi­ous code but with ylab="latency/awakenings/awake (standardized)" in the call to plot.↩︎

  3. Assum­ing the zscore con­ver­sion has been done:

    plot(c(1:32), offtimeawake+offlatency+offawakenings, col="blue",
         xlab="nth", ylab="standardized sleep disturbance score")
    points(c(1:30), ontimeawake+onlatency+onawakenings, col="red")
    ↩︎
  4. The pre­vi­ously described com­pos­ite mea­sure and BEST test:

    # for interventions with dates, see https://www.gwern.net/docs/zeo/2012-2013-potassium.csv
    # all the non-potassium days
    offlatency <- c(11,15,16,16,17,18,20,21,21,24,24,26,29,33,36,42,40,19,32,28,37,36,19,25,
                    30,22,11,20,33,33,42,31)
    offawakenings <- c(8,6,2,7,6,8,7,4,8,3,8,4,7,7,9,12,11,14,8,10,8,6,9,8,13,9,5,5,13,12,9,9)
    offtimeawake <- c(21,14,6,15,7,22,12,17,29,5,14,10,16,16,24,13,42,50,39,15,20,18,33,27,45,
                      23,21,6,25,28,31,61)
    
    # all the potassium days
    onlatency <- c(12,15,16,17,18,19,21,21,23,25,25,26,26,26,27,29,30,30,32,33,33,34,34,
                   54,30,31,30,22,26,23)
    onawakenings <- c(8,3,4,10,8,9,4,5,4,10,7,4,7,8,7,8,12,8,7,3,6,2,8,7,10,9,4,9,11,8)
    ontimeawake <- c(22,08,11,17,10,24,19,8,8,35,9,39,10,29,15,20,90,16,13,6,15,1,20,24,
                     17,60,10,50,22,18)
    
    # normalize
    zscore <- function(x,y) mapply(function(a) (a - mean(y))/sd(y), x)
    offlatency <- zscore(offlatency, c(offlatency, onlatency))
    onlatency  <- zscore(onlatency,  c(offlatency, onlatency))
    offawakenings <- zscore(offawakenings, c(offawakenings, onawakenings))
    onawakenings  <- zscore(onawakenings,  c(offawakenings, onawakenings))
    offtimeawake <- zscore(offtimeawake, c(offtimeawake, ontimeawake))
    ontimeawake <- zscore(ontimeawake, c(offtimeawake, ontimeawake))
    
    # zip together with sum to get a single measure of how deviate a night was
    off <- offlatency + offawakenings + offtimeawake
    on <- onlatency + onawakenings + ontimeawake
    
    # usual Bayesian two-group test
    source("BEST.R")
    mcmcChain = BESTmcmc(off, on)
    postInfo = BESTplot(off, on, mcmcChain) # graph
    postInfo
    #          SUMMARY.INFO
    # PARAMETER      mean  median    mode  HDIlow HDIhigh pcgtZero
    #   mu1        0.1664  0.1655  0.1421 -0.71894  1.0555       NA
    #   mu2        2.4256  2.4210  2.4035  1.81175  3.0478       NA
    #   muDiff    -2.2592 -2.2592 -2.2318 -3.34666 -1.1853    0.006
    #   sigma1     2.3939  2.3607  2.2695  1.78291  3.0915       NA
    #   sigma2     1.6189  1.5988  1.5786  1.11009  2.1614       NA
    #   sigmaDiff  0.7750  0.7606  0.7341 -0.03236  1.6317   97.205
    #   nu        32.0045 23.2730  9.6599  2.33645 88.0997       NA
    #   nuLog10    1.3607  1.3669  1.4214  0.67234  2.0337       NA
    #   effSz     -1.1141 -1.1107 -1.0959 -1.69481 -0.5433    0.006
    ↩︎
  5. Reusing the stan­dard­ized data from before:

    wilcox.test(off, on)
    #     Wilcoxon rank sum test
    #
    # data:  off and on
    # W = 224, p-value = 0.0002168
    ↩︎
  6. As before, we use BEST (the self­-rat­ing is mostly nor­mal):

    Potassium <- c(1,1,0,1,0,1,0,0,1,1,1,0,0,1,1,1,0,1,1,0,1,0,1,1,0,1,0,0,0,0,1,0,0,0,1,0,1,1,
                   0,1,0,1,1,0,1,1,0,0,0,0,0,0,0,0,0,0,1,0,0,1,1,0,0,0,1,0,0,0,1)
    MP <- c(4,4,3,4,4,3,3,2,3,3,3,3,4,4,3,4,2,2,2,3,4,3,4,3,4,3,4,4,3,3,2,3,2,4,4,3,4,2,3,4,2,
            3,3,2,2,2,3,2,3,3,4,2,3,4,3,4,3,3,2,2,3,4,4,3,4,2,2,3,2)
    pot <- data.frame(Potassium, MP)
    
    # first graph:
    library(ggplot2)
    qplot(data=pot, y=MP, color=Potassium)
    
    # analysis:
    source("BEST.R")
    off <- pot$MP[pot$Potassium == 0]
    on <- pot$MP[pot$Potassium == 1]
    mcmcChain = BESTmcmc(off, on)
    postInfo = BESTplot(off, on, mcmcChain) # graph
    postInfo
    #            SUMMARY.INFO
    # PARAMETER       mean   median     mode  HDIlow  HDIhigh pcgtZero
    #   mu1        3.02651  3.02686  3.03576  2.7780   3.2677       NA
    #   mu2        3.10432  3.10390  3.07921  2.7939   3.4127       NA
    #   muDiff    -0.07782 -0.07736 -0.07786 -0.4728   0.3119    34.96
    #   sigma1     0.75685  0.74855  0.73261  0.5834   0.9427       NA
    #   sigma2     0.83168  0.81845  0.79169  0.6133   1.0677       NA
    #   sigmaDiff -0.07483 -0.07033 -0.05617 -0.3755   0.2195    31.15
    #   nu        47.52944 39.43237 23.78338  4.6350 111.4156       NA
    #   nuLog10    1.58217  1.59585  1.63348  0.9931   2.1316       NA
    #   effSz     -0.09844 -0.09761 -0.10476 -0.5879   0.3897    34.96
    
    wilcox.test(off, on)
    #     Wilcoxon rank sum test with continuity correction
    #
    # data:  off and on
    # W = 552.5, p-value = 0.6789
    ↩︎
  7. See pre­vi­ously for expla­na­tion:

    pot <- read.csv("https://www.gwern.net/docs/zeo/2013-gwern-potassium-morning.csv")
    
    # standardize & combine into a single equally-weighted synthetic index z-score
    pot$Disturbance <- scale(pot$Time.to.Z) + scale(pot$Awakenings) + scale(pot$Time.in.Wake)
    
    on  <- pot[pot$Potassium==1,]$Disturbance
    off <- pot[pot$Potassium==0,]$Disturbance
    
    source("BEST.R")
    mcmcChain = BESTmcmc(off, on)
    postInfo = BESTplot(off, on, mcmcChain) # graph
    postInfo
    #            SUMMARY.INFO
    # PARAMETER      mean   median     mode  HDIlow HDIhigh pcgtZero
    #   mu1        0.1329  0.13224  0.11468 -0.6505  0.9203       NA
    #   mu2       -0.2626 -0.26479 -0.22430 -1.1154  0.5966       NA
    #   muDiff     0.3956  0.39838  0.37996 -0.7724  1.5327    75.39
    #   sigma1     1.9961  1.96663  1.89699  1.3978  2.6302       NA
    #   sigma2     1.9403  1.90682  1.86314  1.2797  2.6697       NA
    #   sigmaDiff  0.0558  0.06166  0.04212 -0.8615  0.9499    55.85
    #   nu        33.0593 24.28680  9.49415  1.7036 90.8230       NA
    #   nuLog10    1.3674  1.38537  1.47058  0.6392  2.0655       NA
    #   effSz      0.2054  0.20334  0.18368 -0.3619  0.8119    75.39
    ↩︎
  8. on/off defined and BEST loaded in pre­vi­ous analy­sis:

    mcmcChain = BESTmcmc(off$MP, on$MP)
    postInfo = BESTplot(off$MP, on$MP, mcmcChain) # graph
    postInfo
    #                SUMMARY.INFO
    # PARAMETER        mean   median     mode  HDIlow  HDIhigh pcgtZero
    #   mu1        2.999866  2.99993  2.99749  2.7134   3.2884       NA
    #   mu2        2.955535  2.95571  2.95990  2.6391   3.2689       NA
    #   muDiff     0.044331  0.04465  0.05384 -0.3831   0.4669    58.29
    #   sigma1     0.739736  0.72787  0.71017  0.5371   0.9685       NA
    #   sigma2     0.731523  0.71670  0.68979  0.5081   0.9827       NA
    #   sigmaDiff  0.008212  0.01087  0.01340 -0.3210   0.3419    52.76
    #   nu        41.545632 33.20153 18.29201  2.5717 103.6089       NA
    #   nuLog10    1.502165  1.52116  1.55933  0.8486   2.1209       NA
    #   effSz      0.060755  0.06100  0.07764 -0.5064   0.6339    58.29
    ↩︎