everybody thanks for coming I'm gonna be talking about the the thing that is kernel CI upstream kernel CI or something that is in place of it or kind of trying in place to be in place of it something that exists as sort of more of an idea than the actual fact but nevertheless something that is people want or trying that people are trying to do and I'm gonna be talking from the point of view of a CI system maintainer and the person who is developing a CI system for doing this kernel and participating in these discussions not from a point of view of a maintainer or a developer and the talk is targeted I guess as you would to people like me because that's that's what I see but I would like to hear opinions from other people looking from outside as well as maintainers and developers so do we have any maintainers here oh yeah oh great thanks thanks for coming so please there's gonna be quite a bit of time left for for a discussion so I'll be glad to hear what you think so I'm Nikolai Kondrashov I'm working as a software engineer at CKI project at Red Hat we're building a CI system for Red Hat internal use and also for running tests for upstream for maintainers who are interested in that thing I work with the links foundations kernel CI project on the KCI DB system which I'm going to be talking about a little later and I do electronics and embedded as a hobby and that's partially why I'm here apart from you know doing the kernel work I'm living in Finland but I was born in Russia so briefly short overview of the kernel testing systems what kernel CI and KCI DB are doing and then I'm gonna try to define the continuous integration just for the purpose of this talk and we will take a look at possible metrics how we can measure CI where the current where the kernel kernel CI is and what the hard limitations of that what the challenges are and what we can possibly do with that in my opinion so and these are slides that I put in all my presentations these days but there is a tone of a lot of CI systems which are trying to help test Linux kernel from all interested parties and this is just a just a sample of logos that we that we have there they all have their own report emails that they send to developers and they all have their own dashboards or something like that and kernel CI is the as I said is Linux foundations project which is trying to be the kernel CI kernel CI has their own labs and their own CI and the testing system and run tests on various hardware but also there is the case IDB part where we put together testing reports from various systems and this is the current state of who is sending data to case IDB and we're trying to build a system which would have which would aggregate those testing results and provide a single dashboard and single report to developers who are interested in this thing to you know to save time and to save effort for development and for interpretation of those results but conceptually it's very simple we just get some JSON from the from the submitters we put that put it into database displayed on dashboard and we look at what's coming in at the changes and we generate the notifications about the new results what's coming on we get about 30 300,000 test reports per day and maybe 10,000 builds for a bunch of revisions dashboard looks like this it's a Grafana prototype and we have reports which can look like this but I mean they are customizable so this one is aggregating results from four CI systems and displaying like the build results and then the test failures and the world status and gives you a link to see and see the dashboard and see the actual up-to-date and four results there so for our purpose purposes I'm gonna simplify this and but the main idea of CIS you all know is to test everything test every change as many changes as possible at every moment and provide feedback from that point of view we can define let's say four metrics for base metrics that we can measure a CI system by so how much functionality is tested which I know what kind of coverage we get then latency how how fast feedback is produced is it is it coming in you know after a few hours or later reliability how how you know how can you trust the results how much can you trust the results can you can you get the failure when it's actually a failure or a pass when it's actually a pass and finally how easy it is to understand feedback accessibility how easy how easily can you figure out what's actually broken where the bot you need to fix from that point of view the ideal CI is covering everything provides you instant feedback as you as you submit a change or as you create a change and it's always always true and it just says like what's broken so you don't have to figure it out and the worst I of course is not covering anything useful and takes forever and never tells you the right thing and you cannot understand what's the same so from that point of view it's it's worse than no CI so between those extremes so where we are with the current upstream kernel CI so some people try to measure coverage of their tests and because everybody is testing their own thing there is no single entity who controls the DCI there or knows all about it of course we don't have the results but I have heard rumors that some people tested and tried something and you know have some numbers but nobody really seems to know like this this is the usual lcove output page with coverage this is for run at the Red Hat CKI for AR64 and it could look like this that we had a bunch of tests and we ran them and it's been I cannot 12% code lines was covered but this is not the whole of code base this is just you know the most important things I suppose I don't remember exactly which directors we covered but it's quite a lot of them but in part because of course instrumentation in the kernel is kind of difficult and doesn't always work and makes the kernel much slower but it could look like this so nobody does really know nobody knows which tests we run where and and how much they cover well we at KCIDB know a little bit more because we get all those results we can take a look at them so looking at the mail list and this is just like you know unscientific and I didn't have time to you know to really scrape the mail list and see what's the actual situation because that's quite a lot of work but looking at that and taking a little bit of samples it comes out to be several hours just several hours which is quite good for kernel CI in some cases but it could also be a few weeks after after the change was posted of or you know on the mail list but it's faster of course for the for the merged commits where people pick up commits from a branch and the pre-merge CI is mostly non-existent so in those test results are quite unreliable and many CI systems and CI system maintainers they they do actual manual reviews of test results before sending them to maintainers and to developers because things go wrong quite often and accessibility it is it is quite good in places some some CI systems go like a long way to make it as as accessible as possible and the kernel maintainers are quite demanding that so that's why I guess but all those results they are all different all done in a different way so that makes it a little more difficult so hard limits of course we can only like since the kernel is an obstruction layer for hardware and to really test test that you need to test against all the hardware right so the natural limit for that is how much hardware we have we can test on and that's how much coverage we get of course that's that's not a problem for general functionality but for the hardware obstruction latency again and just just the hardware availability so how many how many tests you can run at the same time so the more hardware we have the faster it can go and reliability again is dependent on hardware how reliable the hardware is if hardware is not working reliably then it's hard to do reliable tests and of course the kernel itself but tests contribute to improving kernel reliability so that's great and accessibility finally again hardware availability because understanding what's happening without access to hardware which is often the case is quite hard so that's a natural limit and the kernel complexity also affects it a lot because the test can only be as simple as the kernel itself that it's testing okay so what are the challenges so the coverage is I think that there's quite a lot of people who want to write tests and a lot of them write them and there's there's a lot of tests in the wild and all the companies are writing tests so that's not a problem except that it's held held back by other difficulties that we have so which will go over in a moment so latency so the challenge with latency I think is that it is not safe to do pre-merge CI except for general functionality because anybody can post on the mail list and you don't want to just run anybody's code on your hardware directly you don't want them to start mining Bitcoin and you don't want them to wreck your hardware so this situation kind of prevents people from running more on real hardware in pre-merge CI even if it was you know more widespread and then the slow human reviews of course contribute to that because it has to go somebody has to go and take a look at it when they walk up next day and then they send the message and it takes a while so a big problem for reliability I think is the fact that the tests are often out of sync with the kernel kernel changes test change and issues come into the kernel tests start failing they keep failing while somebody is still fixing the kernel and they keep failing and they keep failing and the maintainers don't want to hear that they don't want to know like if they've seen somebody have seen that failure they don't want to hear about it they don't want to be said said that you know you submitted this change and failed while it's not their failure and of course developers the same everybody doesn't that nobody does want to be waste their time investigating a problem that they have nothing to do with accessibility is the is the I think the only problem with accessibility is that there is just so much different things different reports different dashboard so it's difficult to figure out and that's something that I think we can improve so and this this this is like just the basic challenges and they come together to give us more problems the low reliability and accessibility they lead to of course to low developer trust towards results so if you if the developer knows that that the test results often fail because of some other issue that they have nothing to do with and because and if those results are hard to understand because they don't have access to hardware and the report doesn't include some information on includes too much information their trust and interest for these results limits so as a result of that nobody wants to use those test results for gating right if if if you spend too much time investigating it and then it's you know it's not your failure like you're not gonna look at those to decide whether you can merge this or not as a result of that the test developers don't get feedback on the results and tests don't improve and the code is not improved because people don't look at test results so the whole improvement the whole feedback loop is starving because of this problem so high latency again contributes to to lack of gating because if if you have to wait for the result you know for for a week or two weeks that's kind of that kind of sucks so as a result in changes going without you know without consulting test results bugs get in they stay longer in the code base tests keep failing and again this contributes to more latency because you have to you know review more tests review more issues you know each time the test fails you have to okay this is this test it's failing because of that issue and the more issues there are the harder it is to try edge and slower it gets the more bugs get into the into the kernel tree and that brings high latency again so it's a vicious loop so this is a little diagram of how I think things affect each other it's a little complicated but I used this to kind of record what I think about this whole situation but we'll take a closer look at this later so if I had to summarize this I think that the this is the main thing that we can take out of there but enough of that it's my dog so that's enough gloom and I think we can go on to you know actually trying to to think what we can do with this or or what we can't do actually so the difference from like of kernel community and any open source community actually that it's not owned by single companies that you cannot really force people to do CI right in the company you can try to do this thing but like you get your tests just good enough and then you say like okay now we do gating and we fix the test afterwards and we start up the feedback loop and there's after a bit of stalling and you know fighting it starts up you're gonna do that here you have to you have to get the tests working perfectly to keep the developer trust high and keep you know thing going because without developer trust it's not gonna work so so what can we actually do and we'll be going over each metric some of the more important some of them are just just for for notice so for coverage obviously companies have the most hardware that we can use for testing right so if we attract more companies into testing into sending results it's gonna be better we will have more tests more coverage and hopefully that's gonna improve things so if you're a company and you have your own CI system and you want to contribute to kernel testing you can talk to me or just send a message to the mail list and we will set you up give you credentials and you can send your test results to KCIDB and help us get those results to developers if you have hardware that you want to contribute you can talk again to us and we can set up a lava lab for example and connect it to kernel CI and build submit tests on your hardware and the developers will get those results so in any case right to the mail list latency so of course it would be great to get pre-merge testing so to get less pub less less time to limit the time the box stay in public order just eliminate that as much as possible and to improve the speed of the loop to shorten the feedback loop so many people were trying to do those things and there's many approaches to do pre-merge testing and some people some people use patchwork systems to pick up patches from the mail list and then you know test them and then submit the results to patchwork and patchwork has facilities for that and and and that's working the only thing again is the authentication the patches that are sent there can be from anybody so you have to be careful how to run those another thing I think is a potential avenue is that is that there is about 50 repositories in the maintainers file that are on either on github or on a github instance that that allows us to to set up authentication actually for those patches and to connect the CI system so one idea that kernel CI is exploring is for example offering a github action that submits your patch to kernel CI and then gives you a check mark or red cross on your merge request and the the main benefit of that is that you can actually get access to real hardware for testing and in your pre-merge workflow in your contribution workflow so for that reason if that thing works we can we can then talk to more maintainers and encourage them to use those Git for just github CI and I know this is controversial and discussed to death in the community but this is this is one thing that some people can be open to as we can see that some a few let's say a few trees are actually doing the merge request and pull request workflow and the in this case as a selling point the CI integration can be actually a set you know a thing so to get that started I think of course the CI systems need to talk to maintainers and one step at a time and we can we can for example talk first first thing that actually many many systems to do in this offering to test a test a staging branch in the maintainers repository where the maintainers can push changes that they want to test and that gives us authentication and gets the tests running it's not pre-merge but it's better than nothing and it's and it can establish the trust and prove that your tests are viable and stable and good enough to go to the next step so after this we could you can like do you can pick a few tests that you can start with that are stable and for example setup okay what was I talking here about yeah so the thing is that you need to start with the test that they can trust but case IDB can be a part of that of course setting up the subscriptions because case IDB allows the the user to subscribe to particular tests particular branches combine those architectures whatever you want to start with to limit the set of data that that you get notifications on and that you get receive results for to just the test that you can you know you can start working with so that could be one thing where you can start as a maintainer or the CI system developer if you want to sell your CI results so there's there's this thing that is happening across systems is that of course there's manual reviews but there's just so many so many tests and so many failures that it's it's inefficient so many systems are setting up triaging and looking at the test results where they automatically can determine if there is an issue in a test result we are building in case IDB right now a triaging system like that but already for example graphics CI has a system where they can and there are various parameters of the tests and the conditions where the test executed and the actual files where to look for for the issue and regular expressions where to match for example if there's a string in a test output or in a console log then that means this is this issue and then we don't raise this test failure the same thing is actually happening at Red Hat this with CKI there's a UI describing an issue linking into a bug report and they the best the best system is at Google sees Google sees bot where they able to actually identify if a kernel crash is the same as the one that is they saw before based on a string that they also extract automatically from from the previous one then they're able to combine those crashes into single thing and not alert the maintainers or developers about this issue again and at the same time provide samples of those crashes so so we are building thing like that in case IDB to let CI systems actually who have this kind of data about more issues let them submit that information to case IDB so that results from other systems can also contribute and that we can finally get something green out of there because at the moment basically every revision that we get in case IDB is read because at least one build usually tens of them failed multiple tests failed this way or another so as is it's basically impossible to send those results to maintainers because there's always a failure even a single say system has a hard time managing the output so that so that it's you know usable for somebody and not failing all the time but when we combine all of these you get you get read all the time so reliability and all this is going to be controversial as well so I think that that a good good good step could be moving some of the tests more tests into the kernel tree that would help us keep the both the kernel and the and the tests in sync that's that's how it's normally done in CI right so that when you submit something for a change you can also fix the test at the same time and then we have less desynchronization and I think that LTP could be one of those tests to start with and of course we would need to integrate this into kernel documentation and you know to tell people that this is the test that you can you can run and make it you know kind of more official there's of course the problem that when you have that test there in that in that particular branch like for example in mainline it kind of gets bound to them to that to the state of that mainline and we have the situation right now where LTP is being executed against mainline stable all other branches and LTP has to actually handle all that and has to know which branches actually work where is the where is the issue how to work with this branch how to work with that branch so when you integrate it into the kernel tree it's kind of like you don't no longer need that and that helps simplify the test code a lot of course but then you can no long longer test all the branches and to handle that you would have to backward LTP to those branches as well but that's possible and that makes actually things simpler I think in some way so and of course if you want that actually work and to have an effect you have to you have to exercise those tests more often to help keep them in sync so those tests that are in tree need to be prioritized execute it more often than faster and closer to the actual change so accessibility again I think that KCIDB can help and you can subscribe to those notifications and you can give us feedback and you can help us make them better there's a link to guides how to start development so this is the complete picture I think how things could work out and how we could affect this whole situation and that's all thank you all right any comments questions ideas we've got one question from the stream maybe with that Lucas asks have you consider considered ignoring tests that are known to fail they've done that in their company and it really helps them start their CI up of course all the systems have a facility for that some less developed some more developed and that's what the known issues detection does for example at CKI and Intel graphics CI and other systems and that's what we are building in case IDB there is there are interesting challenges in that and I can talk about that more but I guess it will be a bit long for the remaining time many more questions there was a hand yes so I have one comment about your slide move tests in tree noticing with the code I fully agree with that with that statement I've yeah I fully understand and I will explain to you why I was the primary architect of a neural simulator long time ago we had about 20,000 tests in total the tests were specified in a declarative format YAML was a possibility for instance that was those tests were always part of the code and we converted the YAML specifications of the tests into HTML which was then picked up by documentation writers so this gave us a good guarantee that the tests were in sync with the code and that documentation was documenting what works and it gave gave us also a very nice structure for conversation between developers and documentation writers including tutorials by the way so it's just a comment that I had something that put up in my mind when I saw that slide thank you thank you yes yes that these are these are absolutely there's a lot of benefits of doing that and kernel of course has k-unit and case of tests in tree already but this these are just a small set of tests that everybody wants to run so there's much more tests that can go in there hello hi I'm the maintainer of the media subsystem and we also have some demands and one of the things that I'm also doing while at Intel I'm working on the two set that are testing GPU drivers and we had a lot of documentation there and I actually developed a new new way to document things so maybe we can try to sync up and maybe I can we can help together in order for both increasing the coverage on media drivers and also that documentation issue which is something that it should be really bad the best commented then what we have right now those days of course thank you of course and I also have one question right now you are actually using what do you should run the test I mean it is just such it is just you can see our database where you are collecting data from all the tests on dangers or whatever or do you also have your own testing environment where you are running the test directly so the kernel CI project thank you for the question the kernel CI project itself has a CI system there is the called kernel CI native where we run LTP and other tests for where it is interested so there's a lot of that and as well we have the case IDB system where we put those results plus results from Intel Microsoft ARM Google and others so there's both yes Tim so really like the analysis of what the deficiencies are and kind of where the gaps are it's not that was really good one of the things I thought was very interesting you know hard access to hardware is always a problem when you're a contributor right because you don't know if you're breaking someone else's hardware and so I always thought it would be really neat if there was a way to as a contributor request testing on specific hardware but you have this chicken and egg problem where people aren't gonna make their boards available to third parties for for random tests you know until they'd really trust kind of the ecosystem is not going to break their stuff right a lot of the boards you want to test on our proprietary behind company firewalls at least in my space and so you know we're not gonna let some random code come in and test our stuff but it'd be really nice as a maintainer or as a contributor if you could say well I want this to run on like a bunch of different hardware or maybe a bunch of different GPUs or something and so setting up some kind of ecosystem where that's accessible on demand it would be really good I think kernel CI has kind of the seeds of that where someone can set up a lava lab and take job requests but to flesh that out I think would be really nice yes absolutely and I think that the key thing for that is really the authentication of this of the request okay like you're not gonna get access to NDA hardware right but and the test results on NDA hardware not gonna go out in any case but it's possible to set up a system where authenticated users like certain authenticated users can request this kind of stuff like say specify like I want this hardware and I want this test to run there for for this change and that change can include the change to the test that you actually want to run and this way you get to exercise the actual you know functionality you want to test on that hardware and get for just like it lap and get how provide facilities for that you can put you know stuff in your commit message that would affect which tests are actually executed for example there are other ways like you know having bots who go and read your comment and take commands from there that execute that that's a little more involved but these things with with authentication and with some sort of platform underneath it these things are possible I have a comment about the reliability and I recommended read it's a blog post by Netflix on how they track performance regressions because normally you have a threshold and you're not allowed to go over the threshold and a lot of the times you might have a fluke that temporarily go above the threshold and then you increase the threshold and then over time you suddenly have a regression so what Netflix does is they look at multiple test results in history and then they look at the derivative are we going the wrong direction and also look if they can also determine if this was just one fluke in a series of tests that's a recommended read it's called the fixing performance regressions before they happen yeah that's that's a good idea yes and that's that's what I have in plans for case adb but I am the main contributor I have one in turn right now but I don't have time to actually contribute myself because I'm busy with red hat stuff but I would love to see more people contributing to case adb and I'll be working on adding more documentation for developers so that's easier but really like if you have an idea come over and like I have these plans absolutely like an idea is how to do that and there are very interesting challenges that I can talk about so there's could be a lot of fun to be had any more questions comments okay awesome thanks everybody


