Creating Interaction Features
Interaction and polynomial features enrich a feature representation, especially for linear models.
Why a Model Depends on Its Columns
A model learns from the numbers you give it, so the way those numbers are laid out matters as much as the algorithm that consumes them. One way to enrich a feature representation, especially for linear models, is to add interaction features and polynomial features built from the original data. This kind of feature engineering shows up in statistical modeling and in many practical machine learning applications.
This article follows one concrete case. A linear model is fit to binned data, and the binned model has learned only a constant for each bin. The question is what happens when one more column is placed next to the bin columns. To answer it, you will trace the shape of the feature matrix at each step, because the shapes tell you what the model actually receives.
Tracing Shapes Step by Step
The source frames the representation as a trace. Each part of the combining expression produces an array, and the final array is the one the linear model receives. Rather than trusting the plot, follow the shape of the data after each construction step. The shape is a pair of numbers: the number of rows, which is the number of data points, and the number of columns, which is the number of features.
In this example the data set has 100 rows, one per data point. Before the stacking step, the original feature and the binned features each have 100 rows. The final printed result for the interaction representation is (100, 20): still 100 rows, and 20 columns. The rest of the explanation shows where those 20 columns come from.
The pipeline above shows the important rule for this section: the row count stays fixed at 100 throughout, and only the column count changes. If the number you print at the end does not match what you count from the two blocks, the problem lies in the stacking step or in the inputs passed to it, not in the regression. Checking shapes after each step lets you find that point quickly.
Bin Indicators and Product Features
The new data set has 20 features, and they come in two families. The first family records which bin each data point falls into. These are the bin indicator columns: for each bin, a column that marks whether the data point lies inside that bin. The second family takes the original x-axis feature and restricts it to each bin. Each product feature holds the original x-axis value inside its bin and zero everywhere else.
Both families are concatenated into one matrix, so the linear model sees a single set of columns and fits them together. Each bin contributes one indicator column and one product column, so the bin ranges are mirrored in both halves of the matrix. Reading a column position therefore tells you which bin it belongs to and whether it is an indicator or a restricted copy of the original value.
(100, 20)- Each block has 100 rows, one per data point, and 10 columns, one per bin.
np.hstackplaces the two blocks side by side and keeps the row count fixed.- The printed shape confirms 100 rows and 10 plus 10 equals 20 columns.
The code above uses placeholder zero arrays only to show the shapes; in the real representation the blocks hold the indicator and product values. The shape reasoning is what carries over: two blocks of 10 columns each, stacked horizontally over the same 100 rows, produce 20 columns.
Per-Bin Offsets and Slopes
Start with what the binned model already does. A linear model on binned data learns a constant value for each bin. Each bin is an interval of the original input, and every point that falls inside it receives the same predicted value. The result is a step-like shape: flat within each bin, with jumps at the bin edges. Such a model cannot express a slope on its own, because a constant has no slope.
When the linear regression is fit on the binned-plus-product representation, the result is a separate line for each bin. The lines do not have to share one slope. Each bin ends up with its own offset and its own slope. The model is still linear in its features, but the features now let the slope change from one bin to the next. In symbols, within bin $j$ the prediction takes the form $a_j + b_j x$, where $a_j$ is the offset and $b_j$ is the slope for that bin.
Adding the Original Feature Back In
A linear model can learn more than offsets. It can also learn slopes. One way to give the model a slope is to add the original feature back in, next to the binned columns. The original feature and the binned features are two blocks of columns describing the same 100 data points. The stacking step places them side by side, so each row carries the raw value together with its bin indicators.
The model now has a single coefficient that applies to the raw input value across all bins. That shared coefficient acts as one global slope, while the bin columns continue to supply a separate level for each bin. In the source example this produces an 11-dimensional data set: the original feature plus the 10 bin columns.
Counting columns after adding the original feature
The binned representation has 10 bin indicator columns over 100 rows. The original feature is one column over the same 100 rows. How many columns result after stacking the original feature with the bin columns, and what is the resulting shape?
- 1Count the original block
The original feature is a single column, so its block contributes 1 column.
- 2Count the bin block
The bin indicators contribute 10 columns, one per bin.
- 3Add the blocks
The stacked column count is the sum of the two blocks: 1 plus 10 equals 11.
- 4Check the rows
np.hstackkeeps the row count fixed, so the rows remain 100.
The stacked matrix has shape (100, 11), an 11-dimensional data set with one global slope term and 10 per-bin levels.
Notice that the stacking step adds columns, not rows. Adding columns changes what each data point looks like to the model, while the number of data points stays the same. That is why the printed row count remains 100 after stacking, and why the column count is always the sum of the column counts of the blocks being joined.
Common Mistakes When Stacking Feature Blocks
Common mistake Expecting the row count to change when columns are stacked
What it looks likeStacking a 100 by 10 block with a 100 by 10 block and expecting 200 rows.
Why it failsnp.hstackadds columns while keeping the row count fixed, because each row is still one data point.Do this insteadExpect 100 rows and 20 columns. Only the column count grows.
Common mistake Assuming bin indicators alone give the model a slope
What it looks likeFitting a model on the bin columns only and expecting a sloped line within each bin.
Why it failsA linear model on binned data learns a constant for each bin, which has no slope.
Do this insteadAdd product features to get per-bin slopes, or keep the original feature to get one global slope.
Common mistake Trusting the plot instead of the shapes
What it looks likeLooking only at the fitted lines and not checking that the stacked matrix has the expected number of columns.
Why it failsA plot can look reasonable even when the column count is wrong, so the error stays hidden.
Do this insteadPrint the shape after each construction step and compare it with the count you expect from the blocks.
Common mistake Forgetting that the original feature adds one column, not one per bin
Predicting Column Counts Before Stacking
What do you think happens?
You stack a binned block with 10 columns and a product block with 10 columns over the same 100 rows. What shape does the result have?
Reveal answer
(100, 20)
The row count stays at 100 because each row is still one data point. The column count is the sum of the two blocks, 10 plus 10, giving 20 columns.
A binned block has 100 rows and 10 columns. The original feature block has 100 rows and 1 column. Predict the shape after stacking them with np.hstack, then state how many columns come from each block and which block supplies the single global slope.
Hints
- Count the columns of each block separately before adding them.
- The original feature is the block that gives the model one shared coefficient across all bins.
- The row count should not change when you stack columns.
A colleague stacks two blocks of 100 rows, one with 10 columns and one with 11 columns, and reports a shape of (200, 21). Identify the step where the result departs from what you expected, and state the shape you should have seen.
Hints
- Check the row count first: stacking columns should keep it fixed.
- The correct column count is the sum of the two blocks.
Key Takeaways
- Interaction features enrich a feature matrix, which matters most for linear models.
- The binned-plus-product representation has 20 columns over 100 rows: 10 bin indicator columns and 10 product features, each holding the original x value inside its bin and zero elsewhere.
- A linear model on this representation fits a separate offset and slope for each bin, because the product features let the slope change between bins.
- Keeping the original feature alongside the bin columns adds one global slope, giving an 11-dimensional data set in the source example.
- Check the shape after each construction step:
np.hstackadds columns while keeping the row count fixed, so the first unexpected column count shows where the problem is.