{"id":"190e323bc6dfa547","slug":"were-kyiv-winters-as-snowy-as-i-remember-as-a-kid","trashed":false,"description":"","likes":49,"publish_level":"live","forks":9,"fork_of":null,"has_importers":false,"update_time":"2025-03-06T09:09:54.088Z","first_public_version":483,"paused_version":484,"publish_time":"2024-05-16T09:19:21.893Z","publish_version":484,"latest_version":484,"thumbnail":"8dafd4ffa027ebdae64515c0ff55ca0b4427d1f1e8784eadd49d9c4b13eccd3f","default_thumbnail":"8dafd4ffa027ebdae64515c0ff55ca0b4427d1f1e8784eadd49d9c4b13eccd3f","roles":[],"sharing":null,"owner":{"id":"78f29d059906819a","avatar_url":"https://avatars.observableusercontent.com/avatar/02cfd655debb6fc593dfe4bcacd9520f73e6b33cedef382248d08ecc40822a1b","login":"mourner","name":"Volodymyr Agafonkin","bio":"Engineer at Mapbox, creator of Leaflet, algorithms geek, open source enthusiast, musician, father of twin girls, Ukrainian. 🇺🇦","home_url":"https://agafonkin.com","type":"team","tier":"starter_2024"},"creator":{"id":"3954cddf6608132f","avatar_url":"https://avatars.observableusercontent.com/avatar/02cfd655debb6fc593dfe4bcacd9520f73e6b33cedef382248d08ecc40822a1b","login":"mourner","name":"Volodymyr Agafonkin","bio":"Engineer at Mapbox, creator of Leaflet, algorithms geek, open source enthusiast, musician, father of twin girls, Ukrainian. 🇺🇦","home_url":"https://agafonkin.com","tier":"pro"},"authors":[{"id":"3954cddf6608132f","avatar_url":"https://avatars.observableusercontent.com/avatar/02cfd655debb6fc593dfe4bcacd9520f73e6b33cedef382248d08ecc40822a1b","name":"Volodymyr Agafonkin","login":"mourner","bio":"Engineer at Mapbox, creator of Leaflet, algorithms geek, open source enthusiast, musician, father of twin girls, Ukrainian. 🇺🇦","home_url":"https://agafonkin.com","tier":"pro","approved":true,"description":""}],"collections":[{"id":"7f9c38896ce07772","type":"public","slug":"personal","title":"Personal","description":"","update_time":"2020-04-30T13:39:18.080Z","pinned":false,"ordered":false,"custom_thumbnail":null,"default_thumbnail":"0adf51ca7fec6a517f10fd007cf6aac62325e114a8e7da2594f09d66da29a7c0","thumbnail":"0adf51ca7fec6a517f10fd007cf6aac62325e114a8e7da2594f09d66da29a7c0","listing_count":8,"parent_collection_count":0,"owner":{"id":"78f29d059906819a","avatar_url":"https://avatars.observableusercontent.com/avatar/02cfd655debb6fc593dfe4bcacd9520f73e6b33cedef382248d08ecc40822a1b","login":"mourner","name":"Volodymyr Agafonkin","bio":"Engineer at Mapbox, creator of Leaflet, algorithms geek, open source enthusiast, musician, father of twin girls, Ukrainian. 🇺🇦","home_url":"https://agafonkin.com","type":"team","tier":"starter_2024"}},{"id":"ec2df8b99596790b","type":"public","slug":"data-visualizations","title":"Data Visualizations","description":"","update_time":"2020-01-15T20:55:13.704Z","pinned":false,"ordered":false,"custom_thumbnail":null,"default_thumbnail":"730057664927661ec4758d2802649b18f1360bdffc7eb0b03716c76339d7ecee","thumbnail":"730057664927661ec4758d2802649b18f1360bdffc7eb0b03716c76339d7ecee","listing_count":5,"parent_collection_count":0,"owner":{"id":"78f29d059906819a","avatar_url":"https://avatars.observableusercontent.com/avatar/02cfd655debb6fc593dfe4bcacd9520f73e6b33cedef382248d08ecc40822a1b","login":"mourner","name":"Volodymyr Agafonkin","bio":"Engineer at Mapbox, creator of Leaflet, algorithms geek, open source enthusiast, musician, father of twin girls, Ukrainian. 🇺🇦","home_url":"https://agafonkin.com","type":"team","tier":"starter_2024"}}],"files":[{"id":"d4b2d9175fe92479d5de45444777d5cc131667a5e752069030f96184a85242e93cbdcdaed505d85250ca0a7b77037b9bd6e18c513dc579705968f4cc068b342b","url":"https://static.observableusercontent.com/files/d4b2d9175fe92479d5de45444777d5cc131667a5e752069030f96184a85242e93cbdcdaed505d85250ca0a7b77037b9bd6e18c513dc579705968f4cc068b342b","download_url":"https://static.observableusercontent.com/files/d4b2d9175fe92479d5de45444777d5cc131667a5e752069030f96184a85242e93cbdcdaed505d85250ca0a7b77037b9bd6e18c513dc579705968f4cc068b342b?response-content-disposition=attachment%3Bfilename*%3DUTF-8%27%272130597.csv","name":"2130597.csv","create_time":"2020-04-29T11:25:30.342Z","mime_type":"text/csv","status":"public","size":437087,"content_encoding":"gzip","private_bucket_id":null},{"id":"981c6b4720603a724fc11f15c2bb63c5beddb32fbdf35b4e24e2a821cfdc9ca4050e2d8eb50022dfff891197177607bdde51de5c026938da1e152cc4c443c412","url":"https://static.observableusercontent.com/files/981c6b4720603a724fc11f15c2bb63c5beddb32fbdf35b4e24e2a821cfdc9ca4050e2d8eb50022dfff891197177607bdde51de5c026938da1e152cc4c443c412","download_url":"https://static.observableusercontent.com/files/981c6b4720603a724fc11f15c2bb63c5beddb32fbdf35b4e24e2a821cfdc9ca4050e2d8eb50022dfff891197177607bdde51de5c026938da1e152cc4c443c412?response-content-disposition=attachment%3Bfilename*%3DUTF-8%27%272513600.csv","name":"2513600.csv","create_time":"2021-03-19T16:36:48.758Z","mime_type":"text/csv","status":"public","size":447323,"content_encoding":"gzip","private_bucket_id":null},{"id":"64f7f1a846cbe43e7b6b9ae1ec551b0880876bb750fb8614932d80796303f6a7739b7d4c9e9eca7a6fe9e60a493ac81edbb4aa497a0aeba0fe124460bc185fc3","url":"https://static.observableusercontent.com/files/64f7f1a846cbe43e7b6b9ae1ec551b0880876bb750fb8614932d80796303f6a7739b7d4c9e9eca7a6fe9e60a493ac81edbb4aa497a0aeba0fe124460bc185fc3","download_url":"https://static.observableusercontent.com/files/64f7f1a846cbe43e7b6b9ae1ec551b0880876bb750fb8614932d80796303f6a7739b7d4c9e9eca7a6fe9e60a493ac81edbb4aa497a0aeba0fe124460bc185fc3?response-content-disposition=attachment%3Bfilename*%3DUTF-8%27%273691099.csv","name":"3691099.csv","create_time":"2024-05-16T09:18:28.660Z","mime_type":"text/csv","status":"public","size":477442,"content_encoding":"gzip","private_bucket_id":null},{"id":"7f763a304d2d402772be0706440d4398e43c6d285ccfe9d6f4a05d419a848358abe7c53d89cc20208d69e6524ca7cf1a94dba01ade07d21c4260ee13d55dd4eb","url":"https://static.observableusercontent.com/files/7f763a304d2d402772be0706440d4398e43c6d285ccfe9d6f4a05d419a848358abe7c53d89cc20208d69e6524ca7cf1a94dba01ade07d21c4260ee13d55dd4eb","download_url":"https://static.observableusercontent.com/files/7f763a304d2d402772be0706440d4398e43c6d285ccfe9d6f4a05d419a848358abe7c53d89cc20208d69e6524ca7cf1a94dba01ade07d21c4260ee13d55dd4eb?response-content-disposition=attachment%3Bfilename*%3DUTF-8%27%273952336.csv","name":"3952336.csv","create_time":"2025-03-06T09:09:29.767Z","mime_type":"text/csv","status":"public","size":486530,"content_encoding":"gzip","private_bucket_id":null}],"comments":[],"commenting_lock":null,"suggestion_from":null,"suggestions_to":[],"version":484,"title":"Were Kyiv Winters as Snowy as I Remember as a Kid?","license":null,"copyright":"","nodes":[{"id":0,"value":"md`# Were Kyiv Winters as Snowy as I Remember as a Kid?\n\nI have fond memories of snowy winters growing up in Kyiv — the joy of sliding from a steep hill on a sled (or a door of a scrap fridge), building snow castles, snowball fights, you name it! A good snowfall was my most anticipated event of the year.\n\nOne of my early memories is wishing for a snowfall on my birthday, then crying because it still doesn't snow, and finally waking up a few days later to a blindingly white window view.\n\nWere winters as snowy as I remember them? It feels like winters are much less snowy than they were two decades ago — is this really true? I decided to find out.\n\nI got the snow depth data in CSV format through [US NOAA's Climate Data Online service](https://www.ncdc.noaa.gov/cdo-web/), and visualized it on the chart below. Every row here covers a range from November to beginning of April every year since 1978 (the year when [Kyiv's central weather station](https://www.ncdc.noaa.gov/cdo-web/datasets/GHCND/stations/GHCND:UPM00033345/detail) started tracking it consistently).\n`","pinned":false,"mode":"js","data":null,"name":null},{"id":194,"value":"{\n  const legend = html`<div style=\"font: 14px sans-serif\"></div>`;\n  for (let i = 0; i <= maxSnow; i += 50) {\n    legend.appendChild(html`<span style=\"\n      display: inline-block;\n      text-align: center;\n      width: 35px;\n      border-top: 8px solid ${dayColor(i)}; \n      padding-left: 0px;\n    \">${i / 10}${i === 600 ? '+' : ''}</span>`)\n  }\n  legend.appendChild(html`<span style=\"color: #777; vertical-align: top; margin-left: 10px\">(snow depth in cm)</span>`)\n  return legend;\n}","pinned":false,"mode":"js","data":null,"name":null},{"id":78,"value":"{\n  const numDays = 156;\n  const margin = 25;\n  const marginTop = 12;\n  const cellHeight = 16;\n  const cellWidth = (width - 2 * margin) / numDays;\n  const height = cellHeight * Object.keys(winters).length + marginTop;\n  const ctx = DOM.context2d(width, height);\n  let y = marginTop;\n  for (const year in winters) {\n    const days = winters[year];\n    const leap = isLeap(+year + 1);\n    for (let i = 0; i < numDays; i++) {\n      ctx.fillStyle = dayColor(days[i]);\n      ctx.fillRect(margin + Math.floor(i * cellWidth), y, cellWidth + 1, cellHeight);\n    }\n    ctx.strokeStyle = '#888';\n    ctx.beginPath();\n    const marchStart = leap ? 121 : 120;\n    const marchX = margin + Math.floor(marchStart * cellWidth);\n    ctx.moveTo(marchX, y);\n    ctx.lineTo(marchX, y + cellHeight);\n    const aprilX = margin + Math.floor((marchStart + 31) * cellWidth);\n    ctx.moveTo(aprilX, y);\n    ctx.lineTo(aprilX, y + cellHeight);\n    ctx.stroke();\n\n    ctx.fillStyle = 'black';\n    ctx.textBaseline = 'middle';\n    ctx.textAlign = 'left';\n    ctx.fillText(year, 0, y + cellHeight / 2);\n    ctx.textAlign = 'right';\n    ctx.fillText(+year + 1, width, y + cellHeight / 2);\n    y += cellHeight; \n  }\n\n  ctx.textAlign = 'left';\n  ctx.textBaseline = 'top';\n  ctx.fillText('November', margin, 0);\n  ctx.fillText('December', margin + Math.floor(30 * cellWidth), 0);\n  ctx.fillText('January', margin + Math.floor(61 * cellWidth), 0);\n  ctx.fillText('February', margin + Math.floor(92 * cellWidth), 0);\n  ctx.fillText('March', margin + Math.floor(120 * cellWidth), 0);\n  ctx.fillText('April', margin + Math.floor(151 * cellWidth), 0);\n  \n  ctx.strokeStyle = 'white';\n  ctx.lineWidth = 2;\n  ctx.beginPath();\n  ctx.moveTo(margin + Math.floor(61 * cellWidth), marginTop);\n  ctx.lineTo(margin + Math.floor(61 * cellWidth), height);\n  ctx.stroke();\n  ctx.strokeStyle = '#888';\n  ctx.lineWidth = 1;\n  ctx.beginPath();\n  ctx.moveTo(margin + Math.floor(30 * cellWidth), marginTop);\n  ctx.lineTo(margin + Math.floor(30 * cellWidth), height);\n  ctx.moveTo(margin + Math.floor(92 * cellWidth), marginTop);\n  ctx.lineTo(margin + Math.floor(92 * cellWidth), height);\n  ctx.stroke();\n  return ctx.canvas;\n}","pinned":false,"mode":"js","data":null,"name":null},{"id":332,"value":"md`## Observations\n- 1986–1987, the winter that came just after I was born (and after the Chernobyl disaster), was the snowiest Kyiv winter of the last four decades — no winter ever came close since.\n- Given that I was born on November 20th of 1986, my \"birthday wish snow\" memory above likely happened in 1992, when I was six years old.\n- The winter of 1995–1996, when I was nine years old, was likely my happiest winter — the most snowy winter of my childhood.\n- Something weird happened at that weather station in 2007–2009, when financial crisis hit — there are lots of gaps in the coverage (especially around holidays). Did workers have to take breaks because they weren't properly paid?\n- Generally there are many gaps when wheather data was recorded but without snow, even though it was definitely present given data before and after — not sure why. Can I fill these gaps based on some heuristic?\n- The winter of 2012–2013 was the best Kyiv winter in my lifetime — consistent snow for 4 months, including two extreme snowfalls!\n- You can clearly see the curious anomalous snowfall of March 22 2013, which caused complete transportation collapse to the point that I wasn't being able to come home and had to stay at a friend's apartment for the night.\n- The winter of 2013–2014 was one of the warmest winters in three decades, which helped bring more people out on the streets during the [Euromaidan protests](https://en.wikipedia.org/wiki/Euromaidan) and the subsequent [revolution](https://en.wikipedia.org/wiki/2014_Ukrainian_revolution).\n- The winter of 2019–2020 is the warmest and most snowless Kyiv winter on record.\n\nAnd most importantly, are Kyiv winters getting less snowy? According to the chart, most definitely not — on the contrary, they're generally _getting snowier_. Which, counter-intuitively, is likely [caused by global warming](https://www.iflscience.com/environment/does-global-warming-mean-more-or-less-snow/) — long story short, warmer winters can hold more moisture in the air, which increases snowfalls as long as temperature stays below freezing. Kyiv didn't yet reach the point where temperature rarely goes below freezing, but we're heading there.\n\nAs for the false feeling of less snowy winters, it probably comes down to me spending way less time outside to enjoy the snow.\n\nP.S. Happy to get comments and suggestions [on Twitter](https://twitter.com/mourner/status/1217469380336017411). Also, feel free to fork this notebook, changing it to be about _your_ hometown! \n\n**2021 update**: added data for the winter of 2020–2021, which thankfully turned out more snowy than the last one.\n`","pinned":false,"mode":"js","data":null,"name":null},{"id":381,"value":"md`## Appendix`","pinned":false,"mode":"js","data":null,"name":null},{"id":5,"value":"winters = {\n  const lines = (await FileAttachment(\"3952336.csv\").text()).trim().split('\\n').slice(1);\n  const re = /\\\".+\\\",\\\"(\\d+)-(\\d+)-(\\d+)\\\",\\\"?(\\d+)?\\.?\\\"?/;\n  const winters = {};\n  for (const line of lines) {\n    const [_, year, month, day, snow] = line.match(re);\n    if (month < 11 && month > 4) continue;\n    const winterYear = month <= 4 ? year - 1 : year;\n    winters[winterYear] = winters[winterYear] || [];\n    winters[winterYear][winterIndex(+year, +month, +day)] = +snow || 0;\n  }\n  return winters;\n}","pinned":false,"mode":"js","data":null,"name":null},{"id":435,"value":"dayColor = d => \n  d === undefined ? 'grey' : \n  d === 0 ? 'black' : \n  interpolateTurbo(0.02 + 0.94 * Math.min(1, d / maxSnow))","pinned":false,"mode":"js","data":null,"name":null},{"id":230,"value":"maxSnow = 600","pinned":false,"mode":"js","data":null,"name":null},{"id":38,"value":"winterIndex = (year, month, day) => {\n  const N1 = Math.floor(275 * month / 9);\n  const N2 = Math.floor((month + 9) / 12);\n  const N3 = (1 + Math.floor((year - 4 * Math.floor(year / 4) + 2) / 3));\n  return N1 - (N2 * N3) + day + 30 - (month > 6 ? (isLeap(year) ? 366 : 365) : 0);\n}","pinned":false,"mode":"js","data":null,"name":null},{"id":27,"value":"isLeap = year => year % 4 === 0 && year % 100 !== 0 || year % 400 === 0","pinned":false,"mode":"js","data":null,"name":null},{"id":90,"value":"import {interpolateTurbo} from '@mbostock/turbo'","pinned":false,"mode":"js","data":null,"name":null}],"resolutions":[],"schedule":null,"last_view_time":null}