Okay, okay, so let me start by talking about trying to combine our coefficients result with the rational rings. And so let me talk for a moment about Branson's go to theorems with coefficients in that rational rings. So, he and I were interested in that of course, once we had the rational result and so we proved the following serum in a follow up paper. It's a little technical so, or it has a lot of hypotheses. So let me write it out. Let's let R M be an excellent f rational. If you don't know what excellent is just think complete local ring. We have J contained in I a reduction. Okay, let's let L be the analytic spread of I let's assume that strictly larger than H, which is the height of I. Okay. So, the following properties. Let's see our mod I is equidimensional. So, that means our mod H minimal prime is the same dimension for all minimal primes. So that I is generically. So that means that of reduction number. At most one so that means that each minimal prime, the, the I localized at P has reduction number at most one. And so there's three that condition that condition, and that are mod I on mixed. So this satisfies Sarah's depth condition. S sub L minus H plus one so this is a condition that locally, we have a certain minimum depth. Then we proved I to the L bar is contained in J. Times I on next. Okay, and I'm not going to try to do any proofs for this little bit, but that that was what we got. We had a student Aline Cosry. And so we were able to generalize this theorem. So, let me take the statement and paste it over here and make the appropriate. The appropriate changes. Okay. So, so we were able to prove that you don't need excellent. You just need cone McCauley so afrational rings are cone McCauley under mild conditions. Okay, so you don't need to assume our mod I is equidimensional. You don't need to assume I is generically of reduced reduction number at most one. And you don't need to assume the depth condition. So you don't need to assume anything, but you can get a better result because for all W greater than or equal to zero. You get that the usual statement holds so over here we get to add a W plus one. And so one thing for for those of you who are are young and starting out. This is a really good lesson to learn is you can read other people's papers. And you can, you should give some real thought to what their hypotheses are, because there are definitely times when most of their hypotheses are not nest that most some of some of the hypotheses may not be necessary. But again, I'm not going to talk about any proofs here just just wanted to point out that we, we can actually combine these. So, I'm going to switch gears now a little bit. And I want to talk about what are called coefficient ideals. Yeah, I think that's pretty good advice. So let me talk now about what are called coefficient ideals. The terminology here perhaps isn't ideal. Because we've been talking about Branson scota theorems with coefficients. Now I'm going to talk about coefficient ideals and they're not quite the same thing. So, let me point out right that are in our earlier results. We've always exploited the difference between the analytics spread in the height of the difference. We looked at L of I minus the height of I. And so the, the equation that I wrote at the end of lecture one and the start of lecture two that that really has the most power so far that we've seen when we when when there's a difference in these two things and we can use that. So then, but, of course, I, that leads to the question what can we say when L of I is equal to the height of I, and these are usually called equimultiple ideals. And so the most basic example here is if we if I is contained in a local ring. And the radical of I is M, then we have that the analytics spread of I well it's bounded below by the height which is the dimension of the ring and above by the dimension of the ring which is the dimension of the ring. And so L of I is equal to the dimension of our is the height of I. Okay, so, so there's a situation where we might want to be able to say more, but we certainly can't exploit this difference. And so I think that leads us to wanting to define this coefficient ideal. We've got properties so I guess I meant to define the coefficient ideal. All right, so definition. Let's let R be no Therian and J and I, and are any two ideals. I'm going to give myself a little bit more room here. So let, so notice J is not I'm not assuming J is contained in I here. So, the coefficient ideal of I relative to J is we're going to use this kind of what's that a frack a I comma J. It is the largest ideal say frack be such that I times frack be is equal to J times frack be. Okay, and if, if we know we're talking about I and J, I'll drop the parentheses here, just call it a. Okay, so that is what the coefficient ideal is so let me go through some of the properties and these are all exercises. So, so say R is no Therian. Okay, and I this is, I don't need to be local or characteristic P. And now let's assume for the moment let's assume J is contained in I. Then, actually, I think I'm going to number these one. This coefficient ideal is always well defined. And to if a so again this is a if frack a the coefficient ideal contains a non zero divisor. Then the two ideals must be equal up to integral closure. Okay. And so, J is a reduction of I. Okay, and in this case, then there exists, and bigger than or equal to zero such that a power of I is contained in the coefficient ideal. All right. And now let's assume we're in the local case. So, if our is local. Let's assume J contained in I is a reduction. And let's set R equal to the reduction number of J with respect, sorry, I with respect to J. Let me define a sequence of ideals let's set a one to be the colon ideal J colon I itself. Okay. And for larger values, let's set a and plus one to be the J times the previous one colon I. Okay. So this is a sequence of ideals. Let's call this three. We have that I to the R is contained in the coefficient ideal. The coefficient ideal is contained in each of these a sub ends are all N. Okay, and this is a decreasing sequence. So, a N plus one is contained in a N. Right. And the fourth thing, if, if I is M primary, then the sequence stabilizes, you know, I E a and is a for all and large enough. Okay, so those are the basic properties here. In your statements to be who is are in the exponent you have in I are because I don't know. Sorry, sorry, sorry. Okay, so those, those are the basic properties of this, and I've left those as exercises. Okay, so here is the theorem, my lights automatically go out if there's no motion. And I guess my arm moving isn't enough. So, this here is a theorem on the notes it's 3.4 again this was proved by myself and Craig in 2001. Okay, so the following. Let R M be a regular local ring containing a field with I guess arm on M infinite. So, let I be an M primary ideal and J contained in I a minimal reduction. Okay, then for all W there's a little trick here. We're going to start with minus one instead of zero. If we look at I to the D plus W integral closure so that's the usual remember D, D is the analytic spread here. In this case, this is contained in J to the W plus one. Well, so that would be the usual pre instance go to theorem, but where can we put the coefficients we can put them in the coefficient ideal. So, as usual, the characteristic zero case follows by reduction to characteristic. So we are going to concentrate on the characteristic case. And so one, the really the biggest tool we want to use here is the following observation. Okay, so in in a regular ring. So two ideals say a and B contained in R. Well, so we can, we can form a colon B, and we can take the q bracket power. And that's obviously related to a to the q colon B to the q. In a regular ring they're equal. And again, that is really the flatness of Frobenius. Okay, in general, if you take the left hand side so if you take the colon and you take the q bracket power you're going to land inside the a to the q colon B to the B to the bracket q. And the right hand side is usually much larger I mean that that, you know, there, there's just all kinds of things that that should get in that colon, that don't start there, or don't come as q power. Okay, but in a regular ring. We, we get that those are the same. Okay. So, yep, I think the rest of my notes are just to do the proof. Okay. And then talk about a few other things. So, I'm going to do. So, most of the work is the w equals minus one case. So which we will. Okay, so we want to show. We want to show that I to the D minus one bar. Well this will be j to the zero. Hey, I J. So in other words, we just, we want the D minus first power to be in a. Right. So, um, So, what we show. So we will show that I to the D minus one bar is contained in a and all and and then right for and sufficiently large. equals. Right. Um, actually. So in fact, we'll show something slightly different. We'll show that I to the D minus one. Bar is contained in a and to the bracket Q. For all and and for all, I guess for all Q. Okay. So, um, And of course, notice that this. We look at I to the D minus one bar to the bracket Q that's contained in there. Okay, so, um, So then I to the D minus one bar is contained in a and star, which is equal to a and because we're in a regular local ring. So that's, that's really our goal. And one thing that this argument is going to illustrate in. In rings of characteristic P when you're using tight closure is that you notice we've we've already taken a Q power here, but we are going to prove this using tight closure, which means we're going to take the Q prime powers of the Q's powers and use tight closure. Kind of doubly then I guess. Alright, so let's see if we can go through that argument. Let's start a new page. So, for n equals one. We want to show that. I D minus one Q the integral closure of that is contained in J colon I that's to the bracket Q, right this, that's a one to the bracket Q. And as we said, that's so because this is a regular ring that's J to the Q colon I Q. Okay. So, in this case, let's let X be in I to the D minus one Q bar. And I guess I'm going to call it to follow the notes or be in I to the Q. Okay, and we just need to multiply. Well, first, we'll choose a C, not equal to zero as in exercise two. And this is for the ideal ID Q for that power of I. Okay. So, then Q prime so we'll introduce our new power of P. We're going to look at C, R X to the Q prime. Okay, so we're, we're trying to show that when we multiply by I to the Q, I, I to the bracket Q times this ideal, we're going to land in this ideal. And there's our R in here. There's our X in here. Okay. So where does that land. Well by the choices we've made. This is in. This is going to end up in J to the D Q, Q prime. Okay, that's where that ends up. This is in J. J is degenerated so that gets us in the Q Q prime bracket power. And the coefficient D minus one Q Q. Okay. Oh, and I don't, I don't really care about the coefficient. So let me erase that. I do this each time I've practiced as well. That's all I need. Right. And so therefore, we've got that R X is in. Well, let me write this as J J to the Q to the Q prime bracket power. So R X is in J to the Q type closure, which is J to the Q. Okay. That's what we wanted. Right. So we've done. We've done that a one case. All right. And so now, following the notes, the way I wrote it, let's assume the result for N minus one, bigger than zero, I guess the way I'm writing that. Okay. And again. So, what do we get we've we still got our X and our R those aren't changing. So, we've got C, R X to the Q prime. Okay now. This is in. Where is it. Okay, so we're still in J D Q Q prime. Okay, which is contained in J to the Q Q prime because J is degenerated, along with the, the remaining terms. So that would be J to the D minus one Q Q prime. Okay. All right, so that's in J to the Q Q prime. Certainly that's contained in I to the D minus one Q Q prime. And that is contained in J to the Q Q prime. So I guess we can take the integral closure, and that gets bigger. I did the D minus one Q Q prime bar. Okay, but now by induction. And where does this land. This is in J times a and minus one to the Q Q prime. Okay, so we are applying the induction hypothesis. We're applying the induction hypothesis on a bigger power Q Q prime. And that's what we needed. And so therefore, again, if we write that as in J to the bracket Q have everything right. I'm sorry. There's no J here. It's the the integral closure in the am minus one. So now we are in J to the Q a and minus one to the bracket Q to the Q prime. And so therefore, we have that R X is in J a and minus one star equals J a and minus one. And so X is in J a and minus one colon I, which is a and that shows the W equals minus one case. That's the end of the proof. Right. So the So if we if we want to try to understand M primary ideals a little further, we have to introduce these coefficient ideals and we we get nice coefficients. So I should point out here that that we we use that I is M primary pretty strongly. So if if I isn't M primary, then the we have we still have this sequence. Right. It it's certainly need it's not at all clear that it stabilizes. Okay, so I should say that again, this is another area that I had my former student Aline Hansry look at and we have a paper where we do generalize this result. In the complete case, one can use Chevrolet's lemma and and get some results, but for for non M primary ideals, but but there it's a little mysterious, otherwise. So we're sorry in our primary deals the only ideals for which these coefficients are known to stabilize. So that's what I wanted to say about that. And then I wanted to use the last 10 to 15 minutes to mention a couple of other places where Branson's go to serums are are relevant. So let me talk about cancellation results. So Okay, so so in in the most basic form, the Branson's go to theorem says, for instance, that I I to the L of I bar is contained in J. But in fact, often, a smaller power is already in J. So so especially if you're if you're thinking about ideals that are particularly nice they have small reduction numbers with respect to J. And so there's there's no reason you necessarily have to go up to the analytic spread value to get inside J. So, so that's a question. So that leads to the following question. What conditions lead to getting say an N less than L of I such that I to the N bar is contained in J. Right night up here, of course, that's a reduction of I. So there's a nice paper of Craig uniki in from 2000. So he has a paper 2000 on cancellation of ideals. Okay, so EG. If say a times B is equal to a times C. That implies B equals C. Right. So that's usually very false right for ideals. For my matrix for my linear algebra students, it's always true for matrices, no matter what I've learned, but but it's generally not true for ideals, but he came up with some nice criterias for for for when this really does work. Okay. So, so no if let me say it this way, if the reduction number of J. I is R, then we have that I to the R plus one is equal to J times I to the R. Okay. And of course this is times I to the R. So there's kind of, you can try to kind of think well is there any form of cancellation here, right can you cancel one of the eyes and does this imply anything like I to the R is contained J. And of course the answer is no, usually right you need, you need a lot more going on here. Okay. But let me just write down what the theorem is that we were able to show. I guess say what that theorem is. So let's let R M be an F rational Gorenstein. Okay, and so therefore in fact it's it's an F regular ring of dimension D. Okay. Say we have an ideal in R with H being the height of I and that we assume is strictly less than L of I. Okay. All that L and suppose that our mod I is cone McCauley, so you have a pretty strong condition upon the quotient. Then for any reduction, J of I, we get that I to the L minus one bar is contained in J. So we can do one better than the Branson's go to theorem in general will tell us. Okay. And my student Aline Hasry generalized this slightly. I didn't, I don't remember the actual result of off the top of my head. But I think we, I think it was about maybe our mod I doesn't quite have to be cone McCauley. So, so this is an interesting result. Something I spent a lot of time thinking about that I don't recall ever getting anything useful is what about coefficients here. So, you know, could we put something here under the right conditions to get the coefficients. And that's a great new result. Anybody could do that. So, you notice here so the analytics spread needs to be strictly larger than the height. But even if the analytics spread is much larger than the height. This results still only allows you to go down by one. So it would be really nice to know if maybe with stronger conditions. Right so so I'll write it as a question. Maybe maybe can one do better. So questions. So one, can we get coefficients on the right hand side. Okay. Okay. Okay. So if L is greater than H plus one, so not just greater than H. Can we get. I to the L minus two bar contained in J. So those are both both interesting questions that it would be nice to see answers to, and then then one can wonder about if, if one can answer the second question positively can you get coefficients right there's no end. There's no end to, to these questions. And let me use the last few minutes. I guess I have another blank page to talk about uniform grants and scota results. So, so uniform. Branson scota results. Well this is, this is coming from one of my favorite papers of all time. So in 1992. I think he published this paper on the uniform. Art and Reese's theorem. Okay. And what that says. So, if our is a nice, I'll just put it in quotes, a nice no theory and ring. And again, the conditions are in the write up. If you want to see them specifically. Then, given. And the sub module of M. These are finitely generated our module. Then we have, of course, if, if I give you an ideal there's an art and Reese, the art and Reese's theorem tells you something. But what he saw is that there is actually a uniform art and Reese. So it goes this way then there exists some number K, which depends only on and such that for all. And bigger than or equal to K. And for all ideals. In the ring. If we look at. I to the N M intersect and then we are contained in. I to the N minus K. Okay, now the art and Reese theorem gives an equality with more information on the right hand side. And that is fault. There is no uniformity there that can't be done. But, and I see a Rena's face showing up higher and she has done lots of work. And that is the art and Reese that I encourage everybody to read as well. So, this is a beautiful theorem. It stems from lots of lots of the ideas that came up in tight closure. Go into that. And it's certainly a paper I encourage anybody to read. If you haven't already. The results. That he proved is the following he proved a uniform. And he proved the same conditions. And he proved in the domain case you can generalize it a little bit but but really it's the domain case. It's where all the information is. Then there exists K. Then there exists K. Such that again for all ideals I contained in our. And all n bigger than or equal to K. If we look at I to the N bar. This sits inside. I to the N minus K. So this is a form of Brienne since go to theorem, right that you just there's there's a, but notice what this says this says this is true for all I, no matter what the analytics spread is. The integral closure sits in a pretty big power of the ideal itself, right that you only have to go down some uniform K, and this, this. Of course this K can be quite large. It's not connected to the dimension of the ring at all it can be much bigger than the dimension. But are there. Um, sorry again, are there any sort of effective ways to compute this number that you need for the appropriate things go to here. Um, I think a proof analysis would probably tell you what you needed to do you have to. You know, for instance in the car at least in the characteristic P case you have to know something about the test ideal. And so, so, I don't know if you can compute the test ideal then you're you've gotten part way. And, and I, but I'm. Yeah, I can't, I can't answer that terribly off the top of my head. That's that's actually what I wanted to finish with I hope for everybody that this that I've helped you to understand what the Branson's go to theorem is and what some of the ideas are behind getting these improvements on it. And I appreciate your attention. Are there any questions or comments. I have a quick question on. I wrote on page 10 for the theorem. Yes, I guess without like any strengthenings. There are cases where L minus one is sharp is that correct. That is probably correct. Yeah, if if H I mean I think if if L is h plus one. You probably can't do better. I mean you can probably come up, you know, you can create examples where you can't do better I should say. Thank you. I have a question here. Yes. The Branson's go to theorem for with coefficient idea. Do we ever get equality. Oh, I. So, so how often do we get an equality for instance in in the, in the, like in the regular case. Right. So, so for instance, you hear something like that. Yeah, because it will, if it was an equality, it will help us to calculate the integral closure. Yeah. I don't, I don't have a really good intuition about that. It strikes me as being unlikely. In general, that we're getting an equality here. You know, unless the reduction, well, yeah, even if the reduction number is small. It seems, it seems unlikely to me that we'd get equalities very often. I mean, basically I would say, you know, there's, there's, there are more coefficients that probably are very hard to quantify in terms of a theorem, but I'm, I wouldn't be surprised at all to know that this is in this, you know, times, the maximal ideal. Most of the time. It could probably happen if the reduction number is low. Yeah. Or is M primary. But then the right hand side, right hand side will give you powers of an ideal. Right well this, yeah, this would not apply this particular form wouldn't apply for an M primary ideal then we'd be wanting to look at at this version. Yeah, and maybe, maybe there's more one could say, I don't know much about it. So, I couldn't, I can't. That that aspect of it I can't tell you. And there is also a generalization of Bransco and Skoda by recent Sally. Where they take in a D dimensional local ring. Instead of taking I to the D bar. The product of these ideals and then the integral closure. The product of the ideal is contained in every joint reduction. Oh, the idea I to the deep bar is really high times I D times. Yeah. I looked at any collection of the M primary ideas and then showed that the, the closure of the product is contained in every joint production of these be ideal. So something like that. Right. Yeah. So, I wonder whether coefficient ideals are considered in for this generalization. That makes sense to me that there's probably something there. But I yeah I have never really done any work with with that situation so I again I don't know. No, that's a good, good assignment for a PhD student I'd say. Or at least something for them to think about. Okay, so are there any more comments or questions. Okay, so let's thank the professor, other back for his lectures. Thank you. Thank you. It's been a pleasure to be


