{"id":2529,"date":"2026-02-02T10:00:25","date_gmt":"2026-02-02T10:00:25","guid":{"rendered":"https:\/\/demo.materiamedica.net\/demo6\/?p=2529"},"modified":"2026-02-02T10:00:25","modified_gmt":"2026-02-02T10:00:25","slug":"numpy-ufunc","status":"publish","type":"post","link":"https:\/\/demo.materiamedica.net\/demo6\/numpy-ufunc\/","title":{"rendered":"NumPy ufunc"},"content":{"rendered":"<h3 dir=\"auto\">1. What is a ufunc really? (honest explanation)<\/h3>\n<p dir=\"auto\"><strong>ufunc = universal function<\/strong><\/p>\n<p dir=\"auto\">A ufunc is a NumPy function that:<\/p>\n<ul dir=\"auto\">\n<li>operates <strong>element-by-element<\/strong> on entire arrays<\/li>\n<li>does it <strong>very fast<\/strong> (written in compiled C code)<\/li>\n<li>supports <strong>broadcasting<\/strong> automatically<\/li>\n<li>can be applied to scalars, arrays, or mixtures<\/li>\n<li>usually returns a <strong>new array<\/strong> (rarely modifies in place)<\/li>\n<\/ul>\n<p dir=\"auto\"><strong>Most important sentence to remember<\/strong>:<\/p>\n<blockquote dir=\"auto\">\n<p dir=\"auto\">In NumPy, almost every mathematical operation you want to do on arrays is a ufunc.<\/p>\n<\/blockquote>\n<p dir=\"auto\">Examples of very common ufuncs:<\/p>\n<div dir=\"auto\">\n<div data-testid=\"code-block\">\n<div>\n<div>text<\/div>\n<div>\n<pre tabindex=\"0\"><code>+   -   *   \/   \/\/   %   **   &amp;   |   ^   ~   &gt;   &gt;=   &lt;   &lt;=   ==   !=\r\nnp.sin  np.cos  np.tan  np.exp  np.log  np.log10  np.sqrt  np.abs\r\nnp.floor  np.ceil  np.round  np.trunc\r\nnp.maximum  np.minimum  np.fmax  np.fmin<\/code><\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<h3 dir=\"auto\">2. Why ufuncs are so much faster than Python loops<\/h3>\n<p dir=\"auto\">Classic beginner mistake:<\/p>\n<div dir=\"auto\">\n<div data-testid=\"code-block\">\n<div>\n<div>Python<\/div>\n<div>\n<pre tabindex=\"0\"><code># Slow Python way\r\na = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]\r\nresult = []\r\nfor x in a:\r\n    result.append(x ** 2 + 3 * x - 7)<\/code><\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<p dir=\"auto\"><strong>NumPy ufunc way<\/strong> \u2014 10\u2013100\u00d7 faster, cleaner, more readable:<\/p>\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([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])\r\nresult = a ** 2 + 3 * a - 7\r\nprint(result)\r\n# [ -3   3  11  21  33  47  63  81 101 123]<\/code><\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<p dir=\"auto\"><strong>Why is it so fast?<\/strong><\/p>\n<ul dir=\"auto\">\n<li>No Python loop overhead<\/li>\n<li>Vectorized execution (SIMD instructions on CPU)<\/li>\n<li>Contiguous memory layout<\/li>\n<li>Compiled C\/Fortran code under the hood<\/li>\n<\/ul>\n<h3 dir=\"auto\">3. The most important ufuncs you will use every day<\/h3>\n<h4 dir=\"auto\">Arithmetic ufuncs<\/h4>\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.arange(1, 11)\r\nb = np.arange(10, 0, -1)\r\n\r\nprint(\"a =\", a)\r\nprint(\"b =\", b)\r\n\r\nprint(\"a + b =\", a + b)\r\nprint(\"a - b =\", a - b)\r\nprint(\"a * b =\", a * b)\r\nprint(\"a \/ b =\", a \/ b.round(2))      # floating point\r\nprint(\"a \/\/ b =\", a \/\/ b)             # floor division\r\nprint(\"a ** 2 =\", a ** 2)\r\nprint(\"a % 3 =\", a % 3)<\/code><\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<h4 dir=\"auto\">Mathematical ufuncs<\/h4>\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.array([0, np.pi\/6, np.pi\/4, np.pi\/3, np.pi\/2])\r\n\r\nprint(\"sin(x) =\", np.sin(x).round(3))\r\nprint(\"cos(x) =\", np.cos(x).round(3))\r\nprint(\"tan(x) =\", np.tan(x).round(3))\r\nprint(\"exp(x) =\", np.exp(x).round(2))\r\nprint(\"log(x+1) =\", np.log1p(x).round(3))   # log(1+x) \u2014 safer for small x\r\nprint(\"sqrt(x) =\", np.sqrt(x).round(3))\r\nprint(\"abs(x-2) =\", np.abs(x-2).round(3))<\/code><\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<h4 dir=\"auto\">Rounding &amp; truncation<\/h4>\n<div dir=\"auto\">\n<div data-testid=\"code-block\">\n<div>\n<div>Python<\/div>\n<div>\n<pre tabindex=\"0\"><code>vals = np.array([-1.7, -1.4, -0.6, 0.3, 1.2, 2.7, 3.1])\r\n\r\nprint(\"floor  :\", np.floor(vals))\r\nprint(\"ceil   :\", np.ceil(vals))\r\nprint(\"trunc  :\", np.trunc(vals))\r\nprint(\"round  :\", np.round(vals))\r\nprint(\"rint   :\", np.rint(vals))       # round to nearest even<\/code><\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<h4 dir=\"auto\">Comparison ufuncs \u2014 return boolean arrays<\/h4>\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([3, 8, 1, 9, 4, 6, 2, 7])\r\nb = np.array([5, 2, 7, 1, 6, 3, 8, 4])\r\n\r\nprint(\"a &gt; b  :\", a &gt; b)\r\nprint(\"a == b :\", a == b)\r\nprint(\"a &gt;= 5 :\", a &gt;= 5)<\/code><\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<h3 dir=\"auto\">4. Broadcasting \u2014 the superpower of ufuncs<\/h3>\n<p dir=\"auto\">ufuncs automatically \u201cstretch\u201d smaller arrays to match larger ones.<\/p>\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.arange(12).reshape(3, 4)\r\nprint(a)\r\n\r\n# Add scalar \u2192 broadcasts to whole array\r\nprint(\"\\na + 100:\")\r\nprint(a + 100)\r\n\r\n# Add 1D row vector\r\nprint(\"\\na + [100, 200, 300, 400]:\")\r\nprint(a + np.array([100, 200, 300, 400]))\r\n\r\n# Add column vector (needs shape (3,1))\r\nprint(\"\\na + [[10],[20],[30]]:\")\r\nprint(a + np.array([[10],[20],[30]]))<\/code><\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<h3 dir=\"auto\">5. Very common realistic patterns (you will write these often)<\/h3>\n<p dir=\"auto\"><strong>Pattern 1 \u2013 Normalize data<\/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(10000, 20)   # 10,000 samples \u00d7 20 features\r\n\r\n# Standardize each column (mean=0, std=1)\r\nX_norm = (X - X.mean(axis=0)) \/ X.std(axis=0)<\/code><\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<p dir=\"auto\"><strong>Pattern 2 \u2013 Clip values to valid range<\/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>pixels = np.random.randint(-50, 300, size=(100, 100))\r\n\r\nclean_pixels = np.clip(pixels, 0, 255)<\/code><\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<p dir=\"auto\"><strong>Pattern 3 \u2013 Replace invalid values<\/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>measurements = np.random.normal(100, 15, 5000)\r\nmeasurements[measurements &lt; 0] = np.nan   # or 0, or mean, etc.<\/code><\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<p dir=\"auto\"><strong>Pattern 4 \u2013 Element-wise conditions<\/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>scores = np.random.randint(40, 101, 200)\r\n\r\npassed = scores &gt;= 60\r\nprint(\"Pass rate:\", passed.mean().round(3))\r\n\r\n# Replace failing with 0\r\nscores[~passed] = 0<\/code><\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<p dir=\"auto\"><strong>Pattern 5 \u2013 Vectorized math on images<\/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, (300, 400, 3), dtype=np.uint8)\r\n\r\n# Increase brightness\r\nbrighter = np.clip(img + 40, 0, 255)\r\n\r\n# Convert to grayscale (simple average)\r\ngray = np.mean(img, axis=2).astype(np.uint8)<\/code><\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<h3 dir=\"auto\">Summary \u2013 ufunc Quick Reference<\/h3>\n<div>\n<div dir=\"auto\">\n<table dir=\"auto\">\n<thead>\n<tr>\n<th data-col-size=\"sm\">Operation type<\/th>\n<th data-col-size=\"lg\">Typical ufuncs<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td data-col-size=\"sm\">Arithmetic<\/td>\n<td data-col-size=\"lg\">+, -, *, \/, \/\/, %, **, np.add, np.subtract\u2026<\/td>\n<\/tr>\n<tr>\n<td data-col-size=\"sm\">Trigonometric<\/td>\n<td data-col-size=\"lg\">np.sin, np.cos, np.tan, np.arcsin\u2026<\/td>\n<\/tr>\n<tr>\n<td data-col-size=\"sm\">Exponential &amp; log<\/td>\n<td data-col-size=\"lg\">np.exp, np.log, np.log10, np.log1p, np.expm1<\/td>\n<\/tr>\n<tr>\n<td data-col-size=\"sm\">Rounding<\/td>\n<td data-col-size=\"lg\">np.floor, np.ceil, np.round, np.trunc, np.rint<\/td>\n<\/tr>\n<tr>\n<td data-col-size=\"sm\">Comparison<\/td>\n<td data-col-size=\"lg\">&gt;, &gt;=, &lt;, &lt;=, ==, !=, np.greater, np.equal\u2026<\/td>\n<\/tr>\n<tr>\n<td data-col-size=\"sm\">Maximum \/ minimum<\/td>\n<td data-col-size=\"lg\">np.maximum, np.minimum, np.fmax, np.fmin<\/td>\n<\/tr>\n<tr>\n<td data-col-size=\"sm\">Absolute \/ sign<\/td>\n<td data-col-size=\"lg\">np.abs, np.sign, np.negative<\/td>\n<\/tr>\n<tr>\n<td data-col-size=\"sm\">Specialized<\/td>\n<td data-col-size=\"lg\">np.sqrt, np.cbrt, np.square, np.reciprocal<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<div><\/div>\n<\/div>\n<\/div>\n<h3 dir=\"auto\">Final teacher advice<\/h3>\n<p dir=\"auto\"><strong>Golden rule #1<\/strong>: If you are writing a for-loop to apply the same math operation to every element of a NumPy array \u2192 <strong>you are almost certainly doing it wrong<\/strong>.<\/p>\n<p dir=\"auto\"><strong>Golden rule #2<\/strong>: When in doubt \u2192 try to write it with ufuncs + broadcasting. 90% of the time it will work and be much faster.<\/p>\n<p dir=\"auto\"><strong>Golden rule #3<\/strong>: Use <strong>np.<\/strong> prefix when you want to be explicit (np.sin vs np.sin) \u2014 helps readability and avoids conflicts with math module.<\/p>\n<p dir=\"auto\">Would you like to go deeper into any of these areas?<\/p>\n<ul dir=\"auto\">\n<li>Advanced broadcasting rules with examples<\/li>\n<li>ufuncs with out= parameter (in-place operations)<\/li>\n<li>Creating your own ufunc with np.frompyfunc \/ np.vectorize<\/li>\n<li>Common ufunc performance traps<\/li>\n<li>Realistic mini-project: vectorized image processing \/ data cleaning<\/li>\n<\/ul>\n<p dir=\"auto\">Just tell me what you want to focus on next! \ud83d\ude0a<\/p>\n","protected":false},"excerpt":{"rendered":"<p>1. What is a ufunc really? (honest explanation) ufunc = universal function A ufunc is a NumPy function that: operates element-by-element on entire arrays does it very fast (written in compiled C code) supports&#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-2529","post","type-post","status-publish","format-standard","hentry","category-numpy"],"_links":{"self":[{"href":"https:\/\/demo.materiamedica.net\/demo6\/wp-json\/wp\/v2\/posts\/2529","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=2529"}],"version-history":[{"count":1,"href":"https:\/\/demo.materiamedica.net\/demo6\/wp-json\/wp\/v2\/posts\/2529\/revisions"}],"predecessor-version":[{"id":2530,"href":"https:\/\/demo.materiamedica.net\/demo6\/wp-json\/wp\/v2\/posts\/2529\/revisions\/2530"}],"wp:attachment":[{"href":"https:\/\/demo.materiamedica.net\/demo6\/wp-json\/wp\/v2\/media?parent=2529"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/demo.materiamedica.net\/demo6\/wp-json\/wp\/v2\/categories?post=2529"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/demo.materiamedica.net\/demo6\/wp-json\/wp\/v2\/tags?post=2529"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}