Keeps is the easiest way to keep your hair. Get a doctor consultation and personalized treatment plan consisting of the only FDA-approved hair loss treatments, all without leaving your couch. Sign in - Google Accounts. Keep IT Simple provide design, implementation, transformation and ongoing support for Private or Public cloud environments running all Openstack, VMWare and Hyper-V. We provide certified AWS and Azure specialists to provide diligence in optimising your environments for maximum efficiency and the least possible cost.
Dimensions in data management and data warehousing contain relatively static data about such entities as geographical locations, customers, or products. Data captured by Slowly Changing Dimensions (SCDs) change slowly but unpredictably, rather than according to a regular schedule.[1]
Some scenarios can cause referential integrity problems.
For example, a database may contain a fact table that stores sales records. This fact table would be linked to dimensions by means of foreign keys. One of these dimensions may contain data about the company's salespeople: e.g., the regional offices in which they work. However, the salespeople are sometimes transferred from one regional office to another. For historical sales reporting purposes it may be necessary to keep a record of the fact that a particular sales person had been assigned to a particular regional office at an earlier date, whereas that sales person is now assigned to a different regional office.[clarification needed]
Keep It 1 0 46
Dealing with these issues involves SCD management methodologies referred to as Type 0 through 6. Type 6 SCDs are also sometimes called Hybrid SCDs.
Type 0: retain original[edit]
The Type 0 dimension attributes never change and are assigned to attributes that have durable values or are described as 'Original'. Examples: Date of Birth, Original Credit Score. Type 0 applies to most Date Dimension attributes.[2]
Type 1: overwrite[edit]
This method overwrites old with new data, and therefore does not track historical data.
Example of a supplier table:
Supplier_Key | Supplier_Code | Supplier_Name | Supplier_State |
---|---|---|---|
123 | ABC | Acme Supply Co | CA |
In the above example, Supplier_Code is the natural key and Supplier_Key is a surrogate key. Technically, the surrogate key is not necessary, since the row will be unique by the natural key (Supplier_Code).
If the supplier relocates the headquarters to Illinois the record would be overwritten:
Supplier_Key | Supplier_Code | Supplier_Name | Supplier_State |
---|---|---|---|
123 | ABC | Acme Supply Co | IL |
The disadvantage of the Type 1 method is that there is no history in the data warehouse. It has the advantage however that it's easy to maintain.
If one has calculated an aggregate table summarizing facts by state, it will need to be recalculated when the Supplier_State is changed.[1]
Type 2: add new row[edit]
This method tracks historical data by creating multiple records for a given natural key in the dimensional tables with separate surrogate keys and/or different version numbers. Unlimited history is preserved for each insert.
For example, if the supplier relocates to Illinois the version numbers will be incremented sequentially:
Supplier_Key | Supplier_Code | Supplier_Name | Supplier_State | Version |
---|---|---|---|---|
123 | ABC | Acme Supply Co | CA | 0 |
124 | ABC | Acme Supply Co | IL | 1 |
Another method is to add 'effective date' columns.
Supplier_Key | Supplier_Code | Supplier_Name | Supplier_State | Start_Date | End_Date |
---|---|---|---|---|---|
123 | ABC | Acme Supply Co | CA | 2000-01-01T00:00:00 | 2004-12-22T00:00:00 |
124 | ABC | Acme Supply Co | IL | 2004-12-22T00:00:00 | NULL |
The Start date/time of the second row is equal to the End date/time of the previous row. The null End_Date in row two indicates the current tuple version. A standardized surrogate high date (e.g. 9999-12-31) may instead be used as an end date, so that the field can be included in an index, and so that null-value substitution is not required when querying.
And a third method uses an effective date and a current flag.
Supplier_Key | Supplier_Code | Supplier_Name | Supplier_State | Effective_Date | Current_Flag |
---|---|---|---|---|---|
123 | ABC | Acme Supply Co | CA | 2000-01-01T00:00:00 | N |
124 | ABC | Acme Supply Co | IL | 2004-12-22T00:00:00 | Y |
The Current_Flag value of 'Y' indicates the current tuple version.
Transactions that reference a particular surrogate key (Supplier_Key) are then permanently bound to the time slices defined by that row of the slowly changing dimension table. An aggregate table summarizing facts by state continues to reflect the historical state, i.e. the state the supplier was in at the time of the transaction; no update is needed. To reference the entity via the natural key, it is necessary to remove the unique constraint making Referential integrity by DBMS impossible.
If there are retroactive changes made to the contents of the dimension, or if new attributes are added to the dimension (for example a Sales_Rep column) which have different effective dates from those already defined, then this can result in the existing transactions needing to be updated to reflect the new situation. This can be an expensive database operation, so Type 2 SCDs are not a good choice if the dimensional model is subject to frequent change.[1]
Type 3: add new attribute[edit]
This method tracks changes using separate columns and preserves limited history. The Type 3 preserves limited history as it is limited to the number of columns designated for storing historical data. The original table structure in Type 1 and Type 2 is the same but Type 3 adds additional columns. In the following example, an additional column has been added to the table to record the supplier's original state - only the previous history is stored.
Supplier_Key | Supplier_Code | Supplier_Name | Original_Supplier_State | Effective_Date | Current_Supplier_State |
---|---|---|---|---|---|
123 | ABC | Acme Supply Co | CA | 2004-12-22T00:00:00 | IL |
This record contains a column for the original state and current state—cannot track the changes if the supplier relocates a second time.
One variation of this is to create the field Previous_Supplier_State instead of Original_Supplier_State which would track only the most recent historical change.[1]
Type 4: add history table[edit]
The Type 4 method is usually referred to as using 'history tables', where one table keeps the current data, and an additional table is used to keep a record of some or all changes. Both the surrogate keys are referenced in the Fact table to enhance query performance.
For the above example, the original table name is Supplier and the history table is Supplier_History.
Supplier_key | Supplier_Code | Supplier_Name | Supplier_State |
---|---|---|---|
124 | ABC | Acme & Johnson Supply Co | IL |
Supplier_key | Supplier_Code | Supplier_Name | Supplier_State | Create_Date |
---|---|---|---|---|
123 | ABC | Acme Supply Co | CA | 2003-06-14T00:00:00 |
124 | ABC | Acme & Johnson Supply Co | IL | 2004-12-22T00:00:00 |
This method resembles how database audit tables and change data capture techniques function.
Type 5[edit]
The type 5 technique builds on the type 4 mini-dimension by embedding a 'current profile' mini-dimension key in the base dimension that's overwritten as a type 1 attribute. This approach, called type 5 because 4 + 1 equals 5, allows the currently-assigned mini-dimension attribute values to be accessed along with the base dimension's others without linking through a fact table. Logically, we typically represent the base dimension and current mini-dimension profile outrigger as a single table in the presentation layer. The outrigger attributes should have distinct column names, like 'Current Income Level,' to differentiate them from attributes in the mini-dimension linked to the fact table. The ETL team must update/overwrite the type 1 mini-dimension reference whenever the current mini-dimension changes over time. If the outrigger approach does not deliver satisfactory query performance, then the mini-dimension attributes could be physically embedded (and updated) in the base dimension.[3]
Type 6: combined approach[edit]
The Type 6 method combines the approaches of types 1, 2 and 3 (1 + 2 + 3 = 6). One possible explanation of the origin of the term was that it was coined by Ralph Kimball during a conversation with Stephen Pace from Kalido[citation needed]. Ralph Kimball calls this method 'Unpredictable Changes with Single-Version Overlay' in The Data Warehouse Toolkit.[1]
The Supplier table starts out with one record for our example supplier:
Supplier_Key | Row_Key | Supplier_Code | Supplier_Name | Current_State | Historical_State | Start_Date | End_Date | Current_Flag |
---|---|---|---|---|---|---|---|---|
123 | 1 | ABC | Acme Supply Co | CA | CA | 2000-01-01T00:00:00 | 9999-12-31T23:59:59 | Y |
The Current_State and the Historical_State are the same. The optional Current_Flag attribute indicates that this is the current or most recent record for this supplier.
https://bestlup448.weebly.com/best-free-roulette-app.html. When Acme Supply Company moves to Illinois, we add a new record, as in Type 2 processing, however a row key is included to ensure we have a unique key for each row:
Supplier_Key | Row_Key | Supplier_Code | Supplier_Name | Current_State | Historical_State | Start_Date | End_Date | Current_Flag |
---|---|---|---|---|---|---|---|---|
123 | 1 | ABC | Acme Supply Co | IL | CA | 2000-01-01T00:00:00 | 2004-12-22T00:00:00 | N |
123 | 2 | ABC | Acme Supply Co | IL | IL | 2004-12-22T00:00:00 | 9999-12-31T23:59:59 | Y |
We overwrite the Current_State information in the first record (Row_Key = 1) with the new information, as in Type 1 processing. We create a new record to track the changes, as in Type 2 processing. And we store the history in a second State column (Historical_State), which incorporates Type 3 processing.
For example, if the supplier were to relocate again, we would add another record to the Supplier dimension, and we would overwrite the contents of the Current_State column:
Supplier_Key | Row_Key | Supplier_Code | Supplier_Name | Current_State | Historical_State | Start_Date | End_Date | Current_Flag |
---|---|---|---|---|---|---|---|---|
123 | 1 | ABC | Acme Supply Co | NY | CA | 2000-01-01T00:00:00 | 2004-12-22T00:00:00 | N |
123 | 2 | ABC | Acme Supply Co | NY | IL | 2004-12-22T00:00:00 | 2008-02-04T00:00:00 | N |
123 | 3 | ABC | Acme Supply Co | NY | NY | 2008-02-04T00:00:00 | 9999-12-31T23:59:59 | Y |
Type 2 / type 6 fact implementation[edit]
Type 2 surrogate key with type 3 attribute[edit]
In many Type 2 and Type 6 SCD implementations, the surrogate key from the dimension is put into the fact table in place of the natural key when the fact data is loaded into the data repository.[1] The surrogate key is selected for a given fact record based on its effective date and the Start_Date and End_Date from the dimension table. This allows the fact data to be easily joined to the correct dimension data for the corresponding effective date.
Here is the Supplier table as we created it above using Type 6 Hybrid methodology:
Supplier_Key | Supplier_Code | Supplier_Name | Current_State | Historical_State | Start_Date | End_Date | Current_Flag |
---|---|---|---|---|---|---|---|
123 | ABC | Acme Supply Co | NY | CA | 2000-01-01T00:00:00 | 2004-12-22T00:00:00 | N |
124 | ABC | Acme Supply Co | NY | IL | 2004-12-22T00:00:00 | 2008-02-04T00:00:00 | N |
125 | ABC | Acme Supply Co | NY | NY | 2008-02-04T00:00:00 | 9999-12-31T23:59:59 | Y |
Once the Delivery table contains the correct Supplier_Key, it can easily be joined to the Supplier table using that key. The following SQL retrieves, for each fact record, the current supplier state and the state the supplier was located in at the time of the delivery:
Pure type 6 implementation[edit]
Keep It 100 Lyrics
Having a Type 2 surrogate key for each time slice can cause problems if the dimension is subject to change.[1]
A pure Type 6 implementation does not use this, but uses a Surrogate Key for each master data item (e.g. each unique supplier has a single surrogate key).
This avoids any changes in the master data having an impact on the existing transaction data.
It also allows more options when querying the transactions.
Here is the Supplier table using the pure Type 6 methodology:
Supplier_Key | Supplier_Code | Supplier_Name | Supplier_State | Start_Date | End_Date |
---|---|---|---|---|---|
456 | ABC | Acme Supply Co | CA | 2000-01-01T00:00:00 | 2004-12-22T00:00:00 |
456 | ABC | Acme Supply Co | IL | 2004-12-22T00:00:00 | 2008-02-04T00:00:00 |
456 | ABC | Acme Supply Co | NY | 2008-02-04T00:00:00 | 9999-12-31T23:59:59 |
The following example shows how the query must be extended to ensure a single supplier record is retrieved for each transaction.
A fact record with an effective date (Delivery_Date) of August 9, 2001 will be linked to Supplier_Code of ABC, with a Supplier_State of 'CA'. A fact record with an effective date of October 11, 2007 will also be linked to the same Supplier_Code ABC, but with a Supplier_State of 'IL'.
While more complex, there are a number of advantages of this approach, including:
- Referential integrity by DBMS is now possible, but one cannot use Supplier_Code as foreign key on Product table and using Supplier_Key as foreign key each product is tied on specific time slice.
- If there is more than one date on the fact (e.g. Order Date, Delivery Date, Invoice Payment Date) one can choose which date to use for a query.
- You can do 'as at now', 'as at transaction time' or 'as at a point in time' queries by changing the date filter logic.
- You don't need to reprocess the Fact table if there is a change in the dimension table (e.g. adding additional fields retrospectively which change the time slices, or if one makes a mistake in the dates on the dimension table one can correct them easily).
- You can introduce bi-temporal dates in the dimension table.
- You can join the fact to the multiple versions of the dimension table to allow reporting of the same information with different effective dates, in the same query.
The following example shows how a specific date such as '2012-01-01T00:00:00' (which could be the current datetime) can be used. Free keno games to play.
Both surrogate and natural key[edit]
An alternative implementation is to place both the surrogate key and the natural key into the fact table.[4] This allows the user to select the appropriate dimension records based on:
- the primary effective date on the fact record (above),
- the most recent or current information,
- any other date associated with the fact record.
This method allows more flexible links to the dimension, even if one has used the Type 2 approach instead of Type 6.
Here is the Supplier table as we might have created it using Type 2 methodology:
Supplier_Key | Supplier_Code | Supplier_Name | Supplier_State | Start_Date | End_Date | Current_Flag |
---|---|---|---|---|---|---|
123 | ABC | Acme Supply Co | CA | 2000-01-01T00:00:00 | 2004-12-22T00:00:00 | N |
124 | ABC | Acme Supply Co | IL | 2004-12-22T00:00:00 | 2008-02-04T00:00:00 | N |
125 | ABC | Acme Supply Co | NY | 2008-02-04T00:00:00 | 9999-12-31T23:59:59 | Y |
The following SQL retrieves the most current Supplier_Name and Supplier_State for each fact record:
If there are multiple dates on the fact record, the fact can be joined to the dimension using another date instead of the primary effective date. For instance, the Delivery table might have a primary effective date of Delivery_Date, but might also have an Order_Date associated with each record.
The following SQL retrieves the correct Supplier_Name and Supplier_State for each fact record based on the Order_Date:
Some cautions:
- Referential integrity by DBMS is not possible since there is not a unique key to create the relationship.
- If relationship is made with surrogate to solve problem above then one ends with entity tied to a specific time slice.
- If the join query is not written correctly, it may return duplicate rows and/or give incorrect answers.
- The date comparison might not perform well.
- Some Business Intelligence tools do not handle generating complex joins well.
- The ETL processes needed to create the dimension table needs to be carefully designed to ensure that there are no overlaps in the time periods for each distinct item of reference data.
Combining types[edit]
Different SCD Types can be applied to different columns of a table. For example, we can apply Type 1 to the Supplier_Name column and Type 2 to the Supplier_State column of the same table.
See also[edit]
- Entity–attribute–value model - Vertical
Notes[edit]
- ^ abcdefgKimball, Ralph; Ross, Margy. The Data Warehouse Toolkit: The Complete Guide to Dimensional Modeling.
- ^http://www.kimballgroup.com/2013/02/design-tip-152-slowly-changing-dimension-types-0-4-5-6-7/
- ^https://www.kimballgroup.com/2013/02/design-tip-152-slowly-changing-dimension-types-0-4-5-6-7/
- ^Ross, Margy; Kimball, Ralph (March 1, 2005). 'Slowly Changing Dimensions Are Not Always as Easy as 1, 2, 3'. Intelligent Enterprise.
References[edit]
- Bruce Ottmann, Chris Angus: Data processing system, US Patent Office, Patent Number 7,003,504. February 21, 2006
- Ralph Kimball:Kimball University: Handling Arbitrary Restatements of History[1]. December 9, 2007
There's a popular story that Gauss, mathematician extraordinaire, had a lazy teacher. The so-called educator wanted to keep the kids busy so he could take a nap; he asked the class to add the numbers 1 to 100.
Gauss approached with his answer: 5050. So soon? The teacher suspected a cheat, but no. Manual addition was for suckers, and Gauss found a formula to sidestep the problem:
Let's share a few explanations of this result and really understand it intuitively. For these examples we'll add 1 to 10, and then see how it applies for 1 to 100 (or 1 to any number).
Technique 1: Pair Numbers
Pairing numbers is a common approach to this problem. Instead of writing all the numbers in a single column, let's wrap the numbers around, like this:
An interesting pattern emerges: the sum of each column is 11. As the top row increases, the bottom row decreases, so the sum stays the same.
Because 1 is paired with 10 (our n), we can say that each column has (n+1). And how many pairs do we have? Well, we have 2 equal rows, we must have n/2 pairs.
which is the formula above.
Wait — what about an odd number of items?
Ah, I'm glad you brought it up. What if we are adding up the numbers 1 to 9? We don't have an even number of items to pair up. Many explanations will just give the explanation above and leave it at that. I won't.
Let's add the numbers 1 to 9, but instead of starting from 1, let's count from 0 instead:
By counting from 0, we get an 'extra item' (10 in total) so we can have an even number of rows. However, our formula will look a bit different.
See also[edit]
- Entity–attribute–value model - Vertical
Notes[edit]
- ^ abcdefgKimball, Ralph; Ross, Margy. The Data Warehouse Toolkit: The Complete Guide to Dimensional Modeling.
- ^http://www.kimballgroup.com/2013/02/design-tip-152-slowly-changing-dimension-types-0-4-5-6-7/
- ^https://www.kimballgroup.com/2013/02/design-tip-152-slowly-changing-dimension-types-0-4-5-6-7/
- ^Ross, Margy; Kimball, Ralph (March 1, 2005). 'Slowly Changing Dimensions Are Not Always as Easy as 1, 2, 3'. Intelligent Enterprise.
References[edit]
- Bruce Ottmann, Chris Angus: Data processing system, US Patent Office, Patent Number 7,003,504. February 21, 2006
- Ralph Kimball:Kimball University: Handling Arbitrary Restatements of History[1]. December 9, 2007
There's a popular story that Gauss, mathematician extraordinaire, had a lazy teacher. The so-called educator wanted to keep the kids busy so he could take a nap; he asked the class to add the numbers 1 to 100.
Gauss approached with his answer: 5050. So soon? The teacher suspected a cheat, but no. Manual addition was for suckers, and Gauss found a formula to sidestep the problem:
Let's share a few explanations of this result and really understand it intuitively. For these examples we'll add 1 to 10, and then see how it applies for 1 to 100 (or 1 to any number).
Technique 1: Pair Numbers
Pairing numbers is a common approach to this problem. Instead of writing all the numbers in a single column, let's wrap the numbers around, like this:
An interesting pattern emerges: the sum of each column is 11. As the top row increases, the bottom row decreases, so the sum stays the same.
Because 1 is paired with 10 (our n), we can say that each column has (n+1). And how many pairs do we have? Well, we have 2 equal rows, we must have n/2 pairs.
which is the formula above.
Wait — what about an odd number of items?
Ah, I'm glad you brought it up. What if we are adding up the numbers 1 to 9? We don't have an even number of items to pair up. Many explanations will just give the explanation above and leave it at that. I won't.
Let's add the numbers 1 to 9, but instead of starting from 1, let's count from 0 instead:
By counting from 0, we get an 'extra item' (10 in total) so we can have an even number of rows. However, our formula will look a bit different.
Notice that each column has a sum of n (not n+1, like before), since 0 and 9 are grouped. And instead of having exactly n items in 2 rows (for n/2 pairs total), we have n + 1 items in 2 rows (for (n + 1)/2 pairs total). If you plug these numbers in you get:
which is the same formula as before. It always bugged me that the same formula worked for both odd and even numbers – won't you get a fraction? Yep, you get the same formula, but for different reasons.
Technique 2: Use Two Rows
The above method works, but you handle odd and even numbers differently. Isn't there a better way? Yes.
Instead of looping the numbers around, let's write them in two rows:
Notice that we have 10 pairs, and each pair adds up to 10+1.
The total of all the numbers above is
But we only want the sum of one row, not both. So we divide the formula above by 2 and get:
Now this is cool (as cool as rows of numbers can be). It works for an odd or even number of items the same!
Technique 3: Make a Rectangle
I recently stumbled upon another explanation, a fresh approach to the old pairing explanation. Different explanations work better for different people, and I tend to like this one better.
Instead of writing out numbers, pretend we have beans. We want to add 1 bean to 2 beans to 3 beans… all the way up to 5 beans.
Sure, we could go to 10 or 100 beans, but with 5 you get the idea. How do we count the number of beans in our pyramid?
Well, the sum is clearly 1 + 2 + 3 + 4 + 5. But let's look at it a different way. Let's say we mirror our pyramid (I'll use 'o' for the mirrored beans), and then topple it over:
Cool, huh? In case you're wondering whether it 'really' lines up, it does. Take a look at the bottom row of the regular pyramid, with 5′x (and 1 o). The next row of the pyramid has 1 less x (4 total) and 1 more o (2 total) to fill the gap. Just like the pairing, one side is increasing, and the other is decreasing.
Now for the explanation: How many beans do we have total? Well, that's just the area of the rectangle.
We have n rows (we didn't change the number of rows in the pyramid), and our collection is (n + 1) units wide, since 1 'o' is paired up with all the 'x's.
Notice that this time, we don't care about n being odd or even – the total area formula works out just fine. If n is odd, we'll have an even number of items (n+1) in each row.
But of course, we don't want the total area (the number of x's and o's), we just want the number of x's. Since we doubled the x's to get the o's, the x's by themselves are just half of the total area:
And we're back to our original formula. Again, the number of x's in the pyramid = 1 + 2 + 3 + 4 + 5, or the sum from 1 to n.
Technique 4: Average it out
We all know that
average = sum / number of items
Active dock 1 1 1999.
which we can rewrite to
sum = average * number of items
So let's figure out the sum. If we have 100 numbers (1…100), then we clearly have 100 items. That was easy.
To get the average, notice that the numbers are all equally distributed. For every big number, there's a small number on the other end. Let's look at a small set:
The average is 2. 2 is already in the middle, and 1 and 3 'cancel out' so their average is 2.
For an even number of items
the average is between 2 and 3 – it's 2.5. Even though we have a fractional average, this is ok — since we have an even number of items, when we multiply the average by the count that ugly fraction will disappear.
Notice in both cases, 1 is on one side of the average and N is equally far away on the other. So, we can say the average of the entire set is actually just the average of 1 and n: (1 + n)/2.
Putting this into our formula
And voila! We have a fourth way of thinking about our formula.
So why is this useful?
Three reasons:
1) Adding up numbers quickly can be useful for estimation. Notice that the formula expands to this:
Serial 2 0 0 3. Let's say you want to add the numbers from 1 to 1000: suppose you get 1 additional visitor to your site each day – how many total visitors will you have after 1000 days? Since thousand squared = 1 million, we get million / 2 + 1000/2 = 500,500
.
2) This concept of adding numbers 1 to N shows up in other places, like figuring out the probability for the birthday paradox. Having a firm grasp of this formula will help your understanding in many areas.
3) Most importantly, this example shows there are many ways to understand a formula. Maybe you like the pairing method, maybe you prefer the rectangle technique, or maybe there's another explanation that works for you. Don't give up when you don't understand — try to find another explanation that works. Happy math.
By the way, there are more details about the history of this story and the technique Gauss may have used.
Variations
Instead of 1 to n, how about 5 to n?
Start with the regular formula (1 + 2 + 3 + … + n = n * (n + 1) / 2) and subtract off the part you don't want (1 + 2 + 3 + 4 = 4 * (4 + 1) / 2 = 10).
And for any starting number a:
We want to get rid of every number from 1 up to a – 1.
How about even numbers, like 2 + 4 + 6 + 8 + … + n?
Just double the regular formula. To add evens from 2 to 50, find 1 + 2 + 3 + 4 … + 25 and double it:
So, to get the evens from 2 to 50 you'd do 25 * (25 + 1) = 650
How about odd numbers, like 1 + 3 + 5 + 7 + … + n?
That's the same as the even formula, except each number is 1 less than its counterpart (we have 1 instead of 2, 3 instead of 4, and so on). We get the next biggest even number (n + 1) and take off the extra (n + 1)/2 '-1″ items:
Dmg canvas 2 3 2 – create custom disk images. To add 1 + 3 + 5 + … 13, get the next biggest even (n + 1 = 14) and do
Combinations: evens and offset
Let's say you want the evens from 50 + 52 + 54 + 56 + … 100. Find all the evens
and subtract off the ones you don't want
So, the sum from 50 + 52 + … 100 = (50 * 51) – (24 * 25) = 1950
Phew! Hope this helps.
Ruby nerds: you can check this using
Javascript geeks, do this:
Join Over 450k Monthly Readers
Enjoy the article? There's plenty more to help you build a lasting, intuitive understanding of math. Join the newsletter for bonus content and the latest updates.