Hey folks, in the last several episodes of Code Club, we've been looking at a variety of approaches to build climate spirals. Climate spirals are a line plot depicted in polar coordinates, where each lap around the spiral represents a different year. And so these were originally made by Ed Hawkins at the University of Reading and quickly became quite iconic. More recently, NASA released a more updated version with newer data from their data set. And until we've been building out these different versions, based on Ed Hawkins' version as well as the NASA version, to build GIFs or MP4s that you could kind of watch the spiral evolve over time. What I want to do in today's episode, though, is show you how we can build an interactive version of a climate spiral. And we're going to do it in three dimensions. And so we'll have the polar coordinates. But then we'll also have a Z layer, which will be the time, right? And so you can imagine that climate spiral in 2D now like a slinky pulling it up and being able to visualize it and kind of dig into it, if you will, using interactivity. So there's two frameworks that I've used in the past to build interactive data visuals. The first is RGL. I find that RGL maybe has a little bit of limitations to it and isn't as easy to use. The second approach is plotly. I've talked about plotly maybe about a year ago. And so plotly is great for building web based interactive visuals. So you could make a visual, put it into an HTML document like a web page, and then your viewers could play around with it and move the data around. If you're coming from the microbiome world like me, perhaps you could think about making a three dimensional ordination, or you could have an ordination and two dimensions, plus a time component. So I'm not going to do that. But what we're going to do today is to take our climate spiral and bring that into plotly to make an interactive three dimensional visualization that I think you'll find is going to be pretty cool. So before we can plot the data, we need to get the data. If you want to get the data that I have, along with all the code that I'm going to generate down below in the description is a link to a blog post that will get you everything you need. I'm going to go ahead and save this R script into my code directory, or call this climate spiral plotly. And we'll go ahead and add to this library tidyverse. And we're also going to need the library plotly. So you might need to install plotly if you haven't installed that before. Great. So we are going to need to get our data in. And we've done this in previous episodes, but typically a copy and paste over and over again. But I want to do it from scratch for anybody that's just joining us for the first time. And so we'll do a read CSV data forward slash, and it's this GLB file. Go ahead and run that. And that brings it in as a CSV. But why isn't it parsing it? Oh, right, because the first line of this file has a header, and it's just kind of a comment that we don't really need. So we can say skip equals one. And so now this reads in and parses it properly with the year and the 12 months. Very good. And so now what I want to do is to do a select on the year. And that was a capital Y year. And then I want all of the months. So then I can do month dot ABB. And so now I get the year plus the 12 months. I didn't comment on this before. But the full version has kind of seasonal ranges. So like, and also an annual average from January to December, December to November, but then like December, January, February, March, April, May, so forth. Right. The other thing I noticed when I ran this with the select was that it gives me a warning message that if we use a vector to get specific columns, the best practice is to use all of as a function around the vector of names we want. And so we can go ahead and add that here. So we'll do all underscore of month ABB. And again, what that's doing is telling the select function that we want all of the values in this external vector in this month dot ABB vector. And again, if you haven't seen that before, month ABB is a built in vector that has the names of all those months. And so now when we run this, of course, we get that and we don't get the warning message about using all of very good. So now we need to get this to be tidy. So we'll now do pivot longer. And we'll do everything but the year. And we'll do names to and I'll do month. And our values to all say t diff. And so this is a temperature difference. Each month is a difference for that month, relative to that month over the years of 1951 to 1980. And so that's why the numbers look a little bit weird. They're not the absolute temperatures that you might expect to see. So I'm getting a error message that it can't do this pivot longer, because January is a double and April is a character. And that reminds me that in the original CSV file, they indicate na values with three stars. So now if I do na equals star quotes, and then three stars, if we run that, we now see that April is a double. Whereas before when we ran it, let me come way back up here. Yeah, we see that April was a character, right? And so I should have noticed that those months from April out are formatted a little bit differently than January, February, March. Anyway, so we should be good to go now. Yeah. And so now it pivoted longer. That also means that there's na values in our data frame. And I think that that is the last several values. And so if we do slice tail, and let's do n equals 12. Yeah. So basically, from April through December of 22, I pulled these data down in March, or I guess in April, and they didn't have the full April data yet. Those are na values. And so I could replace that slice tail with drop na. And now if I do slice tail, n equals 24. Yeah, we no longer have those na values, and everything looks good. So the next thing that I want to be thinking about is that my month is of type character and I'd rather it be a type of factor. And that's because the month, the alphabetical is not their order, right? It's January through December. And so I can then do a mutate on month to be a factor of the month column. And I can say levels equals month dot ABB. And so now when I run this, I see that this column month is now a factor. If I hadn't done that, then it would order my months alphabetically. Now it's going to order my months chronologically as the order of month ABB. Excellent. I'll go ahead and for good measure do an assorting. I'll do a range, year and month. I don't think that's going to change anything. I realize that I have a lowercase year when it's an uppercase up here. I'm going to go ahead and change that. So I can in the select, I can say lowercase year equals uppercase year. And so then I need to change that capital Y to lowercase all the way throughout. I prefer to work in lowercase because that way I don't have to worry about if this variable capitalized or lowercase, it just it's always lowercase, right? And so now we've got those data sorted appropriately, and we're in good shape. Now what we need to do is build out our x, y and z coordinates. And so we'll do another mutate, we need x, y and z, right? And so to get x, y and z, I first want to think in terms of polar coordinates in terms of the angle theta, as well as the radius. So my radius, I'm going to think of that as being my t diff column. And then my theta, we saw this before. But we can think of that as being basically the month number divided by 12 times 2 pi. And that'll give us the radians position in that polar coordinates. And if if this is novel to you, go watch the last episode where I talked about building out one of these climate spirals without using cord polar that describes all the trigonometry. Okay, so to get that, I want to also have the month number. And to get the month number, what we can then do is as dot numeric on month. And let me just double check that works the way I anticipate it working. And so yeah, so now we see we've got the month number for each of the months, right? So like September is number nine. That's great. Okay, so I can then put a comma here, radius t diff theta then is going to be month number minus one because I want January to be at zero position. And then that divided by 12. But I want that all times two times pi, pi is a built in constant in R, which gives us 3.14, whatever, right? And so now what we get is our month number, our radius and the theta. So now what we need to do is to convert our radius and our theta into x and y. So our x, we can say radius times the sign of theta. And our y is the radius times the cosine of theta. And then our z is going to be the year. So now we have all this wonderful information that we can then use to make a plot. So let's go ahead and save this as t underscore data. And as we saw before, we could take like t data, pipe that to ggplot aes x equals x, y equals y, and then do color equals year or z say, and then we can do geom path. And that gets us our climate spiral. One thing I'm remembering from the last episode, however, is that our radius for t diff is going to be negative, right? So again, if I look at t data and look at my t diffs, I've got negative values in there. And so that does funky things of giving us a negative radius like we see here. And so what we did in the last episode was added a constant to all of the radius values. So let's go ahead and do plus 1.5. And so now we can see that that hole if you will inside of the donut. Okay, we've done this already. But we want to do now is to make an interactive version of this where the year is in the z axis. So what we'll do now is go over and learn a little bit about plotly. If you go to plotly.com, it brings you to this website. plotly is a JavaScript HTML based tool that allows you to make interactive visualizations that are web based. So if you go to docs, and then go to graphic libraries docs, you'll see that there's a variety of tools available for interacting with plotly. There's Python, there's our Julia JavaScript. There's a plotly GG plot to graphic library, there's F sharp, which I've never heard of before in that lab, and then something called dash, what I'm going to be interested in is mainly this plotly are open source graphing library. There's also a GG plot to add on, which basically allows you to take a GG plot based visual and feed that directly into plotly to make an interactive tool that way. I find that that's a little bit limited for what we want to do within three dimensions. So I'm going to work within the R based open source library, which will allow us to build a figure directly within plotly. And so going into that page, we find that they've got a really great source of visuals that I kind of scan through and I find something that looks like what I want to do. And then I see how they did it in that visual. So again, there's fundamentals kind of the basics of using plotly, some basic two dimensional charts, statistical charts, variety of scientific charts, financial charts, maps, artificial intelligence, 3d charts, that's what I'm interested in. But we'll also see there's sub plots, ways to make facets, transformations, various controls, some animations, and a variety of other things, right? So what again, we're interested in our 3d charts. And specifically, I'm interested in 3d line plots. And so again, my approach when I'm learning something new is to look for visuals that look like what I want to do, but in that new framework. So in this case, plotly, right? And so 3d line plots looks a lot like what I want. So let's go ahead and click on that. This opens the 3d line plots in our page. And we can kind of look through here and see, you know, basic 3d line plot. And so one of the things that you might notice is that I can click on the plotting window, and spin this around, right? I can use my scroll around my mouse to zoom in, and to zoom out, right? And so there's a tool tip so that if I hover on a different point, I get a bubble that pops up with the x, y and z coordinates, right? And I can make line plots like this. And so this then would be the code to make a very basic line plot like you see here. So let's go down. So we can make a 3d line and markers plot. Hey, this looks a lot like what we're doing, right? This is in polar coordinates, we can see that they've got x and y and z, like we do, right? So again, I didn't intend this, but it's it's pretty cool to see something like a climate spiral here. One thing that they're adding to this is that they've got, if we scan in, they've got markers on the different points of our line plot, which isn't what I totally want. Also, this has a color scale that's coming from the Veritas package, which we saw a while back when we were looking at Ed Hawkins version. I'd prefer to use the blue to white to red color scale that we saw with the NASA project. Ah, look, it's almost like we asked for it, custom color scale. And once you know it, it's also in polar coordinates, right? And so they've got this kind of cool spirally thing that's got a custom color scheme. Perfect, right? This is exactly what we want. What I'm going to do is I will go ahead and copy this into a new R script here in our studio. And I'm going to go ahead and run these different lines. And so as I'm running this code, I don't want to just copy and paste the code. I want to think about what is the code doing, right? And so, so we load the plotly library. There's something here about a count of 3000. So maybe this is that there's going to be like 3000 different points being represented. They then create X, Y, Z and C as vectors, but they're empty vectors. They then use a for loop to go through all 3000 values of count. R then is the radius, which is the count or I, the counter, the stepper variable times the count minus one. So again, that's going to be the radius. The X and Y then are, yeah, so it's basically the same transformation that we saw before. They're using C X comma, and then the trigonometry that we've seen. And what that does is that basically concatenates on a new value to all the previous values of X, Y, Z and C. I'm not sure what the C is. Maybe it's color. And so then they make a data frame from those four vectors, and then they throw it in and make a plot. And so again, looking at the syntax for this plot, we can see that the syntax is a bit different than what we're used to, right? So they give plotly the data, the data frame, right? So in our case, that would be like the T data. They then define X, Y and Z. And they're using this tilde X, I think to say the column X within data should get mapped to X, the Y column in data should get mapped to Y, Z to Z, right? And that it's then using a type scatter plot, a 3D scatter plot, and the mode lines. Then there's some styling here that we can add for the line, right? And so the width being for the color is getting mapped by the C column from the data data frame. And then again, remember, this section of the web page was for a custom color scale. And so what we can see then is that we've got this color scale argument going to this list to define the line. So when C is zero, it should be this color in hexadecimal. When it's one, it should be this color. So I think I have a handle of what it's doing. Let me just run all this and make sure it works like we saw on the web page. And sure enough, here is the visual that we had up on the web page. So again, what I want to do now is I'm going to go ahead and copy this code that they use to build the figure into my climate spiral. I'll go ahead and comment out the ggplot and I'll paste that in. Maybe I'll remove the fig because I don't know that I definitely necessarily need all that. And we'll tab this over a little bit to make it look nicer. And so again, in our data, that's going to be t underscore data, we have then x, y and z. Again, type scatter 3d lines. And then we need we're going to let's leave it with the four. And then my color, I'm going to make that t diff. So let's go ahead and run this and see what it looks like. Of course, we'll have to adjust along the way. Very cool. There is our climate spiral. And you can see again that we can pan around spin it around play with it. And it's pretty cool. And we can zoom in and we can kind of look click on different lines and see what the temperature diff was for those different positions. Very cool. All right. So I'm not such a fan of this color scheme. And so what I want to think about is how can we adjust this color scale to get the color scheme we want. And so let's go ahead and mess with the zero and one. I'm getting the sense that zero to one is a scale. So it's basically transforming t diff to be a number between zero and one. So let's go ahead and make our zero the lowest level. I want to make that blue. So in hexadecimal, it gets two characters for red, two for green, two for blue. So do 0000ff. And then for red, of course, will be ff 0000, because again, red, green, blue. Fantastic. We now have our blue to red. Of course, I want that to go through zero at point five. So let me go ahead and now see if we can modify this. And so I'll do 0.5. And for that, that's going to be white. So it's going to be all f. So 123456. Let's see what happens when we do that. So very cool. We now see that goes from blue out to red. And I'm pretty happy with the way that that all works. And it's looking pretty well. And it's amazingly simple to have gotten this far. The next thing that I want to do is go ahead and change the tooltip, right? So I don't care so much about the X, Y or the Z being shown. What I'd rather have would be to have the month, the year, and then the temperature difference. And so we can modify the appearance of that tooltip and what information is being portrayed. To do that, let's go back to the plotly and see if we can figure it out from one of the demos that they have. So let's go back to the gallery of different options. And across the top here, I remember the first was fundamentals. So let's look at the different options or things that they have available for looking at fundamentals. And we can see that there's all sorts of different ways to change the marking and styling of what's going on. There's one here called hover text and formatting. So let's check that out. So again, we're in this hover text and formatting in our page. And so if I hover over each of these points, I see I get a different label. So typically what we got before would be like the X, Y coordinates. And so what we're seeing here is that when I cover this point here, I get text D. So then coming up here, I notice two things so that there is a line in here for text, as well as for hover info. And so text then is the text that shows up. And so I imagine that could be a vector of values. And then hover info is text, which I think is telling plotly that the hover info should come from the text column. So again, we'll do text equals and I'll do T diff. And we'll modify this later to make it look better. And then we'll do hover info equals text. So now returning to our climate spiral, we see that when we highlight over a different point, we get, yeah, we get the T diff for that particular value. Cool. So now I'd like to make it look a little bit nicer. So what we'll do is create another variable in our data frame that will be the label. And so I'll do label equals glue. And so I'm going to use the glue function, which comes to us from the glue package. So I need to do library glue. And again, what I want is the month and the year and then the temperature difference. And so I'll do month and space and then year. And then I'll do a backslash and occurs me I need this all to be in quotes. And then I'll put in here T diff. And so now if we look at T data, we get a column that's got our month, year, and at least on my zoomed in version, it gets trimmed off. So then we'll change our text to be label. And so now what we see is that we have the month, the year and the temperature difference. So that's pretty cool, right? So I can then scroll in here and grab a point, any point. And I see that I've got blue at negative 0.03. So something that has me a little bit worried is that here I have a positive value. So July 1983 has a T diff of 0.18. That should be red, not blue. And so again, if I kind of look through these different values, 0.1 is kind of large for the values we're looking at here, the range we're looking at, and it's still blue and that should be red, right? So my concern is that it's scaling all of the values to between zero and one with the midpoint at 0.5 being white. I think what we need to do though is tell it what T diff value needs to be at 0.5. So let's come back to fundamentals and see if there's anything that sticks out to us about modifying the color. Let's see. Let's see. I thought I saw something. So built-in color scales. Let's look at that, and maybe let's look at discrete colors. So the built-in color scales, they've got things from Color Brewer that allow us to put in specific color palettes. Maybe the Divergent would work for us, but still we need a way to tell it what's at that midpoint. So I'm not seeing anything here. If we look at discrete colors. Yeah, so this is like setting specific values for different variables. And that's not really what I want either. So I think we're going to have to turn to our friend Google. So I think what I'll do is I'll search for plotly color scale. That's the name of the argument that we're modifying in the list to set that color range. And let's do midpoint. And so this first link is for a Python version. But let's check that out because maybe that will be useful in the long run. So again, continuous scales. Of course, it is Python, but maybe it'll translate over to R. And so as I look down here, I see color ranges. And as I read through this minimum to maximum range of data mapped to the zero to one input range, again, in our color scales argument, we had blue being zero, red being one, kind of reading through this a little bit more, I see C min, C mid, C max for the various arguments for things like color axis, C min, so forth, right. So the C mid is intriguing to me. So I'm going to go ahead and search for C mid. So here they've got C min within the marker next to the values. And so I think that could be it. So next to the color scale, perhaps, or next to the color and the color scale, what we could do would be to do C mid equals zero. Let's see if that works. And so now that looks a little bit different, right? And so if we look at 0.5, that's now a reddish color rather than a bluish color. And negative 0.15 is a bluish color, right? So I think we were successful and that 0.2 is a little bit red, pinkish, but it's still pretty white. So I think that C mid was what we needed to associate 0.5 with a specific value from T diff. And again, it's a bit of troubleshooting and kind of searching for something that looks like what we want, getting the right Google terms. In this case, we were taking an example from Python and bring it over to R. I don't really know Python, but the plotly library in general is consistent across these different languages. The language like Python or R is the interface to the plotly library. And so I think a lot of the documentation will be similar ish, right? In a relative context. I don't think it's going to change anything necessarily, but I could also go ahead and put in my C min and C max. And so my C min is going to be T data, it's going to be the minimum of T data, dollar sign T diff. And C max then will be the max of that, right? So max on that. And like I said, I don't think that really changed much of anything. Finally, what I want to do is go ahead and remove the x and y coordinates. So back in the fundamentals page on plotly, there is a special button for axes. So the first thing we see is that this tutorial explains how to set properties over two dimensional Cartesian axes, namely, namely x and y, other kinds of subplots and axes are described in others. So for 3d axes, the axis object is seen. So let's go to 3d axes. And so then this is how to format the axes of 3d plots and are with plotly. Wonderful. And so again, what we have is that we would add to their figure a layout, right, where we have seen equals list x axis y axis z axis. Just kind of looking through here for examples where perhaps they've removed or change the axis values or labels. So let's start with removing the title. And so I think what I'll do is x, x, and y and z. So let's go ahead and copy this down. And I'll put that right up here above the plotly. And so my title for x will be nothing for y will be nothing. And z, I'm going to go ahead and remove that also, because I think if I leave in the year, that'll be pretty obvious. And so let's go ahead and run all that. So we saw down here is that we need the scene layout. So I'll copy that. And then I'll put that at the end of my plotly and put that on another line. So I now see that my x and y axis titles are gone. Let's go ahead and put these on separate lines. So it's easier to see what's going on. So as I look down through here, I see information about changing the grid color, changing the range, or types of values on the axes, but not exactly what I'm looking for, right? I'm looking to remove the grid line, as well as the tick labels. So I'll come in here to plotly, remove grid lines are how to horizontal grid lines and plotly are. So let's go down here, axes. This takes us back to the page that we were at before. So let's look for grid lines through here. So toggling axis grid lines, let's go to that. So again, this is the 2d page, not the 3d page, but maybe it'll work anyway. So there's an argument here of show grid equals false. So let's come back up here to our x, x, and we'll do show grid equals false. And let's put that in for our y as well. And so we've gotten rid of those grid lines. There's still a zero line in here. Coming back to this page, I noticed that there's also toggling axis zero lines. The next thing down. So that's zero line equals false. And then way back up here, we'll go ahead and add that as an argument for our zero line. So that zero line is gone. I do like having the grid lines there for the years, because again, it's easy when it's at an angle to kind of see where the line is. We do have those labels on the axes that I'm not a fan of. There's also toggling axis labels I see here. Axis tick marks can be disabled by sitting the show ticks axis property to false. So let's try that. So we'll come back up here. Show tick labels equals false. And we'll do false here. Great. So let's give this a shot. We've lost the x and y axis ticks. And that looks pretty good. That's exactly what I was hoping for. Great. So the next thing I want to do is save this to an HTML page so that I can look at it and perhaps put it up on a web page, including the blog post. Again, if you go down below in the description, there's a link to a blog post. I'll put the HTML version of this interactive over on the blog post so you can play around with it and see what it looks like. So to save this, I need to assign the plot object to a variable. I'll call that P. So the function that we want comes from a package called HTML widgets. So I can go ahead and do library HTML widgets. Make sure I've got that loaded. And then we'll do save widget. And we give it the widget. So the plot that we want P, as well as the file name. So I'm going to put that in quotes, I'm going to put it in figures. And we will then do climate spiral. Plotly.html. So there we go. Those lines are a bit thin. I would like them to be maybe a bit thicker. So it's kind of easier to see what's going on. If we came back to our code, you'll notice that we had width equals four here. Let's go ahead and put that up to 10 make it a bit thicker. So that looks a lot thicker and more robust. One thing I'm noticing, however, is that we seem somewhere have to lost the zeroing of our data at white. So here, April 1973, we've got 0.27. That that should be a reddish color. So I think what I'm going to do is come back in here and seem I know the things we're working before we added the C min and C max. So I'm going to go ahead and turn that off. And there's our April 1973 is that reddish color. So it looks good. One final thing I'm going to do because I can't help myself is to add the degree sign. So we'll come back up here before we call it a day. And here where we've got our T diff, we'll go ahead and add the Unicode, which is the U00B0 and then a C for degrees Celsius. And there we go. We now have our nicely labeled interactive plot. And again, I think this is really cool. This is basically what we had been making in the static version. But again, the ability to zoom in and put your cursor on different points to spin it to look at what's going on. I think it's just really powerful and really attractive. Of course, this only works in an HTML based environment like on a browser. This won't obviously work on a PDF. So if you're in science like me, you know, we can dream of the day when we can have interactive visuals on for our papers, right? But we're still stuck in this mode of thinking about a physical paper, a physical PDF, so to speak, rather than using the interactivity that comes with, you know, working with the internet. Anyway, I hope you found this interesting as another way to visualize these data. Again, with the GIF or an MP4, as the creator, we're giving the view to our audience. Although there is that animation here, we allow our audience to interact with the visual, which I think is just really powerful. And I think this is a really cool instance for three dimensions where we can put that time through the years on that z dimension. Let me know what you think down below in the comments. Please share this with your friends. And we'll see you next time for another episode of Code Club.