{"id":2476,"date":"2026-02-02T07:39:33","date_gmt":"2026-02-02T07:39:33","guid":{"rendered":"https:\/\/demo.materiamedica.net\/demo6\/?p=2476"},"modified":"2026-02-02T07:39:33","modified_gmt":"2026-02-02T07:39:33","slug":"chapter-9-numpy-array-copy-vs-view","status":"publish","type":"post","link":"https:\/\/demo.materiamedica.net\/demo6\/chapter-9-numpy-array-copy-vs-view\/","title":{"rendered":"Chapter 9: NumPy Array Copy vs View"},"content":{"rendered":"<p dir=\"auto\"><strong>NumPy Array Copy vs View<\/strong> \u2014 written exactly like a patient teacher sitting next to you, showing examples, explaining <strong>why<\/strong> this confuses almost everyone, what actually happens in memory, and how to avoid the most common painful bugs.<\/p>\n<p dir=\"auto\">This topic is <strong>very important<\/strong> \u2014 misunderstanding copy vs view is one of the top 3 reasons beginners get surprised or wrong results in NumPy.<\/p>\n<div dir=\"auto\">\n<div data-testid=\"code-block\">\n<div>\n<div>Python<\/div>\n<div>\n<pre tabindex=\"0\"><code>import numpy as np<\/code><\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<h3 dir=\"auto\">The Core Idea \u2014 One Sentence Version<\/h3>\n<p dir=\"auto\">When you create a new array from an existing one, NumPy tries <strong>very hard not to copy the data<\/strong> \u2014 because copying is slow and uses extra memory.<\/p>\n<p dir=\"auto\">So most operations give you a <strong>view<\/strong> (a different way to look at the <strong>same data<\/strong> in memory) instead of a real copy.<\/p>\n<p dir=\"auto\"><strong>View<\/strong> = same data, different window \/ name <strong>Copy<\/strong> = completely new, independent data<\/p>\n<p dir=\"auto\">If you change something through a <strong>view<\/strong>, the original array also changes \u2014 this is what surprises most people.<\/p>\n<h3 dir=\"auto\">1. The Classic Trap \u2014 Everyone Falls Into This<\/h3>\n<div dir=\"auto\">\n<div data-testid=\"code-block\">\n<div>\n<div>Python<\/div>\n<div>\n<pre tabindex=\"0\"><code>a = np.array([10, 20, 30, 40, 50, 60])\r\nprint(\"Original a:\", a)\r\n\r\nb = a[2:5]          # \u2190 slicing \u2192 this is a VIEW\r\nprint(\"b (slice): \", b)\r\n\r\nb[0] = 999\r\nprint(\"b after change:\", b)\r\nprint(\"Original a after change:\", a)   # \u2190 surprise!<\/code><\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<p dir=\"auto\">Output:<\/p>\n<div dir=\"auto\">\n<div data-testid=\"code-block\">\n<div>\n<div>text<\/div>\n<div>\n<pre tabindex=\"0\"><code>Original a:      [10 20 30 40 50 60]\r\nb (slice):       [30 40 50]\r\nb after change:  [999  40  50]\r\nOriginal a after change: [ 10  20 999  40  50  60]<\/code><\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<p dir=\"auto\"><strong>Why did a change?<\/strong> Because b is <strong>not a copy<\/strong> \u2014 it is just a different way of looking at <strong>the same memory block<\/strong>.<\/p>\n<p dir=\"auto\">b starts at position 2 of a, takes 3 elements, but the data is <strong>not duplicated<\/strong>.<\/p>\n<h3 dir=\"auto\">2. Operations That Usually Return a VIEW<\/h3>\n<p dir=\"auto\">These almost always give you a view (same data in memory):<\/p>\n<div>\n<div dir=\"auto\">\n<table dir=\"auto\">\n<thead>\n<tr>\n<th data-col-size=\"lg\">Operation<\/th>\n<th data-col-size=\"md\">Example<\/th>\n<th data-col-size=\"xs\">Returns<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td data-col-size=\"lg\">Basic slicing<\/td>\n<td data-col-size=\"md\">a[3:8], a[:10], a[::2]<\/td>\n<td data-col-size=\"xs\"><strong>View<\/strong><\/td>\n<\/tr>\n<tr>\n<td data-col-size=\"lg\">Reverse slicing<\/td>\n<td data-col-size=\"md\">a[::-1]<\/td>\n<td data-col-size=\"xs\"><strong>View<\/strong><\/td>\n<\/tr>\n<tr>\n<td data-col-size=\"lg\">Reshape (when possible)<\/td>\n<td data-col-size=\"md\">a.reshape(4, -1)<\/td>\n<td data-col-size=\"xs\"><strong>View<\/strong> (most cases)<\/td>\n<\/tr>\n<tr>\n<td data-col-size=\"lg\">Transpose<\/td>\n<td data-col-size=\"md\">a.T, matrix.T<\/td>\n<td data-col-size=\"xs\"><strong>View<\/strong><\/td>\n<\/tr>\n<tr>\n<td data-col-size=\"lg\">Swapaxes, rollaxis<\/td>\n<td data-col-size=\"md\">np.swapaxes(a, 0, 1)<\/td>\n<td data-col-size=\"xs\"><strong>View<\/strong><\/td>\n<\/tr>\n<tr>\n<td data-col-size=\"lg\">Simple indexing (single element)<\/td>\n<td data-col-size=\"md\">a[5]<\/td>\n<td data-col-size=\"xs\">scalar (not array)<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<div><\/div>\n<\/div>\n<\/div>\n<p dir=\"auto\"><strong>Very important rule for beginners:<\/strong><\/p>\n<blockquote dir=\"auto\">\n<p dir=\"auto\">If you used only <strong>numbers and :<\/strong> in the indexing \u2192 almost always <strong>view<\/strong><\/p>\n<\/blockquote>\n<h3 dir=\"auto\">3. Operations That Usually Return a COPY<\/h3>\n<p dir=\"auto\">These usually create a <strong>new, independent array<\/strong>:<\/p>\n<div>\n<div dir=\"auto\">\n<table dir=\"auto\">\n<thead>\n<tr>\n<th data-col-size=\"lg\">Operation<\/th>\n<th data-col-size=\"lg\">Example<\/th>\n<th data-col-size=\"xs\">Returns<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td data-col-size=\"lg\">Boolean indexing<\/td>\n<td data-col-size=\"lg\">a[a &gt; 0], a[mask]<\/td>\n<td data-col-size=\"xs\"><strong>Copy<\/strong><\/td>\n<\/tr>\n<tr>\n<td data-col-size=\"lg\">Fancy indexing (list\/array)<\/td>\n<td data-col-size=\"lg\">a[[1,4,7]], a[idx]<\/td>\n<td data-col-size=\"xs\"><strong>Copy<\/strong><\/td>\n<\/tr>\n<tr>\n<td data-col-size=\"lg\">Advanced indexing (multiple lists)<\/td>\n<td data-col-size=\"lg\">a[[0,2], [1,3]]<\/td>\n<td data-col-size=\"xs\"><strong>Copy<\/strong><\/td>\n<\/tr>\n<tr>\n<td data-col-size=\"lg\">.copy() method<\/td>\n<td data-col-size=\"lg\">a.copy()<\/td>\n<td data-col-size=\"xs\"><strong>Copy<\/strong><\/td>\n<\/tr>\n<tr>\n<td data-col-size=\"lg\">np.copy() function<\/td>\n<td data-col-size=\"lg\">np.copy(a)<\/td>\n<td data-col-size=\"xs\"><strong>Copy<\/strong><\/td>\n<\/tr>\n<tr>\n<td data-col-size=\"lg\">np.array(a)<\/td>\n<td data-col-size=\"lg\">np.array(a)<\/td>\n<td data-col-size=\"xs\"><strong>Copy<\/strong><\/td>\n<\/tr>\n<tr>\n<td data-col-size=\"lg\">Most np. functions that create new data<\/td>\n<td data-col-size=\"lg\">np.concatenate, np.vstack, etc.<\/td>\n<td data-col-size=\"xs\"><strong>Copy<\/strong><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<div><\/div>\n<\/div>\n<\/div>\n<p dir=\"auto\"><strong>Quick memory rule:<\/strong><\/p>\n<blockquote dir=\"auto\">\n<p dir=\"auto\">If you used <strong>lists, arrays, or boolean masks<\/strong> inside the brackets \u2192 usually <strong>copy<\/strong><\/p>\n<\/blockquote>\n<h3 dir=\"auto\">4. How to Force a Real Copy (Safest Ways)<\/h3>\n<div dir=\"auto\">\n<div data-testid=\"code-block\">\n<div>\n<div>Python<\/div>\n<div>\n<pre tabindex=\"0\"><code>original = np.arange(12).reshape(3, 4)\r\n\r\n# The three safest, clearest ways:\r\nsafe1 = original.copy()              # most readable\r\nsafe2 = np.copy(original)\r\nsafe3 = original.astype(original.dtype)   # also creates copy\r\n\r\n# Slicing + copy (very common pattern)\r\nsafe_slice = original[1:3, 2:5].copy()<\/code><\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<p dir=\"auto\"><strong>Rule to live by:<\/strong><\/p>\n<blockquote dir=\"auto\">\n<p dir=\"auto\">Whenever you plan to <strong>modify<\/strong> the new array and you <strong>don\u2019t<\/strong> want the original to change \u2192 always add .copy()<\/p>\n<\/blockquote>\n<h3 dir=\"auto\">5. Realistic Examples \u2014 When This Bites People<\/h3>\n<p dir=\"auto\"><strong>Example 1 \u2013 Preprocessing subset of columns<\/strong><\/p>\n<div dir=\"auto\">\n<div data-testid=\"code-block\">\n<div>\n<div>Python<\/div>\n<div>\n<pre tabindex=\"0\"><code>X = np.random.randn(1000, 20)\r\n\r\n# Wrong way (very common mistake)\r\nimportant = X[:, [0, 3, 7, 12, 19]]     # \u2190 this is a COPY (fancy indexing)\r\nimportant -= important.mean(axis=0)      # OK, original X unchanged\r\n\r\n# But if you do slicing this way:\r\nsubset = X[:, 0:5]                       # \u2190 this is a VIEW\r\nsubset -= subset.mean(axis=0)            # \u2190 modifies original X columns 0\u20134 !<\/code><\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<p dir=\"auto\"><strong>Example 2 \u2013 Image cropping (classic)<\/strong><\/p>\n<div dir=\"auto\">\n<div data-testid=\"code-block\">\n<div>\n<div>Python<\/div>\n<div>\n<pre tabindex=\"0\"><code>img = np.random.randint(0, 256, (512, 512, 3), dtype=np.uint8)\r\n\r\ncrop = img[100:400, 150:450, :]       # \u2190 VIEW\r\ncrop[:] = 0                            # \u2190 you just painted part of original image black!<\/code><\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<p dir=\"auto\"><strong>Correct:<\/strong><\/p>\n<div dir=\"auto\">\n<div data-testid=\"code-block\">\n<div>\n<div>Python<\/div>\n<div>\n<pre tabindex=\"0\"><code>crop_safe = img[100:400, 150:450, :].copy()\r\ncrop_safe[:] = 0                       # original img unchanged<\/code><\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<h3 dir=\"auto\">6. Quick Test You Can Use to Check<\/h3>\n<div dir=\"auto\">\n<div data-testid=\"code-block\">\n<div>\n<div>Python<\/div>\n<div>\n<pre tabindex=\"0\"><code>def is_view(a, b):\r\n    return a.base is b.base and a.base is not None\r\n\r\na = np.arange(20)\r\nb = a[5:15]\r\nc = a[[5,6,7,8,9]]\r\n\r\nprint(is_view(a, b))     # True  \u2190 view\r\nprint(is_view(a, c))     # False \u2190 copy<\/code><\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<p dir=\"auto\">But honestly, most people just remember:<\/p>\n<blockquote dir=\"auto\">\n<p dir=\"auto\">If I didn\u2019t write .copy(), and I used only :, assume it\u2019s a <strong>view<\/strong>.<\/p>\n<\/blockquote>\n<h3 dir=\"auto\">Summary Table \u2013 Copy vs View Cheat Sheet<\/h3>\n<div>\n<div dir=\"auto\">\n<table dir=\"auto\">\n<thead>\n<tr>\n<th data-col-size=\"lg\">Situation \/ Operation<\/th>\n<th data-col-size=\"xs\">Usually Copy or View?<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td data-col-size=\"lg\">b = a<\/td>\n<td data-col-size=\"xs\">View (just another name)<\/td>\n<\/tr>\n<tr>\n<td data-col-size=\"lg\">b = a[3:10]<\/td>\n<td data-col-size=\"xs\"><strong>View<\/strong><\/td>\n<\/tr>\n<tr>\n<td data-col-size=\"lg\">b = a[::2]<\/td>\n<td data-col-size=\"xs\"><strong>View<\/strong><\/td>\n<\/tr>\n<tr>\n<td data-col-size=\"lg\">b = a.T<\/td>\n<td data-col-size=\"xs\"><strong>View<\/strong><\/td>\n<\/tr>\n<tr>\n<td data-col-size=\"lg\">b = a.reshape(&#8230;)<\/td>\n<td data-col-size=\"xs\"><strong>View<\/strong> (most cases)<\/td>\n<\/tr>\n<tr>\n<td data-col-size=\"lg\">b = a[a &gt; 0]<\/td>\n<td data-col-size=\"xs\"><strong>Copy<\/strong><\/td>\n<\/tr>\n<tr>\n<td data-col-size=\"lg\">b = a[[1,4,7]]<\/td>\n<td data-col-size=\"xs\"><strong>Copy<\/strong><\/td>\n<\/tr>\n<tr>\n<td data-col-size=\"lg\">b = a[np.ix_([1,3],[2,5])]<\/td>\n<td data-col-size=\"xs\"><strong>Copy<\/strong><\/td>\n<\/tr>\n<tr>\n<td data-col-size=\"lg\">b = a.copy()<\/td>\n<td data-col-size=\"xs\"><strong>Copy<\/strong><\/td>\n<\/tr>\n<tr>\n<td data-col-size=\"lg\">You want to modify b without changing a<\/td>\n<td data-col-size=\"xs\"><strong>Always use .copy()<\/strong><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<div><\/div>\n<\/div>\n<\/div>\n<h3 dir=\"auto\">Final Advice from Teacher to Student<\/h3>\n<p dir=\"auto\"><strong>Golden rules to live by:<\/strong><\/p>\n<ol dir=\"auto\">\n<li>If you are going to <strong>modify<\/strong> the new array \u2192 <strong>always<\/strong> write .copy()<\/li>\n<li>If you are only <strong>reading<\/strong> or doing calculations that create new arrays \u2192 view is fine (and faster)<\/li>\n<li>When in doubt \u2192 .copy()<\/li>\n<li>Boolean indexing and fancy indexing are usually safe (they copy), slicing is usually dangerous (it views)<\/li>\n<\/ol>\n<p dir=\"auto\">Would you like to go deeper into any of these next?<\/p>\n<ul dir=\"auto\">\n<li>How to debug copy\/view issues<\/li>\n<li>Memory layout and why views are so fast<\/li>\n<li>Situations where reshape \/ transpose creates copy<\/li>\n<li>Realistic data cleaning \/ preprocessing examples with copy\/view traps<\/li>\n<\/ul>\n<p dir=\"auto\">Just tell me what you want to focus on now! \ud83d\ude0a<\/p>\n","protected":false},"excerpt":{"rendered":"<p>NumPy Array Copy vs View \u2014 written exactly like a patient teacher sitting next to you, showing examples, explaining why this confuses almost everyone, what actually happens in memory, and how to avoid the&#46;&#46;&#46;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[75],"tags":[],"class_list":["post-2476","post","type-post","status-publish","format-standard","hentry","category-numpy"],"_links":{"self":[{"href":"https:\/\/demo.materiamedica.net\/demo6\/wp-json\/wp\/v2\/posts\/2476","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/demo.materiamedica.net\/demo6\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/demo.materiamedica.net\/demo6\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/demo.materiamedica.net\/demo6\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/demo.materiamedica.net\/demo6\/wp-json\/wp\/v2\/comments?post=2476"}],"version-history":[{"count":1,"href":"https:\/\/demo.materiamedica.net\/demo6\/wp-json\/wp\/v2\/posts\/2476\/revisions"}],"predecessor-version":[{"id":2477,"href":"https:\/\/demo.materiamedica.net\/demo6\/wp-json\/wp\/v2\/posts\/2476\/revisions\/2477"}],"wp:attachment":[{"href":"https:\/\/demo.materiamedica.net\/demo6\/wp-json\/wp\/v2\/media?parent=2476"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/demo.materiamedica.net\/demo6\/wp-json\/wp\/v2\/categories?post=2476"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/demo.materiamedica.net\/demo6\/wp-json\/wp\/v2\/tags?post=2476"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}