<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Engr. Md Monjur Bakth Mazumder on Medium]]></title>
        <description><![CDATA[Stories by Engr. Md Monjur Bakth Mazumder on Medium]]></description>
        <link>https://medium.com/@monjurmbm404?source=rss-d31e4eba6166------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*LRX0unPB66YmZA0cqnkVZQ.jpeg</url>
            <title>Stories by Engr. Md Monjur Bakth Mazumder on Medium</title>
            <link>https://medium.com/@monjurmbm404?source=rss-d31e4eba6166------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Sat, 14 Nov 2026 19:10:17 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@monjurmbm404/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com/" rel="hub"/>
        <item>
            <title><![CDATA[Pandas Advanced GroupBy: Multiple Grouping, Aggregation and Named Aggregation]]></title>
            <link>https://medium.com/@monjurmbm404/pandas-advanced-groupby-multiple-grouping-aggregation-and-named-aggregation-02452fec9ac1?source=rss-d31e4eba6166------2</link>
            <guid isPermaLink="false">https://medium.com/p/02452fec9ac1</guid>
            <dc:creator><![CDATA[Engr. Md Monjur Bakth Mazumder]]></dc:creator>
            <pubDate>Sat, 14 Nov 2026 14:01:02 GMT</pubDate>
            <atom:updated>2026-11-14T14:01:02.572Z</atom:updated>
            <content:encoded><![CDATA[<h3>A Practical Guide to groupby(), agg(), Multiple Grouping, Custom Aggregation and Named Aggregation in Python Pandas</h3><p><strong>Series:</strong> Data Analysis with Pandas<br><strong>Part:</strong> 21</p><h3>Introduction</h3><p>In the previous article, we learned the basics of Pandas groupby().</p><p>We saw how grouping can answer simple questions such as:</p><ul><li>What is the average salary by department?</li><li>What are the total sales by category?</li><li>How many employees are in each department?</li></ul><p>But real-world data analysis usually requires more than one grouping column or one aggregation.</p><p>For example:</p><blockquote><em>What are the total sales, average sales, highest sale and number of orders for each product category in each region?</em></blockquote><p>This requires:</p><ul><li>Multiple grouping columns</li><li>Multiple aggregations</li><li>Different aggregation functions</li><li>Custom calculations</li><li>Clearly named output columns</li></ul><p>This is where <strong>Advanced GroupBy</strong> becomes useful.</p><p>In this article, we will explore:</p><pre>groupby()<br>agg()<br>Multiple Grouping<br>Multiple Aggregations<br>Custom Aggregation<br>Named Aggregation<br>Business Analysis</pre><p>The goal is not simply to learn more Pandas syntax.</p><p>The goal is to learn how to use GroupBy to answer more realistic business questions.</p><h3>1. What Is Advanced GroupBy?</h3><p>A basic GroupBy might look like this:</p><pre>df.groupby(&quot;Department&quot;)[&quot;Salary&quot;].mean()</pre><p>This answers:</p><blockquote><em>What is the average salary for each department?</em></blockquote><p>But suppose we want:</p><pre>Department<br>+ Region<br>    ↓<br>Total Sales<br>Average Sales<br>Maximum Sales<br>Minimum Sales<br>Number of Orders</pre><p>Now we need a more advanced grouping and aggregation strategy.</p><p>A typical pattern is:</p><pre>df.groupby([&quot;Department&quot;, &quot;Region&quot;]).agg(...)</pre><p>The mental model is:</p><pre>Raw Data<br>   ↓<br>Group by one or more columns<br>   ↓<br>Apply one or more aggregations<br>   ↓<br>Produce summarized results</pre><h3>2. The groupby() Function</h3><p>The general syntax is:</p><pre>df.groupby(by)</pre><p>For example:</p><pre>df.groupby(&quot;Department&quot;)</pre><p>For multiple columns:</p><pre>df.groupby([&quot;Department&quot;, &quot;Region&quot;])</pre><p>However, groupby() itself does not usually produce the final summary.</p><p>It creates a GroupBy object that can then be aggregated.</p><p>For example:</p><pre>grouped = df.groupby(&quot;Department&quot;)</pre><pre>print(grouped)</pre><p>You might see something similar to:</p><pre>&lt;pandas.core.groupby.generic.DataFrameGroupBy object ...&gt;</pre><p>The actual analysis happens when we apply an aggregation.</p><p>For example:</p><pre>df.groupby(&quot;Department&quot;)[&quot;Salary&quot;].mean()</pre><h3>3. Multiple Grouping</h3><p>One of the most useful features of Pandas GroupBy is grouping by multiple columns.</p><p>Suppose we have:</p><pre>import pandas as pd</pre><pre>df = pd.DataFrame({<br>    &quot;Region&quot;: [<br>        &quot;North&quot;, &quot;North&quot;, &quot;South&quot;, &quot;South&quot;,<br>        &quot;North&quot;, &quot;South&quot;<br>    ],<br>    &quot;Category&quot;: [<br>        &quot;Laptop&quot;, &quot;Mobile&quot;, &quot;Laptop&quot;,<br>        &quot;Mobile&quot;, &quot;Laptop&quot;, &quot;Mobile&quot;<br>    ],<br>    &quot;Sales&quot;: [<br>        50000, 30000, 45000,<br>        28000, 55000, 35000<br>    ]<br>})</pre><pre>print(df)</pre><p>We can group by both:</p><pre>df.groupby([&quot;Region&quot;, &quot;Category&quot;])[&quot;Sales&quot;].sum()</pre><p>The grouping hierarchy becomes:</p><pre>Region<br>    ↓<br>Category<br>    ↓<br>Sales Summary</pre><p>This allows us to answer:</p><blockquote><em>How much did each product category sell in each region?</em></blockquote><h3>4. Understanding Multiple Grouping</h3><p>Consider:</p><pre>df.groupby([&quot;Region&quot;, &quot;Category&quot;])[&quot;Sales&quot;].sum()</pre><p>Pandas first creates groups such as:</p><pre>North + Laptop<br>North + Mobile<br>South + Laptop<br>South + Mobile</pre><p>Then it calculates the total sales for each group.</p><p>This is very different from:</p><pre>df.groupby(&quot;Region&quot;)[&quot;Sales&quot;].sum()</pre><p>The first gives a more detailed analysis.</p><p>The second gives only region-level analysis.</p><p>So:</p><pre>groupby(&quot;Region&quot;)</pre><p>means:</p><blockquote><em>Analyze at the region level.</em></blockquote><p>While:</p><pre>groupby([&quot;Region&quot;, &quot;Category&quot;])</pre><p>means:</p><blockquote><em>Analyze at the region + category level.</em></blockquote><h3>5. Multiple Aggregations with agg()</h3><p>Suppose we want more than one statistic.</p><p>For example:</p><pre>Total Sales<br>Average Sales<br>Maximum Sales<br>Minimum Sales</pre><p>We can use:</p><pre>df.groupby(&quot;Region&quot;)[&quot;Sales&quot;].agg(<br>    [&quot;sum&quot;, &quot;mean&quot;, &quot;max&quot;, &quot;min&quot;]<br>)</pre><p>This is much more powerful than calling each aggregation separately.</p><p>Without agg():</p><pre>df.groupby(&quot;Region&quot;)[&quot;Sales&quot;].sum()</pre><pre>df.groupby(&quot;Region&quot;)[&quot;Sales&quot;].mean()</pre><pre>df.groupby(&quot;Region&quot;)[&quot;Sales&quot;].max()</pre><pre>df.groupby(&quot;Region&quot;)[&quot;Sales&quot;].min()</pre><p>With agg():</p><pre>df.groupby(&quot;Region&quot;)[&quot;Sales&quot;].agg(<br>    [&quot;sum&quot;, &quot;mean&quot;, &quot;max&quot;, &quot;min&quot;]<br>)</pre><p>This keeps related analysis together.</p><h3>6. Multiple Aggregations on Multiple Columns</h3><p>We can go one step further.</p><p>Suppose our dataset contains:</p><pre>df = pd.DataFrame({<br>    &quot;Region&quot;: [&quot;North&quot;, &quot;North&quot;, &quot;South&quot;, &quot;South&quot;],<br>    &quot;Sales&quot;: [50000, 30000, 45000, 28000],<br>    &quot;Profit&quot;: [10000, 6000, 9000, 5000],<br>    &quot;Orders&quot;: [50, 30, 45, 28]<br>})</pre><p>We may want:</p><pre>Sales:<br>    sum<br>    mean<br>    max</pre><pre>Profit:<br>    sum<br>    mean<br>    max</pre><pre>Orders:<br>    sum<br>    mean</pre><p>We can write:</p><pre>summary = df.groupby(&quot;Region&quot;).agg({<br>    &quot;Sales&quot;: [&quot;sum&quot;, &quot;mean&quot;, &quot;max&quot;],<br>    &quot;Profit&quot;: [&quot;sum&quot;, &quot;mean&quot;, &quot;max&quot;],<br>    &quot;Orders&quot;: [&quot;sum&quot;, &quot;mean&quot;]<br>})</pre><pre>print(summary)</pre><p>This allows different columns to have different aggregation functions.</p><h3>7. Why Use a Dictionary with agg()?</h3><p>The dictionary approach is useful when different columns need different calculations.</p><p>For example:</p><pre>df.groupby(&quot;Region&quot;).agg({<br>    &quot;Sales&quot;: [&quot;sum&quot;, &quot;mean&quot;],<br>    &quot;Profit&quot;: [&quot;sum&quot;, &quot;max&quot;],<br>    &quot;Orders&quot;: &quot;count&quot;<br>})</pre><p>This means:</p><pre>Sales  → sum + mean<br>Profit → sum + max<br>Orders → count</pre><p>This is useful for business dashboards and analytical summaries.</p><h3>8. Multiple Grouping + Multiple Aggregations</h3><p>Now let’s combine the concepts.</p><pre>summary = (<br>    df.groupby([&quot;Region&quot;, &quot;Category&quot;])<br>      .agg({<br>          &quot;Sales&quot;: [&quot;sum&quot;, &quot;mean&quot;, &quot;max&quot;],<br>          &quot;Profit&quot;: [&quot;sum&quot;, &quot;mean&quot;],<br>          &quot;Orders&quot;: &quot;count&quot;<br>      })<br>)</pre><pre>print(summary)</pre><p>This is a powerful business-analysis pattern.</p><p>We are answering multiple questions at once:</p><pre>For each Region + Category:</pre><pre>How much was sold?<br>What was the average sale?<br>What was the highest sale?<br>How much profit was generated?<br>What was the average profit?<br>How many records/orders exist?</pre><h3>9. Using agg() with Different Functions</h3><p>Pandas allows many built-in aggregation functions.</p><p>Common examples include:</p><pre>&quot;sum&quot;<br>&quot;mean&quot;<br>&quot;median&quot;<br>&quot;min&quot;<br>&quot;max&quot;<br>&quot;count&quot;<br>&quot;std&quot;<br>&quot;var&quot;<br>&quot;first&quot;<br>&quot;last&quot;</pre><p>For example:</p><pre>df.groupby(&quot;Region&quot;)[&quot;Sales&quot;].agg([<br>    &quot;count&quot;,<br>    &quot;sum&quot;,<br>    &quot;mean&quot;,<br>    &quot;median&quot;,<br>    &quot;min&quot;,<br>    &quot;max&quot;,<br>    &quot;std&quot;<br>])</pre><p>This creates a statistical summary for each region.</p><h3>10. Named Aggregation</h3><p>Multiple aggregations are powerful, but the default output column names can sometimes become difficult to read.</p><p>For example:</p><pre>df.groupby(&quot;Region&quot;).agg({<br>    &quot;Sales&quot;: [&quot;sum&quot;, &quot;mean&quot;, &quot;max&quot;]<br>})</pre><p>may produce columns conceptually like:</p><pre>Sales<br>    sum<br>    mean<br>    max</pre><p>For production-quality analysis, we often want meaningful column names.</p><p>This is where <strong>Named Aggregation</strong> becomes useful.</p><h3>11. Named Aggregation Syntax</h3><p>Named aggregation allows us to define the final column names directly.</p><p>Example:</p><pre>summary = df.groupby(&quot;Region&quot;).agg(<br>    total_sales=(&quot;Sales&quot;, &quot;sum&quot;),<br>    average_sales=(&quot;Sales&quot;, &quot;mean&quot;),<br>    highest_sale=(&quot;Sales&quot;, &quot;max&quot;)<br>)</pre><pre>print(summary)</pre><p>The syntax is:</p><pre>new_column_name=(&quot;source_column&quot;, &quot;aggregation_function&quot;)</pre><p>For example:</p><pre>total_sales=(&quot;Sales&quot;, &quot;sum&quot;)</pre><p>means:</p><pre>Take Sales<br>    ↓<br>Apply sum()<br>    ↓<br>Store the result as total_sales</pre><h3>12. Named Aggregation with Multiple Columns</h3><p>We can apply named aggregation to several columns.</p><pre>summary = df.groupby(&quot;Region&quot;).agg(<br>    total_sales=(&quot;Sales&quot;, &quot;sum&quot;),<br>    average_sales=(&quot;Sales&quot;, &quot;mean&quot;),<br>    total_profit=(&quot;Profit&quot;, &quot;sum&quot;),<br>    average_profit=(&quot;Profit&quot;, &quot;mean&quot;),<br>    total_orders=(&quot;Orders&quot;, &quot;sum&quot;)<br>)</pre><pre>print(summary)</pre><p>This produces a clean analytical output.</p><p>Instead of nested column labels, we get meaningful names such as:</p><pre>total_sales<br>average_sales<br>total_profit<br>average_profit<br>total_orders</pre><p>This is often easier to use for:</p><ul><li>Reporting</li><li>Dashboards</li><li>Visualization</li><li>Further analysis</li><li>Exporting results</li></ul><h3>13. Named Aggregation with Multiple Grouping</h3><p>Named aggregation becomes even more useful with multiple grouping columns.</p><pre>summary = (<br>    df.groupby([&quot;Region&quot;, &quot;Category&quot;])<br>      .agg(<br>          total_sales=(&quot;Sales&quot;, &quot;sum&quot;),<br>          average_sales=(&quot;Sales&quot;, &quot;mean&quot;),<br>          maximum_sale=(&quot;Sales&quot;, &quot;max&quot;),<br>          total_profit=(&quot;Profit&quot;, &quot;sum&quot;),<br>          average_profit=(&quot;Profit&quot;, &quot;mean&quot;)<br>      )<br>)</pre><pre>print(summary)</pre><p>Now the analysis is performed for every:</p><pre>Region + Category</pre><p>combination.</p><p>This is one of the most useful patterns to remember.</p><h3>14. Custom Aggregation</h3><p>Built-in functions are not always enough.</p><p>Sometimes a business question requires custom logic.</p><p>For example:</p><blockquote><em>What is the range between the highest and lowest sale?</em></blockquote><p>There is no standard aggregation called:</p><pre>&quot;range&quot;</pre><p>We can create our own function.</p><pre>def sales_range(series):<br>    return series.max() - series.min()</pre><p>Then:</p><pre>df.groupby(&quot;Region&quot;)[&quot;Sales&quot;].agg(sales_range)</pre><p>We can also use a lambda:</p><pre>df.groupby(&quot;Region&quot;)[&quot;Sales&quot;].agg(<br>    lambda x: x.max() - x.min()<br>)</pre><p>The custom function approach is often easier to read and reuse.</p><h3>15. Custom Aggregation with Named Aggregation</h3><p>We can combine custom functions with named aggregation.</p><pre>def sales_range(series):<br>    return series.max() - series.min()</pre><pre>summary = df.groupby(&quot;Region&quot;).agg(<br>    total_sales=(&quot;Sales&quot;, &quot;sum&quot;),<br>    average_sales=(&quot;Sales&quot;, &quot;mean&quot;),<br>    sales_range=(&quot;Sales&quot;, sales_range)<br>)</pre><pre>print(summary)</pre><p>This produces a clean business summary.</p><h3>16. Custom Percentage Calculation</h3><p>Suppose we want to calculate the percentage contribution of a group’s total sales relative to its own total.</p><p>A simple custom aggregation can calculate a statistic within each group.</p><p>For example:</p><pre>def sales_range(series):<br>    return series.max() - series.min()</pre><p>But some calculations require information from multiple columns or from the whole dataset.</p><p>For those cases, it is important to distinguish between:</p><pre>Aggregation<br>Transformation<br>Post-aggregation calculation</pre><p>Not every business calculation belongs inside agg().</p><p>For example, a total profit margin can often be calculated more meaningfully from aggregated totals:</p><pre>summary = (<br>    df.groupby(&quot;Region&quot;)<br>      .agg(<br>          total_sales=(&quot;Sales&quot;, &quot;sum&quot;),<br>          total_profit=(&quot;Profit&quot;, &quot;sum&quot;)<br>      )<br>)</pre><pre>summary[&quot;profit_margin&quot;] = (<br>    summary[&quot;total_profit&quot;] / summary[&quot;total_sales&quot;]<br>) * 100</pre><p>This is often better than averaging individual row-level profit percentages.</p><h3>17. Why Aggregation Order Matters</h3><p>Consider these rows:</p><pre>Sales   Profit<br>100     10<br>1000    100</pre><p>Individual profit margins are:</p><pre>10%<br>10%</pre><p>The average margin is:</p><pre>10%</pre><p>Now imagine:</p><pre>Sales   Profit<br>100     20<br>1000    100</pre><p>Margins are:</p><pre>20%<br>10%</pre><p>The simple average is:</p><pre>15%</pre><p>But the overall margin is:</p><pre>120 / 1100 × 100</pre><p>which is approximately:</p><pre>10.91%</pre><p>These are answering different questions.</p><p>Therefore, in business analysis, always ask:</p><blockquote><em>Should I aggregate the underlying values first, or aggregate the derived metric?</em></blockquote><p>This distinction is extremely important.</p><h3>18. Custom Aggregation for Business Rules</h3><p>Suppose a company wants to know the number of high-value sales.</p><p>A high-value sale is defined as:</p><pre>Sales &gt;= 50,000</pre><p>We can create a custom aggregation:</p><pre>def high_value_sales(series):<br>    return (series &gt;= 50000).sum()</pre><p>Then:</p><pre>summary = df.groupby(&quot;Region&quot;).agg(<br>    total_sales=(&quot;Sales&quot;, &quot;sum&quot;),<br>    high_value_orders=(&quot;Sales&quot;, high_value_sales)<br>)</pre><pre>print(summary)</pre><p>Because:</p><pre>(series &gt;= 50000)</pre><p>produces Boolean values:</p><pre>True<br>False<br>True</pre><p>and:</p><pre>.sum()</pre><p>counts the True values.</p><h3>19. Custom Aggregation with Lambda</h3><p>For small, simple rules, a lambda can be convenient.</p><pre>summary = df.groupby(&quot;Region&quot;).agg(<br>    total_sales=(&quot;Sales&quot;, &quot;sum&quot;),<br>    high_value_orders=(<br>        &quot;Sales&quot;,<br>        lambda x: (x &gt;= 50000).sum()<br>    )<br>)</pre><p>This is compact.</p><p>However, if the business rule becomes complex, a named function is usually easier to understand:</p><pre>def high_value_sales(series):<br>    return (series &gt;= 50000).sum()</pre><p>Then:</p><pre>high_value_orders=(&quot;Sales&quot;, high_value_sales)</pre><p>Prefer readability when the logic is important.</p><h3>20. Business Example: Regional Sales Analysis</h3><p>Let’s build a more realistic dataset.</p><pre>import pandas as pd</pre><pre>sales = pd.DataFrame({<br>    &quot;Region&quot;: [<br>        &quot;North&quot;, &quot;North&quot;, &quot;North&quot;,<br>        &quot;South&quot;, &quot;South&quot;, &quot;South&quot;,<br>        &quot;East&quot;, &quot;East&quot;<br>    ],<br>    &quot;Category&quot;: [<br>        &quot;Laptop&quot;, &quot;Mobile&quot;, &quot;Laptop&quot;,<br>        &quot;Mobile&quot;, &quot;Laptop&quot;, &quot;Mobile&quot;,<br>        &quot;Laptop&quot;, &quot;Mobile&quot;<br>    ],<br>    &quot;Sales&quot;: [<br>        80000, 45000, 70000,<br>        60000, 90000, 40000,<br>        75000, 50000<br>    ],<br>    &quot;Profit&quot;: [<br>        16000, 9000, 14000,<br>        12000, 18000, 8000,<br>        15000, 10000<br>    ]<br>})</pre><p>Now suppose management asks:</p><blockquote><em>Show sales and profit performance by region and product category.</em></blockquote><p>We can write:</p><pre>summary = (<br>    sales.groupby([&quot;Region&quot;, &quot;Category&quot;])<br>         .agg(<br>             total_sales=(&quot;Sales&quot;, &quot;sum&quot;),<br>             average_sales=(&quot;Sales&quot;, &quot;mean&quot;),<br>             maximum_sale=(&quot;Sales&quot;, &quot;max&quot;),<br>             total_profit=(&quot;Profit&quot;, &quot;sum&quot;),<br>             average_profit=(&quot;Profit&quot;, &quot;mean&quot;)<br>         )<br>)</pre><pre>print(summary)</pre><p>This produces a much richer analysis than a simple groupby().sum().</p><h3>21. Adding Profit Margin</h3><p>After aggregation:</p><pre>summary[&quot;profit_margin&quot;] = (<br>    summary[&quot;total_profit&quot;] /<br>    summary[&quot;total_sales&quot;]<br>) * 100</pre><p>Then round it:</p><pre>summary[&quot;profit_margin&quot;] = (<br>    summary[&quot;profit_margin&quot;].round(2)<br>)</pre><p>Now we have:</p><pre>Region + Category<br>        ↓<br>Total Sales<br>Average Sales<br>Maximum Sale<br>Total Profit<br>Average Profit<br>Profit Margin</pre><p>This is much closer to a real business report.</p><h3>22. Business Example: Employee Analysis</h3><p>GroupBy is not limited to sales.</p><p>Suppose we have:</p><pre>employees = pd.DataFrame({<br>    &quot;Department&quot;: [<br>        &quot;IT&quot;, &quot;IT&quot;, &quot;HR&quot;,<br>        &quot;HR&quot;, &quot;Finance&quot;, &quot;Finance&quot;<br>    ],<br>    &quot;Level&quot;: [<br>        &quot;Senior&quot;, &quot;Junior&quot;, &quot;Senior&quot;,<br>        &quot;Junior&quot;, &quot;Senior&quot;, &quot;Junior&quot;<br>    ],<br>    &quot;Salary&quot;: [<br>        90000, 50000, 75000,<br>        45000, 85000, 55000<br>    ],<br>    &quot;Experience&quot;: [<br>        8, 2, 7, 1, 9, 3<br>    ]<br>})</pre><p>We can analyze department and level together:</p><pre>summary = (<br>    employees<br>    .groupby([&quot;Department&quot;, &quot;Level&quot;])<br>    .agg(<br>        employee_count=(&quot;Salary&quot;, &quot;count&quot;),<br>        average_salary=(&quot;Salary&quot;, &quot;mean&quot;),<br>        maximum_salary=(&quot;Salary&quot;, &quot;max&quot;),<br>        average_experience=(&quot;Experience&quot;, &quot;mean&quot;)<br>    )<br>)</pre><pre>print(summary)</pre><p>This answers:</p><blockquote><em>How many employees are at each level in each department, and what are their salary and experience characteristics?</em></blockquote><h3>23. Business Example: Customer Analysis</h3><p>Suppose an e-commerce dataset contains:</p><pre>Customer<br>Region<br>OrderValue</pre><p>We can analyze customers by region.</p><pre>customer_summary = (<br>    df.groupby(&quot;Region&quot;)<br>      .agg(<br>          total_revenue=(&quot;OrderValue&quot;, &quot;sum&quot;),<br>          average_order=(&quot;OrderValue&quot;, &quot;mean&quot;),<br>          largest_order=(&quot;OrderValue&quot;, &quot;max&quot;),<br>          order_count=(&quot;OrderValue&quot;, &quot;count&quot;)<br>      )<br>)</pre><p>Now management can identify:</p><ul><li>High-revenue regions</li><li>Average order value</li><li>Largest transaction</li><li>Number of orders</li></ul><p>This is the real purpose of advanced GroupBy.</p><h3>24. GroupBy for Business Questions</h3><p>Instead of thinking:</p><blockquote><em>Which GroupBy syntax should I use?</em></blockquote><p>Think:</p><blockquote><em>What business question am I trying to answer?</em></blockquote><p>For example:</p><h3>Question 1</h3><blockquote><em>Which region generates the most revenue?</em></blockquote><pre>df.groupby(&quot;Region&quot;)[&quot;Sales&quot;].sum()</pre><h3>Question 2</h3><blockquote><em>Which region and category combination generates the most revenue?</em></blockquote><pre>df.groupby([&quot;Region&quot;, &quot;Category&quot;])[&quot;Sales&quot;].sum()</pre><h3>Question 3</h3><blockquote><em>What are the average and maximum sales for each region?</em></blockquote><pre>df.groupby(&quot;Region&quot;)[&quot;Sales&quot;].agg([&quot;mean&quot;, &quot;max&quot;])</pre><h3>Question 4</h3><blockquote><em>What are the total sales and total profit by region and category?</em></blockquote><pre>df.groupby([&quot;Region&quot;, &quot;Category&quot;]).agg(<br>    total_sales=(&quot;Sales&quot;, &quot;sum&quot;),<br>    total_profit=(&quot;Profit&quot;, &quot;sum&quot;)<br>)</pre><h3>Question 5</h3><blockquote><em>What is the profit margin for each region?</em></blockquote><pre>summary = df.groupby(&quot;Region&quot;).agg(<br>    total_sales=(&quot;Sales&quot;, &quot;sum&quot;),<br>    total_profit=(&quot;Profit&quot;, &quot;sum&quot;)<br>)</pre><pre>summary[&quot;profit_margin&quot;] = (<br>    summary[&quot;total_profit&quot;] /<br>    summary[&quot;total_sales&quot;]<br>) * 100</pre><p>The question determines the GroupBy design.</p><h3>25. agg() vs Calling Aggregations Separately</h3><p>You could write:</p><pre>total_sales = df.groupby(&quot;Region&quot;)[&quot;Sales&quot;].sum()</pre><pre>average_sales = df.groupby(&quot;Region&quot;)[&quot;Sales&quot;].mean()</pre><pre>maximum_sales = df.groupby(&quot;Region&quot;)[&quot;Sales&quot;].max()</pre><p>But this repeats the grouping operation.</p><p>Instead:</p><pre>summary = df.groupby(&quot;Region&quot;)[&quot;Sales&quot;].agg(<br>    [&quot;sum&quot;, &quot;mean&quot;, &quot;max&quot;]<br>)</pre><p>Or, for a production-friendly result:</p><pre>summary = df.groupby(&quot;Region&quot;).agg(<br>    total_sales=(&quot;Sales&quot;, &quot;sum&quot;),<br>    average_sales=(&quot;Sales&quot;, &quot;mean&quot;),<br>    maximum_sales=(&quot;Sales&quot;, &quot;max&quot;)<br>)</pre><p>The named aggregation version is often the clearest when building a final analytical dataset.</p><h3>26. count() vs size() in Advanced GroupBy</h3><p>This distinction remains important.</p><p>Consider:</p><pre>df.groupby(&quot;Region&quot;)[&quot;Sales&quot;].count()</pre><p>count() counts non-missing values in the selected column.</p><p>Whereas:</p><pre>df.groupby(&quot;Region&quot;).size()</pre><p>counts rows in each group.</p><p>For example:</p><pre>summary = df.groupby(&quot;Region&quot;).agg(<br>    sales_count=(&quot;Sales&quot;, &quot;count&quot;)<br>)</pre><p>counts non-null Sales values.</p><p>If you want the number of records regardless of whether Sales is missing, use:</p><pre>summary = df.groupby(&quot;Region&quot;).size()</pre><p>Choose based on the business question.</p><h3>27. Resetting the Index</h3><p>When grouping by multiple columns, Pandas commonly creates a MultiIndex.</p><p>For example:</p><pre>summary = (<br>    df.groupby([&quot;Region&quot;, &quot;Category&quot;])<br>      .agg(<br>          total_sales=(&quot;Sales&quot;, &quot;sum&quot;)<br>      )<br>)</pre><p>The result has:</p><pre>Region<br>Category</pre><p>as index levels.</p><p>If you want them as regular columns:</p><pre>summary = summary.reset_index()</pre><p>Alternatively, you can use:</p><pre>summary = (<br>    df.groupby(<br>        [&quot;Region&quot;, &quot;Category&quot;],<br>        as_index=False<br>    )<br>    .agg(<br>        total_sales=(&quot;Sales&quot;, &quot;sum&quot;)<br>    )<br>)</pre><p>This is often convenient when the result will be used for:</p><ul><li>Visualization</li><li>Export</li><li>Further DataFrame operations</li><li>Dashboard development</li></ul><h3>28. Custom Aggregation: When Should You Use It?</h3><p>Custom aggregation is useful when:</p><ul><li>Built-in aggregation functions are not enough</li><li>A specific business rule is required</li><li>A derived group-level statistic is needed</li><li>You need reusable analytical logic</li></ul><p>For example:</p><pre>def value_range(series):<br>    return series.max() - series.min()</pre><p>Then:</p><pre>df.groupby(&quot;Region&quot;).agg(<br>    sales_range=(&quot;Sales&quot;, value_range)<br>)</pre><p>But do not create custom functions for calculations that Pandas already handles efficiently.</p><p>Prefer:</p><pre>df.groupby(&quot;Region&quot;)[&quot;Sales&quot;].mean()</pre><p>instead of unnecessarily writing:</p><pre>def calculate_mean(series):<br>    return series.sum() / series.count()</pre><p>Use built-in Pandas operations whenever they already express the required logic.</p><h3>29. Common Mistakes</h3><h3>Mistake 1: Grouping by too many columns</h3><pre>df.groupby([<br>    &quot;Region&quot;,<br>    &quot;Category&quot;,<br>    &quot;Product&quot;,<br>    &quot;Customer&quot;,<br>    &quot;Salesperson&quot;<br>])</pre><p>This can create extremely granular groups.</p><p>Ask whether every grouping column is necessary.</p><h3>Mistake 2: Using the wrong aggregation</h3><p>For example:</p><pre>df.groupby(&quot;Region&quot;)[&quot;Profit&quot;].mean()</pre><p>may not answer the same question as:</p><pre>df.groupby(&quot;Region&quot;)[&quot;Profit&quot;].sum()</pre><p>Mean and sum represent different business concepts.</p><h3>Mistake 3: Averaging percentages blindly</h3><p>Suppose each transaction has a profit margin.</p><p>A simple:</p><pre>df.groupby(&quot;Region&quot;)[&quot;ProfitMargin&quot;].mean()</pre><p>may not represent the actual overall regional margin.</p><p>Often you should aggregate:</p><pre>Total Profit<br>Total Sales</pre><p>and then calculate:</p><pre>Total Profit / Total Sales</pre><h3>Mistake 4: Overusing custom functions</h3><p>If Pandas already provides:</p><pre>sum<br>mean<br>median<br>min<br>max<br>count<br>std</pre><p>use them.</p><p>Custom functions should solve a real requirement.</p><h3>Mistake 5: Ignoring missing values</h3><p>Aggregation behavior depends on the function and the data.</p><p>Always inspect missing values before interpreting the result.</p><p>Useful checks include:</p><pre>df.isna().sum()</pre><p>and:</p><pre>df.info()</pre><h3>Mistake 6: Forgetting the level of analysis</h3><p>Before writing:</p><pre>groupby(...)</pre><p>ask:</p><blockquote><em>What should one row of my final summary represent?</em></blockquote><p>For example:</p><pre>One row = one region</pre><p>or:</p><pre>One row = one region + category</pre><p>This simple question prevents many analytical mistakes.</p><h3>30. A Practical Advanced GroupBy Workflow</h3><p>A reliable workflow looks like this:</p><pre>1. Load Data<br>      ↓<br>2. Inspect Data<br>      ↓<br>3. Define Business Question<br>      ↓<br>4. Select Grouping Columns<br>      ↓<br>5. Select Analysis Columns<br>      ↓<br>6. Choose Aggregations<br>      ↓<br>7. Apply groupby()<br>      ↓<br>8. Apply agg()<br>      ↓<br>9. Add Derived Metrics<br>      ↓<br>10. Validate Results<br>      ↓<br>11. Sort / Filter<br>      ↓<br>12. Visualize or Export</pre><p>For example:</p><pre>summary = (<br>    df.groupby([&quot;Region&quot;, &quot;Category&quot;])<br>      .agg(<br>          total_sales=(&quot;Sales&quot;, &quot;sum&quot;),<br>          average_sales=(&quot;Sales&quot;, &quot;mean&quot;),<br>          total_profit=(&quot;Profit&quot;, &quot;sum&quot;)<br>      )<br>      .reset_index()<br>)</pre><pre>summary[&quot;profit_margin&quot;] = (<br>    summary[&quot;total_profit&quot;] /<br>    summary[&quot;total_sales&quot;] * 100<br>)</pre><pre>summary[&quot;profit_margin&quot;] = (<br>    summary[&quot;profit_margin&quot;].round(2)<br>)</pre><pre>summary = summary.sort_values(<br>    &quot;total_sales&quot;,<br>    ascending=False<br>)</pre><pre>print(summary)</pre><p>This is a realistic data-analysis workflow.</p><h3>31. Advanced GroupBy Mental Model</h3><p>Think about Advanced GroupBy like this:</p><pre>Raw Data<br>                     │<br>                     ▼<br>             Define the Question<br>                     │<br>                     ▼<br>              Choose Groups<br>                     │<br>             ┌───────┴────────┐<br>             │                │<br>          Region          Category<br>             │                │<br>             └───────┬────────┘<br>                     ▼<br>                  groupby()<br>                     │<br>                     ▼<br>                    agg()<br>                     │<br>       ┌─────────────┼─────────────┐<br>       ▼             ▼             ▼<br>      sum           mean          max<br>       │             │             │<br>       └─────────────┼─────────────┘<br>                     ▼<br>              Business Summary<br>                     │<br>                     ▼<br>             Derived Metrics<br>                     │<br>                     ▼<br>                Insights</pre><p>This is more important than memorizing syntax.</p><h3>32. Quick Reference</h3><h3>Single grouping</h3><pre>df.groupby(&quot;Region&quot;)</pre><h3>Multiple grouping</h3><pre>df.groupby([&quot;Region&quot;, &quot;Category&quot;])</pre><h3>Single aggregation</h3><pre>df.groupby(&quot;Region&quot;)[&quot;Sales&quot;].sum()</pre><h3>Multiple aggregations</h3><pre>df.groupby(&quot;Region&quot;)[&quot;Sales&quot;].agg(<br>    [&quot;sum&quot;, &quot;mean&quot;, &quot;max&quot;]<br>)</pre><h3>Multiple columns</h3><pre>df.groupby(&quot;Region&quot;).agg({<br>    &quot;Sales&quot;: [&quot;sum&quot;, &quot;mean&quot;],<br>    &quot;Profit&quot;: [&quot;sum&quot;, &quot;max&quot;]<br>})</pre><h3>Named aggregation</h3><pre>df.groupby(&quot;Region&quot;).agg(<br>    total_sales=(&quot;Sales&quot;, &quot;sum&quot;),<br>    average_sales=(&quot;Sales&quot;, &quot;mean&quot;)<br>)</pre><h3>Multiple grouping + named aggregation</h3><pre>df.groupby([&quot;Region&quot;, &quot;Category&quot;]).agg(<br>    total_sales=(&quot;Sales&quot;, &quot;sum&quot;),<br>    average_sales=(&quot;Sales&quot;, &quot;mean&quot;),<br>    total_profit=(&quot;Profit&quot;, &quot;sum&quot;)<br>)</pre><h3>Custom aggregation</h3><pre>def value_range(series):<br>    return series.max() - series.min()</pre><pre>df.groupby(&quot;Region&quot;).agg(<br>    sales_range=(&quot;Sales&quot;, value_range)<br>)</pre><h3>Keep grouping columns as regular columns</h3><pre>df.groupby(<br>    [&quot;Region&quot;, &quot;Category&quot;],<br>    as_index=False<br>).agg(<br>    total_sales=(&quot;Sales&quot;, &quot;sum&quot;)<br>)</pre><h3>33. Key Takeaways</h3><p>Advanced GroupBy is about turning raw records into meaningful summaries.</p><p>The most important concepts are:</p><pre>groupby()</pre><p>Use it to define groups.</p><pre>Multiple grouping</pre><p>Use multiple columns when analysis requires more detailed segmentation.</p><pre>agg()</pre><p>Use it to apply multiple aggregation functions.</p><pre>Custom aggregation</pre><p>Use it when built-in functions cannot express your business rule.</p><pre>Named aggregation</pre><p>Use it to create clean, meaningful output column names.</p><p>And most importantly:</p><blockquote><strong><em>Choose the grouping level and aggregation based on the business question — not simply because a particular Pandas function exists.</em></strong></blockquote><p>A strong data analyst does not just know how to write:</p><pre>df.groupby(...)</pre><p>They know <strong>what should be grouped, what should be aggregated, and why the resulting metric matters.</strong></p><h3>What Comes Next?</h3><p>In this article, we learned how to perform advanced aggregation with:</p><ul><li>Multiple grouping</li><li>Multiple aggregations</li><li>agg()</li><li>Custom aggregation</li><li>Named aggregation</li><li>Business-oriented analysis</li></ul><p>But GroupBy can do more than summarize data.</p><p>Sometimes we need to perform an operation <strong>within each group while keeping the original DataFrame structure</strong>.</p><p>That is where:</p><blockquote><strong><em>GroupBy.transform() vs </em></strong><strong><em>GroupBy.filter()</em></strong></blockquote><p>becomes important.</p><p>In <strong>Post #22</strong>, we will explore:</p><p><strong>Pandas GroupBy </strong><strong>transform() vs </strong><strong>filter(): When and Why to Use Them</strong></p><h3>About This Series</h3><p><strong>Data Analysis with Pandas</strong> is a practical, beginner-friendly series covering Pandas from fundamentals to advanced data analysis.</p><p>The series progresses through:</p><pre>Pandas Basics<br>      ↓<br>Data Selection<br>      ↓<br>Data Cleaning<br>      ↓<br>Transformation<br>      ↓<br>Feature Engineering<br>      ↓<br>GroupBy &amp; Aggregation<br>      ↓<br>Reshaping<br>      ↓<br>Date &amp; Time Analysis<br>      ↓<br>Time Series<br>      ↓<br>EDA<br>      ↓<br>Real-World Projects<br>      ↓<br>Data Science &amp; ML Preparation</pre><p>The goal is to help beginners move from:</p><pre>Learning Pandas</pre><p>to:</p><pre>Using Pandas to solve real data-analysis problems.</pre><h3>Author</h3><p><strong>Engr. Md. Monjur Bakth Mazumder</strong></p><p>GitHub: <a href="https://github.com/monjurmbm404/Python-Data-Science-Tools">Python-Data-Science-Tools</a></p><h3>Final Thought</h3><p>Learning advanced Pandas is not about memorizing more methods.</p><p>It is about learning how to transform a business question into a structured analytical operation.</p><p>When you can think:</p><pre>Business Question<br>       ↓<br>Grouping Level<br>       ↓<br>Aggregation<br>       ↓<br>Derived Metric<br>       ↓<br>Insight</pre><p>you are moving beyond Pandas syntax and starting to think like a data analyst.</p><h3>Connect &amp; Follow</h3><h4>Professional Profiles</h4><ul><li>Portfolio: <a href="https://monjurmbm404.github.io/?source=user_about----------------------d31e4eba6166------------------------">https://monjurmbm404.github.io</a></li><li>GitHub: <a href="https://github.com/monjurmbm404?source=user_about----------------------d31e4eba6166------------------------">https://github.com/monjurmbm404</a></li><li>LinkedIn: <a href="https://www.linkedin.com/in/monjurmbm404?source=user_about----------------------d31e4eba6166------------------------">https://www.linkedin.com/in/monjurmbm404</a></li><li>Medium: <a href="https://medium.com/@monjurmbm404?source=user_about----------------------d31e4eba6166------------------------">https://medium.com/@monjurmbm404</a></li></ul><h4>Books &amp; Publications</h4><ul><li>Amazon: <a href="https://www.amazon.com/author/monjurmbm404?source=user_about----------------------d31e4eba6166------------------------">https://www.amazon.com/author/monjurmbm404</a></li></ul><h4>Competitive Programming</h4><ul><li>Codeforces: <a href="https://codeforces.com/profile/monjurmbm404?source=user_about----------------------d31e4eba6166------------------------">https://codeforces.com/profile/monjurmbm404</a></li><li>CodeChef: <a href="https://www.codechef.com/users/monjurmbm404?source=user_about----------------------d31e4eba6166------------------------">https://www.codechef.com/users/monjurmbm404</a></li><li>LeetCode: <a href="https://leetcode.com/u/monjurmbm404/?source=user_about----------------------d31e4eba6166------------------------">https://leetcode.com/u/monjurmbm404/</a></li><li>VJudge: <a href="https://vjudge.net/user/monjurmbm404?source=user_about----------------------d31e4eba6166------------------------">https://vjudge.net/user/monjurmbm404</a></li></ul><h4>Social Media</h4><ul><li>YouTube: <a href="https://youtube.com/@monjurmbm404?source=user_about----------------------d31e4eba6166------------------------">https://youtube.com/@monjurmbm404</a></li><li>X: <a href="https://x.com/monjurmbm404?source=user_about----------------------d31e4eba6166------------------------">https://x.com/monjurmbm404</a></li><li>Facebook: <a href="https://www.facebook.com/monjurmbm404?source=user_about----------------------d31e4eba6166------------------------">https://www.facebook.com/monjurmbm404</a></li></ul><h4>Connect with Me</h4><ul><li>Email<strong>:</strong> <a href="/service/mailto:monjurmbm404@gmail.com">monjurmbm404@gmail.com</a></li></ul><p>— <strong>Engr. Md Monjur Bakth Mazumder<br></strong>Software Engineer | Technical Author | Competitive Programmer | AI/ML Enthusiast</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=02452fec9ac1" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Pandas GroupBy Basics: Grouping Data for Analysis]]></title>
            <link>https://medium.com/@monjurmbm404/pandas-groupby-basics-grouping-data-for-analysis-8ecac52a9cde?source=rss-d31e4eba6166------2</link>
            <guid isPermaLink="false">https://medium.com/p/8ecac52a9cde</guid>
            <dc:creator><![CDATA[Engr. Md Monjur Bakth Mazumder]]></dc:creator>
            <pubDate>Fri, 13 Nov 2026 14:01:04 GMT</pubDate>
            <atom:updated>2026-11-13T14:01:04.765Z</atom:updated>
            <content:encoded><![CDATA[<blockquote><strong><em>Series:</em></strong><em> Data Analysis with Pandas<br></em><strong><em>Part:</em></strong><em> 20</em></blockquote><blockquote>A Practical Guide to Grouping Data, Applying Aggregations and Analyzing Groups with df.groupby()</blockquote><p>When working with real-world datasets, looking at the entire DataFrame at once is often not enough.</p><p>Suppose we have sales data containing:</p><pre>Product<br>Category<br>Region<br>Sales<br>Profit</pre><p>Instead of asking:</p><blockquote><em>What is the total sales of the entire dataset?</em></blockquote><p>we may want to ask:</p><ul><li>What is the total sales for each region?</li><li>What is the average sales for each product category?</li><li>How many orders came from each region?</li><li>What is the highest sale in each category?</li><li>What is the average student score for each class?</li></ul><p>These questions require us to <strong>group related records together</strong>.</p><p>This is where Pandas groupby() becomes one of the most important tools for data analysis.</p><p>In this article, we will learn:</p><ul><li>What grouping means</li><li>What df.groupby() does</li><li>How to group by one column</li><li>How to calculate aggregations</li><li>How to analyze groups</li><li>How to use count(), sum(), mean(), min() and max()</li><li>How groupby() works conceptually</li><li>Practical student and sales examples</li><li>Common mistakes</li><li>A simple GroupBy workflow</li></ul><h3>1. What Is Grouping?</h3><p>Grouping means dividing rows into logical groups based on the value of one or more columns.</p><p>Consider this dataset:</p><pre>import pandas as pd</pre><pre>df = pd.DataFrame({<br>    &quot;Department&quot;: [<br>        &quot;IT&quot;,<br>        &quot;HR&quot;,<br>        &quot;IT&quot;,<br>        &quot;Sales&quot;,<br>        &quot;HR&quot;,<br>        &quot;Sales&quot;<br>    ],<br>    &quot;Salary&quot;: [<br>        70000,<br>        50000,<br>        80000,<br>        60000,<br>        55000,<br>        65000<br>    ]<br>})</pre><p>We have three departments:</p><pre>IT<br>HR<br>Sales</pre><p>Instead of analyzing every employee individually, we can group employees by:</p><pre>df.groupby(&quot;Department&quot;)</pre><p>Conceptually:</p><pre>Original Data<br>      ↓<br>Group by Department<br>      ↓<br>IT<br>HR<br>Sales</pre><p>Each group contains the rows belonging to that department.</p><h3>2. What Does groupby() Do?</h3><p>The basic syntax is:</p><pre>df.groupby(&quot;column&quot;)</pre><p>For example:</p><pre>df.groupby(&quot;Department&quot;)</pre><p>But there is an important point:</p><blockquote><em>groupby() itself does not usually produce the final summary you want.</em></blockquote><p>It creates a <strong>grouped object</strong> that can then be combined with an aggregation or another operation.</p><p>Think of the process as:</p><pre>Group<br>  ↓<br>Apply operation<br>  ↓<br>Combine results</pre><p>This is commonly called the:</p><blockquote><strong><em>Split → Apply → Combine</em></strong><em> pattern.</em></blockquote><h3>3. The Split → Apply → Combine Pattern</h3><p>This is the core mental model behind groupby().</p><h3>Step 1 — Split</h3><p>Divide the DataFrame into groups.</p><pre>df.groupby(&quot;Department&quot;)</pre><h3>Step 2 — Apply</h3><p>Perform an operation on each group.</p><p>For example:</p><pre>.mean()</pre><h3>Step 3 — Combine</h3><p>Pandas combines the results into a new summarized object.</p><p>So:</p><pre>df.groupby(&quot;Department&quot;)[&quot;Salary&quot;].mean()</pre><p>means:</p><pre>Split employees by Department<br>        ↓<br>Calculate Salary mean for each group<br>        ↓<br>Combine the results</pre><p>This pattern is fundamental to data analysis.</p><h3>4. Basic groupby() Syntax</h3><p>The basic structure is:</p><pre>df.groupby(&quot;column&quot;)</pre><p>Usually, we then select a numerical column and apply an aggregation:</p><pre>df.groupby(&quot;group_column&quot;)[&quot;value_column&quot;].mean()</pre><p>For example:</p><pre>df.groupby(&quot;Department&quot;)[&quot;Salary&quot;].mean()</pre><p>This asks:</p><blockquote><em>What is the average salary for each department?</em></blockquote><h3>5. Grouping by One Column</h3><p>Let’s use a simple sales dataset:</p><pre>sales = pd.DataFrame({<br>    &quot;Region&quot;: [<br>        &quot;North&quot;,<br>        &quot;South&quot;,<br>        &quot;North&quot;,<br>        &quot;East&quot;,<br>        &quot;South&quot;,<br>        &quot;East&quot;<br>    ],<br>    &quot;Sales&quot;: [<br>        1000,<br>        1500,<br>        1200,<br>        900,<br>        1800,<br>        1100<br>    ]<br>})</pre><p>Group by region:</p><pre>sales.groupby(&quot;Region&quot;)</pre><p>The groups are:</p><pre>North<br>South<br>East</pre><p>Each group contains the sales records belonging to that region.</p><h3>6. GroupBy Needs an Analysis Operation</h3><p>Simply writing:</p><pre>sales.groupby(&quot;Region&quot;)</pre><p>does not answer a business question.</p><p>We need to apply an operation.</p><p>For example:</p><pre>sales.groupby(&quot;Region&quot;)[&quot;Sales&quot;].sum()</pre><p>Now we are asking:</p><blockquote><em>What is the total sales for each region?</em></blockquote><p>This produces a summarized result.</p><h3>7. Aggregation</h3><p><strong>Aggregation</strong> means reducing multiple values into a summary value.</p><p>For example:</p><pre>1000<br>1200</pre><p>can become:</p><pre>2200</pre><p>using:</p><pre>sum()</pre><p>Similarly:</p><pre>1000<br>1200</pre><p>can become:</p><pre>1100</pre><p>using:</p><pre>mean()</pre><p>Common aggregation functions include:</p><pre>count()<br>sum()<br>mean()<br>median()<br>min()<br>max()<br>std()</pre><p>For GroupBy basics, the most important ones are:</p><pre>count<br>sum<br>mean<br>min<br>max</pre><h3>8. GroupBy with sum()</h3><p>Let’s calculate total sales by region:</p><pre>sales.groupby(&quot;Region&quot;)[&quot;Sales&quot;].sum()</pre><p>Conceptually:</p><pre>North<br>→ 1000 + 1200</pre><pre>South<br>→ 1500 + 1800</pre><pre>East<br>→ 900 + 1100</pre><p>The result tells us the total sales generated by each region.</p><p>This is one of the most common uses of groupby().</p><h3>9. GroupBy with mean()</h3><p>Now calculate average sales:</p><pre>sales.groupby(&quot;Region&quot;)[&quot;Sales&quot;].mean()</pre><p>The calculation becomes:</p><pre>North<br>→ average of North sales</pre><pre>South<br>→ average of South sales</pre><pre>East<br>→ average of East sales</pre><p>This is useful when comparing typical performance across groups.</p><h3>10. GroupBy with count()</h3><p>We can count observations in each group.</p><p>For example:</p><pre>sales.groupby(&quot;Region&quot;)[&quot;Sales&quot;].count()</pre><p>This tells us how many non-missing Sales values each region has.</p><p>If the question is:</p><blockquote><em>How many records belong to each region?</em></blockquote><p>you can also use:</p><pre>sales.groupby(&quot;Region&quot;).size()</pre><p>This distinction is important:</p><pre>count()<br>→ counts non-missing values in the selected column</pre><pre>size()<br>→ counts rows in each group</pre><p>For clean data with no missing values, they may produce the same counts.</p><h3>11. GroupBy with min()</h3><p>We can find the minimum value in each group:</p><pre>sales.groupby(&quot;Region&quot;)[&quot;Sales&quot;].min()</pre><p>This answers:</p><blockquote><em>What was the smallest recorded sale in each region?</em></blockquote><p>For example:</p><pre>North → minimum North sale<br>South → minimum South sale<br>East  → minimum East sale</pre><h3>12. GroupBy with max()</h3><p>Similarly:</p><pre>sales.groupby(&quot;Region&quot;)[&quot;Sales&quot;].max()</pre><p>answers:</p><blockquote><em>What was the largest recorded sale in each region?</em></blockquote><p>This is useful for identifying peak performance.</p><h3>13. GroupBy with median()</h3><p>GroupBy also supports:</p><pre>sales.groupby(&quot;Region&quot;)[&quot;Sales&quot;].median()</pre><p>Median can be useful when extreme values may distort the average.</p><p>For example:</p><pre>1000<br>1100<br>1200<br>50000</pre><p>The mean is strongly influenced by 50000.</p><p>The median provides a different view of the typical value.</p><h3>14. GroupBy with std()</h3><p>We can also calculate variation:</p><pre>sales.groupby(&quot;Region&quot;)[&quot;Sales&quot;].std()</pre><p>This helps answer:</p><blockquote><em>How much does sales vary within each region</em></blockquote><p>Remember from earlier Pandas statistics content:</p><pre>Series.std()</pre><p>uses sample standard deviation by default.</p><h3>15. Selecting the Column to Analyze</h3><p>Consider:</p><pre>sales.groupby(&quot;Region&quot;)</pre><p>There may be several columns available.</p><p>If we only want to analyze Sales, write:</p><pre>sales.groupby(&quot;Region&quot;)[&quot;Sales&quot;].sum()</pre><p>This is usually clearer than applying an aggregation to every compatible column.</p><p>The structure is:</p><pre>df.groupby(&quot;group_column&quot;)[&quot;value_column&quot;].aggregation()</pre><p>For example:</p><pre>df.groupby(&quot;Department&quot;)[&quot;Salary&quot;].mean()</pre><h3>16. Grouping Student Data</h3><p>Let’s use a student example:</p><pre>students = pd.DataFrame({<br>    &quot;Class&quot;: [<br>        &quot;A&quot;,<br>        &quot;A&quot;,<br>        &quot;B&quot;,<br>        &quot;B&quot;,<br>        &quot;C&quot;,<br>        &quot;C&quot;<br>    ],<br>    &quot;Score&quot;: [<br>        80,<br>        90,<br>        70,<br>        75,<br>        85,<br>        95<br>    ]<br>})</pre><p>We can calculate the average score by class:</p><pre>students.groupby(&quot;Class&quot;)[&quot;Score&quot;].mean()</pre><p>Now we can answer:</p><blockquote><em>What is the average score for each class?</em></blockquote><p>Instead of manually filtering every class, groupby() performs the operation systematically.</p><h3>17. Total Score by Class</h3><p>We can calculate total scores:</p><pre>students.groupby(&quot;Class&quot;)[&quot;Score&quot;].sum()</pre><p>This answers:</p><blockquote><em>What is the total score across students in each class?</em></blockquote><p>This may be useful in some analyses, although average score is often more appropriate for comparing groups of different sizes.</p><h3>18. Number of Students per Class</h3><p>We can count students:</p><pre>students.groupby(&quot;Class&quot;).size()</pre><p>This answers:</p><blockquote><em>How many students are in each class?</em></blockquote><p>For example:</p><pre>Class A → 2<br>Class B → 2<br>Class C → 2</pre><p>If your DataFrame has one row per student, this is a direct row count.</p><h3>19. Minimum and Maximum Score by Class</h3><p>We can calculate:</p><pre>students.groupby(&quot;Class&quot;)[&quot;Score&quot;].min()</pre><p>and:</p><pre>students.groupby(&quot;Class&quot;)[&quot;Score&quot;].max()</pre><p>This allows us to compare:</p><pre>Lowest score per class<br>Highest score per class</pre><p>Together, these can provide a quick picture of group performance.</p><h3>20. GroupBy Result and Index</h3><p>When we run:</p><pre>students.groupby(&quot;Class&quot;)[&quot;Score&quot;].mean()</pre><p>the grouping column becomes the index of the resulting Series.</p><p>Conceptually:</p><pre>Class<br>A    ...<br>B    ...<br>C    ...</pre><p>This is normal Pandas behavior.</p><p>If you want the result as a regular DataFrame column structure, you can use:</p><pre>students.groupby(&quot;Class&quot;)[&quot;Score&quot;].mean().reset_index()</pre><p>Now Class becomes a regular column.</p><h3>21. Using as_index=False</h3><p>Another option is:</p><pre>students.groupby(<br>    &quot;Class&quot;,<br>    as_index=False<br>)[&quot;Score&quot;].mean()</pre><p>This keeps the grouping column as a normal column in the result.</p><p>This can be convenient when you plan to continue working with the result as a DataFrame.</p><p>For example:</p><pre>summary = students.groupby(<br>    &quot;Class&quot;,<br>    as_index=False<br>)[&quot;Score&quot;].mean()</pre><h3>22. GroupBy with Multiple Aggregation Functions</h3><p>Even at the basic level, we can calculate several summaries.</p><p>For example:</p><pre>students.groupby(&quot;Class&quot;)[&quot;Score&quot;].agg(<br>    [&quot;count&quot;, &quot;mean&quot;, &quot;min&quot;, &quot;max&quot;]<br>)</pre><p>This gives us several statistics for each class.</p><p>Conceptually:</p><pre>Class<br>  ↓<br>count<br>mean<br>min<br>max</pre><p>Although advanced aggregation will be explored more deeply in a later article, this pattern is useful to understand now.</p><h3>23. Understanding agg()</h3><p>The agg() method means:</p><blockquote><em>Apply one or more aggregation functions.</em></blockquote><p>For example:</p><pre>students.groupby(&quot;Class&quot;)[&quot;Score&quot;].agg(<br>    [&quot;mean&quot;, &quot;max&quot;]<br>)</pre><p>This calculates:</p><pre>Average score<br>Maximum score</pre><p>for each class.</p><p>The important idea is:</p><pre>groupby()<br>    ↓<br>group<br>    ↓<br>agg()<br>    ↓<br>multiple summaries</pre><p>More advanced agg() patterns will be covered later.</p><h3>24. Group Analysis</h3><p>GroupBy becomes powerful when we use it to answer analytical questions.</p><p>For example, with sales data:</p><pre>sales.groupby(&quot;Region&quot;)[&quot;Sales&quot;].sum()</pre><p>answers:</p><blockquote><em>Which regions generated the most total sales?</em></blockquote><p>With:</p><pre>sales.groupby(&quot;Region&quot;)[&quot;Sales&quot;].mean()</pre><p>we can ask:</p><blockquote><em>Which region has the highest average sale?</em></blockquote><p>With:</p><pre>sales.groupby(&quot;Region&quot;)[&quot;Sales&quot;].max()</pre><p>we can ask:</p><blockquote><em>Which region recorded the largest individual sale?</em></blockquote><p>The same dataset can therefore answer different questions depending on the aggregation.</p><h3>25. GroupBy Is About Questions, Not Just Syntax</h3><p>A common beginner mistake is learning:</p><pre>groupby()</pre><p>as a syntax pattern without understanding the analytical purpose.</p><p>Instead, start with the question:</p><blockquote><em>What do I want to compare?</em></blockquote><p>Then identify:</p><h3>Grouping column</h3><p>Who or what defines the groups?</p><pre>Region<br>Department<br>Category<br>Class</pre><h3>Analysis column</h3><p>What numerical information do I want to analyze?</p><pre>Sales<br>Salary<br>Score<br>Profit</pre><h3>Aggregation</h3><p>What do I want to calculate?</p><pre>sum<br>mean<br>count<br>min<br>max</pre><p>Then construct:</p><pre>df.groupby(&quot;group_column&quot;)[&quot;value_column&quot;].aggregation()</pre><h3>26. A Business Example</h3><p>Suppose:</p><pre>sales = pd.DataFrame({<br>    &quot;Region&quot;: [<br>        &quot;North&quot;,<br>        &quot;North&quot;,<br>        &quot;South&quot;,<br>        &quot;South&quot;,<br>        &quot;East&quot;,<br>        &quot;East&quot;<br>    ],<br>    &quot;Sales&quot;: [<br>        1000,<br>        1500,<br>        2000,<br>        2500,<br>        1200,<br>        1800<br>    ]<br>})</pre><h3>Total sales by region</h3><pre>sales.groupby(&quot;Region&quot;)[&quot;Sales&quot;].sum()</pre><h3>Average sales by region</h3><pre>sales.groupby(&quot;Region&quot;)[&quot;Sales&quot;].mean()</pre><h3>Number of sales records</h3><pre>sales.groupby(&quot;Region&quot;).size()</pre><h3>Largest sale</h3><pre>sales.groupby(&quot;Region&quot;)[&quot;Sales&quot;].max()</pre><h3>Smallest sale</h3><pre>sales.groupby(&quot;Region&quot;)[&quot;Sales&quot;].min()</pre><p>Now we have several perspectives on the same dataset.</p><h3>27. Combining GroupBy with Earlier Binning</h3><p>In Post #19, we learned:</p><pre>pd.cut()<br>pd.qcut()</pre><p>Suppose we create:</p><pre>students[&quot;Score_Group&quot;] = pd.cut(<br>    students[&quot;Score&quot;],<br>    bins=[0, 50, 60, 70, 80, 100],<br>    labels=[<br>        &quot;Fail&quot;,<br>        &quot;Pass&quot;,<br>        &quot;Good&quot;,<br>        &quot;Very Good&quot;,<br>        &quot;Excellent&quot;<br>    ]<br>)</pre><p>We can then analyze the groups:</p><pre>students.groupby(<br>    &quot;Score_Group&quot;,<br>    observed=True<br>)[&quot;Score&quot;].count()</pre><p>This answers:</p><blockquote><em>How many students belong to each performance group?</em></blockquote><p>This is a natural example of how Pandas operations build on one another.</p><h3>28. GroupBy with Categorical Data</h3><p>When grouping by a categorical column created using pd.cut() or pd.qcut(), you may see categories that have no observations.</p><p>For example:</p><pre>Fail<br>Pass<br>Good<br>Very Good<br>Excellent</pre><p>If there are no students in Fail, Pandas may still know that Fail is a defined category.</p><p>Using:</p><pre>observed=True</pre><p>asks Pandas to focus the groupby result on categories actually observed in the data.</p><p>For example:</p><pre>students.groupby(<br>    &quot;Score_Group&quot;,<br>    observed=True<br>)[&quot;Score&quot;].mean()</pre><p>This is particularly useful when working with categorical bins.</p><h3>29. GroupBy and Missing Values</h3><p>Suppose the grouping column contains missing values:</p><pre>df = pd.DataFrame({<br>    &quot;Department&quot;: [<br>        &quot;IT&quot;,<br>        &quot;HR&quot;,<br>        None,<br>        &quot;IT&quot;<br>    ],<br>    &quot;Salary&quot;: [<br>        70000,<br>        50000,<br>        60000,<br>        80000<br>    ]<br>})</pre><p>By default, missing grouping keys are generally excluded from the groups.</p><p>If you want to include missing values as their own group, use:</p><pre>df.groupby(<br>    &quot;Department&quot;,<br>    dropna=False<br>)[&quot;Salary&quot;].mean()</pre><p>This is important when missing group labels themselves carry analytical meaning.</p><h3>30. GroupBy vs Filtering</h3><p>Filtering answers:</p><blockquote><em>Which rows satisfy a condition?</em></blockquote><p>For example:</p><pre>students[<br>    students[&quot;Score&quot;] &gt; 80<br>]</pre><p>GroupBy answers:</p><blockquote><em>How do values behave within each group?</em></blockquote><p>For example:</p><pre>students.groupby(&quot;Class&quot;)[&quot;Score&quot;].mean()</pre><p>So:</p><pre>Filtering<br>→ Select rows</pre><pre>GroupBy<br>→ Analyze groups</pre><p>They are different operations, although they can be used together.</p><h3>31. GroupBy vs Sorting</h3><p>Sorting:</p><pre>df.sort_values(&quot;Sales&quot;)</pre><p>changes the order of records.</p><p>Grouping:</p><pre>df.groupby(&quot;Region&quot;)[&quot;Sales&quot;].sum()</pre><p>summarizes records by category.</p><p>So:</p><pre>sort_values()<br>→ Order the data</pre><pre>groupby()<br>→ Summarize the data by groups</pre><p>This distinction is important when choosing the right Pandas operation.</p><h3>32. GroupBy vs Binning</h3><p>Binning creates groups.</p><p>For example:</p><pre>df[&quot;Age_Group&quot;] = pd.cut(...)</pre><p>GroupBy analyzes those groups:</p><pre>df.groupby(&quot;Age_Group&quot;, observed=True)[&quot;Salary&quot;].mean()</pre><p>So the relationship is:</p><pre>Numerical Data<br>     ↓<br>pd.cut() / pd.qcut()<br>     ↓<br>Categorical Groups<br>     ↓<br>groupby()<br>     ↓<br>Aggregation<br>     ↓<br>Analysis</pre><p>This is a powerful Pandas workflow.</p><h3>33. GroupBy with count() vs size()</h3><p>This distinction deserves special attention.</p><p>Suppose:</p><pre>df = pd.DataFrame({<br>    &quot;Department&quot;: [<br>        &quot;IT&quot;,<br>        &quot;IT&quot;,<br>        &quot;HR&quot;<br>    ],<br>    &quot;Salary&quot;: [<br>        70000,<br>        None,<br>        50000<br>    ]<br>})</pre><p>Using:</p><pre>df.groupby(&quot;Department&quot;)[&quot;Salary&quot;].count()</pre><p>counts non-missing salary values.</p><p>For IT:</p><pre>70000<br>None</pre><p>only one value is non-missing.</p><p>But:</p><pre>df.groupby(&quot;Department&quot;).size()</pre><p>counts rows.</p><p>So IT has:</p><pre>2 rows</pre><p>Remember:</p><pre>count()<br>→ non-null values in selected column</pre><pre>size()<br>→ rows in each group</pre><h3>34. GroupBy with Different Numerical Columns</h3><p>Suppose:</p><pre>employees = pd.DataFrame({<br>    &quot;Department&quot;: [<br>        &quot;IT&quot;,<br>        &quot;IT&quot;,<br>        &quot;HR&quot;,<br>        &quot;HR&quot;<br>    ],<br>    &quot;Salary&quot;: [<br>        70000,<br>        80000,<br>        50000,<br>        55000<br>    ],<br>    &quot;Experience&quot;: [<br>        3,<br>        5,<br>        2,<br>        4<br>    ]<br>})</pre><p>We can analyze salary:</p><pre>employees.groupby(<br>    &quot;Department&quot;<br>)[&quot;Salary&quot;].mean()</pre><p>Or experience:</p><pre>employees.groupby(<br>    &quot;Department&quot;<br>)[&quot;Experience&quot;].mean()</pre><p>The grouping column stays the same while the analytical column changes.</p><h3>35. GroupBy with sum()</h3><p>Suppose we want total experience by department:</p><pre>employees.groupby(<br>    &quot;Department&quot;<br>)[&quot;Experience&quot;].sum()</pre><p>This may answer:</p><blockquote><em>What is the combined experience represented by employees in each department?</em></blockquote><p>Whether that statistic is useful depends on the analytical context.</p><p>This is why aggregation should always be tied to a meaningful question.</p><h3>36. GroupBy with mean() for Comparison</h3><p>Average values are often useful when groups have different sizes.</p><p>For example:</p><pre>employees.groupby(<br>    &quot;Department&quot;<br>)[&quot;Salary&quot;].mean()</pre><p>can compare typical salary levels between departments.</p><p>If one department has many more employees than another, total salary may not be an appropriate measure for comparing individual compensation.</p><h3>37. GroupBy with min() and max() Together</h3><p>We can also inspect the range within each group:</p><pre>employees.groupby(<br>    &quot;Department&quot;<br>)[&quot;Salary&quot;].agg(<br>    [&quot;min&quot;, &quot;max&quot;]<br>)</pre><p>This gives:</p><pre>Minimum salary<br>Maximum salary</pre><p>for each department.</p><p>The difference between these values can provide a basic view of salary spread.</p><h3>38. GroupBy Output as a DataFrame</h3><p>Suppose:</p><pre>summary = students.groupby(<br>    &quot;Class&quot;,<br>    as_index=False<br>)[&quot;Score&quot;].mean()</pre><p>We can then inspect:</p><pre>summary</pre><p>and continue working with the result.</p><p>For example:</p><pre>summary.sort_values(<br>    &quot;Score&quot;,<br>    ascending=False<br>)</pre><p>This combines two previously learned concepts:</p><pre>GroupBy<br>    ↓<br>Aggregation<br>    ↓<br>Sorting</pre><p>The result is a small analytical dataset.</p><h3>39. GroupBy as a Data Analysis Workflow</h3><p>A common workflow is:</p><pre>Load Data<br>    ↓<br>Inspect Data<br>    ↓<br>Clean Data<br>    ↓<br>Create Features / Categories<br>    ↓<br>Group Data<br>    ↓<br>Aggregate<br>    ↓<br>Compare Groups<br>    ↓<br>Extract Insights</pre><p>For example:</p><pre>students[&quot;Performance&quot;] = pd.cut(<br>    students[&quot;Score&quot;],<br>    bins=[0, 50, 60, 70, 80, 100],<br>    labels=[<br>        &quot;Fail&quot;,<br>        &quot;Pass&quot;,<br>        &quot;Good&quot;,<br>        &quot;Very Good&quot;,<br>        &quot;Excellent&quot;<br>    ]<br>)</pre><pre>summary = students.groupby(<br>    &quot;Performance&quot;,<br>    observed=True,<br>    as_index=False<br>)[&quot;Score&quot;].agg(<br>    [&quot;count&quot;, &quot;mean&quot;, &quot;min&quot;, &quot;max&quot;]<br>)</pre><p>Each step contributes to the final analysis.</p><h3>40. Common Mistakes</h3><h3>Mistake 1: Calling groupby() Without an Analysis</h3><p>This:</p><pre>df.groupby(&quot;Region&quot;)</pre><p>only creates the grouping structure.</p><p>Usually, you still need:</p><pre>.sum()<br>.mean()<br>.count()</pre><p>or another operation.</p><h3>Mistake 2: Grouping by the Wrong Column</h3><p>Always ask:</p><blockquote><em>What defines my groups?</em></blockquote><p>If you want regional analysis:</p><pre>df.groupby(&quot;Region&quot;)</pre><p>not:</p><pre>df.groupby(&quot;Sales&quot;)</pre><p>unless grouping by exact sales values is genuinely the goal.</p><h3>Mistake 3: Using count() When You Need Row Counts</h3><p>Remember:</p><pre>groupby()[&quot;Column&quot;].count()</pre><p>counts non-null values in that column.</p><p>If you need the number of rows:</p><pre>groupby().size()</pre><p>is often clearer.</p><h3>Mistake 4: Using Sum for Every Question</h3><p>A total is not always meaningful.</p><p>For example:</p><pre>df.groupby(&quot;Department&quot;)[&quot;Salary&quot;].sum()</pre><p>may tell you total payroll, but not typical employee salary.</p><p>For comparison, you may need:</p><pre>.mean()</pre><p>or:</p><pre>.median()</pre><p>Choose the aggregation based on the question.</p><h3>Mistake 5: Forgetting Missing Group Values</h3><p>Missing grouping keys may be excluded by default.</p><p>If they matter:</p><pre>dropna=False</pre><p>can be useful.</p><h3>41. A Simple GroupBy Mental Model</h3><p>Whenever you see:</p><pre>df.groupby(&quot;Category&quot;)[&quot;Sales&quot;].mean()</pre><p>read it in plain English:</p><blockquote><strong><em>Group the DataFrame by Category, take the Sales column, and calculate the mean for each category.</em></strong></blockquote><p>For:</p><pre>df.groupby(&quot;Region&quot;)[&quot;Revenue&quot;].sum()</pre><p>read:</p><blockquote><strong><em>Group by Region, take Revenue, and calculate total revenue for each region.</em></strong></blockquote><p>This way, Pandas syntax becomes easier to understand.</p><h3>42. Quick Reference</h3><h3>Basic grouping</h3><pre>df.groupby(&quot;Category&quot;)</pre><h3>Sum by group</h3><pre>df.groupby(&quot;Category&quot;)[&quot;Sales&quot;].sum()</pre><h3>Mean by group</h3><pre>df.groupby(&quot;Category&quot;)[&quot;Sales&quot;].mean()</pre><h3>Count non-null values</h3><pre>df.groupby(&quot;Category&quot;)[&quot;Sales&quot;].count()</pre><h3>Count rows</h3><pre>df.groupby(&quot;Category&quot;).size()</pre><h3>Minimum</h3><pre>df.groupby(&quot;Category&quot;)[&quot;Sales&quot;].min()</pre><h3>Maximum</h3><pre>df.groupby(&quot;Category&quot;)[&quot;Sales&quot;].max()</pre><h3>Median</h3><pre>df.groupby(&quot;Category&quot;)[&quot;Sales&quot;].median()</pre><h3>Standard deviation</h3><pre>df.groupby(&quot;Category&quot;)[&quot;Sales&quot;].std()</pre><h3>Multiple aggregations</h3><pre>df.groupby(&quot;Category&quot;)[&quot;Sales&quot;].agg(<br>    [&quot;count&quot;, &quot;mean&quot;, &quot;min&quot;, &quot;max&quot;]<br>)</pre><h3>Keep group column as a regular column</h3><pre>df.groupby(<br>    &quot;Category&quot;,<br>    as_index=False<br>)[&quot;Sales&quot;].mean()</pre><h3>Include missing group labels</h3><pre>df.groupby(<br>    &quot;Category&quot;,<br>    dropna=False<br>)[&quot;Sales&quot;].mean()</pre><h3>43. Final Takeaways</h3><p>groupby() is one of the most important Pandas tools for analytical work.</p><p>The core pattern is:</p><pre>Split<br>  ↓<br>Apply<br>  ↓<br>Combine</pre><p>In Pandas:</p><pre>df.groupby(&quot;group_column&quot;)</pre><p>creates groups.</p><p>Then an aggregation such as:</p><pre>.sum()<br>.mean()<br>.count()<br>.min()<br>.max()</pre><p>summarizes each group.</p><p>The general pattern is:</p><pre>df.groupby(<br>    &quot;group_column&quot;<br>)[&quot;value_column&quot;].aggregation()</pre><p>For example:</p><pre>df.groupby(<br>    &quot;Department&quot;<br>)[&quot;Salary&quot;].mean()</pre><p>means:</p><blockquote><em>Calculate the average salary for each department.</em></blockquote><p>The most important concepts to remember are:</p><ul><li>groupby() divides data into logical groups.</li><li>Aggregation summarizes each group.</li><li>sum() calculates totals.</li><li>mean() calculates averages.</li><li>count() counts non-missing values in the selected column.</li><li>size() counts rows.</li><li>min() and max() identify group-level extremes.</li><li>as_index=False keeps the grouping key as a normal column.</li><li>dropna=False can include missing group labels.</li><li>GroupBy works naturally with categories created using pd.cut() and pd.qcut().</li></ul><p>Most importantly:</p><blockquote><strong><em>GroupBy is not just a Pandas function. It is a way of asking structured questions about subsets of your data.</em></strong></blockquote><h3>44. Where We Go Next</h3><p>We now understand the foundation:</p><pre>Data<br> ↓<br>Group<br> ↓<br>Aggregate<br> ↓<br>Analyze</pre><p>But real-world analysis often requires more than one grouping column and more flexible aggregation rules.</p><p>In <strong>Post #21</strong>, we will move into:</p><blockquote><strong><em>Pandas GroupBy Advanced: Multiple Groups, </em></strong><strong><em>agg() and Custom Aggregation</em></strong></blockquote><p>We will learn how to:</p><pre>Group by multiple columns<br>Apply multiple aggregations<br>Use different functions on different columns<br>Rename aggregation results<br>Build more detailed analytical summaries</pre><p>This will take our basic groupby() knowledge into more realistic data-analysis workflows.</p><h3>About This Series</h3><p>This article is part of <strong>Data Analysis with Pandas</strong>, a practical series designed to help developers and beginners build strong foundations in Python data analysis.</p><p>The goal is not simply to memorize Pandas methods.</p><p>Instead, the series focuses on understanding how individual operations connect:</p><pre>Load<br>  ↓<br>Inspect<br>  ↓<br>Clean<br>  ↓<br>Transform<br>  ↓<br>Feature Engineer<br>  ↓<br>Group<br>  ↓<br>Aggregate<br>  ↓<br>Analyze<br>  ↓<br>Visualize</pre><p>Each article builds toward a complete data-analysis workflow.</p><h3>Author</h3><p><strong>Engr. Md. Monjur Bakth Mazumder</strong></p><p><strong>GitHub:</strong><br><a href="https://github.com/monjurmbm404/Python-Data-Science-Tools">https://github.com/monjurmbm404/Python-Data-Science-Tools</a></p><h3>Final Thought</h3><p>The real power of groupby() is not the syntax.</p><p>It is the ability to turn a large collection of individual records into meaningful comparisons.</p><p>Instead of asking:</p><pre>What happened to every row?</pre><p>we can ask:</p><pre>What happened in each region?<br>What happened in each department?<br>What happened in each class?<br>What happened in each category?</pre><p>That shift — from individual records to meaningful groups — is one of the foundations of practical data analysis.</p><p>And once you become comfortable with:</p><pre>df.groupby()</pre><p>Pandas becomes much more powerful for answering real-world analytical questions.</p><h3>Connect &amp; Follow</h3><h4>Professional Profiles</h4><ul><li>Portfolio: <a href="https://monjurmbm404.github.io/?source=user_about----------------------d31e4eba6166------------------------">https://monjurmbm404.github.io</a></li><li>GitHub: <a href="https://github.com/monjurmbm404?source=user_about----------------------d31e4eba6166------------------------">https://github.com/monjurmbm404</a></li><li>LinkedIn: <a href="https://www.linkedin.com/in/monjurmbm404?source=user_about----------------------d31e4eba6166------------------------">https://www.linkedin.com/in/monjurmbm404</a></li><li>Medium: <a href="https://medium.com/@monjurmbm404?source=user_about----------------------d31e4eba6166------------------------">https://medium.com/@monjurmbm404</a></li></ul><h4>Books &amp; Publications</h4><ul><li>Amazon: <a href="https://www.amazon.com/author/monjurmbm404?source=user_about----------------------d31e4eba6166------------------------">https://www.amazon.com/author/monjurmbm404</a></li></ul><h4>Competitive Programming</h4><ul><li>Codeforces: <a href="https://codeforces.com/profile/monjurmbm404?source=user_about----------------------d31e4eba6166------------------------">https://codeforces.com/profile/monjurmbm404</a></li><li>CodeChef: <a href="https://www.codechef.com/users/monjurmbm404?source=user_about----------------------d31e4eba6166------------------------">https://www.codechef.com/users/monjurmbm404</a></li><li>LeetCode: <a href="https://leetcode.com/u/monjurmbm404/?source=user_about----------------------d31e4eba6166------------------------">https://leetcode.com/u/monjurmbm404/</a></li><li>VJudge: <a href="https://vjudge.net/user/monjurmbm404?source=user_about----------------------d31e4eba6166------------------------">https://vjudge.net/user/monjurmbm404</a></li></ul><h4>Social Media</h4><ul><li>YouTube: <a href="https://youtube.com/@monjurmbm404?source=user_about----------------------d31e4eba6166------------------------">https://youtube.com/@monjurmbm404</a></li><li>X: <a href="https://x.com/monjurmbm404?source=user_about----------------------d31e4eba6166------------------------">https://x.com/monjurmbm404</a></li><li>Facebook: <a href="https://www.facebook.com/monjurmbm404?source=user_about----------------------d31e4eba6166------------------------">https://www.facebook.com/monjurmbm404</a></li></ul><h4>Connect with Me</h4><ul><li>Email<strong>:</strong> <a href="/service/mailto:monjurmbm404@gmail.com">monjurmbm404@gmail.com</a></li></ul><p>— <strong>Engr. Md Monjur Bakth Mazumder<br></strong>Software Engineer | Technical Author | Competitive Programmer | AI/ML Enthusiast</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=8ecac52a9cde" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Pandas Binning: cut() and qcut() for Categorical Data Analysis]]></title>
            <link>https://medium.com/@monjurmbm404/pandas-binning-cut-and-qcut-for-categorical-data-analysis-189c35604f9a?source=rss-d31e4eba6166------2</link>
            <guid isPermaLink="false">https://medium.com/p/189c35604f9a</guid>
            <dc:creator><![CDATA[Engr. Md Monjur Bakth Mazumder]]></dc:creator>
            <pubDate>Thu, 12 Nov 2026 14:01:03 GMT</pubDate>
            <atom:updated>2026-11-12T14:01:03.126Z</atom:updated>
            <content:encoded><![CDATA[<blockquote><strong><em>Series:</em></strong><em> Data Analysis with Pandas<br></em><strong><em>Part:</em></strong><em> 19</em></blockquote><blockquote>A Practical Guide to Converting Continuous Numerical Data into Meaningful Categories with pd.cut() and pd.qcut()</blockquote><p>Real-world datasets often contain continuous numerical variables such as:</p><pre>Age<br>Salary<br>Score<br>Income<br>Experience<br>Transaction Amount<br>Temperature</pre><p>These values are useful in their original numerical form.</p><p>But sometimes, analyzing every individual number is not the easiest way to understand a dataset.</p><p>For example, instead of looking at:</p><pre>18<br>21<br>24<br>29<br>34<br>41<br>48<br>57<br>63</pre><p>we may want:</p><pre>18–25   → Young Adult<br>26–35   → Adult<br>36–50   → Middle Age<br>51+     → Senior</pre><p>This process is called <strong>binning</strong>.</p><p>Pandas provides two especially useful functions for this:</p><pre>pd.cut()<br>pd.qcut()</pre><p>They allow us to transform continuous numerical values into categorical groups.</p><p>In this article, we will learn:</p><ul><li>What binning is</li><li>Why binning is useful</li><li>Continuous vs categorical data</li><li>pd.cut()</li><li>Equal-width binning</li><li>Age groups</li><li>Score groups</li><li>pd.qcut()</li><li>Quantile-based binning</li><li>cut() vs qcut()</li><li>Practical data-analysis examples</li><li>Common mistakes</li><li>When to use each method</li></ul><h3>1. What Is Binning?</h3><p><strong>Binning</strong> means dividing continuous numerical values into a set of intervals or groups.</p><p>Suppose we have student scores:</p><pre>scores = [<br>    45,<br>    52,<br>    61,<br>    67,<br>    73,<br>    81,<br>    89,<br>    95<br>]</pre><p>Instead of analyzing every individual score, we might create:</p><pre>0–49    → Fail<br>50–59   → Pass<br>60–69   → Good<br>70–79   → Very Good<br>80–100  → Excellent</pre><p>The original numerical variable:</p><pre>Score</pre><p>has now been transformed into a categorical representation:</p><pre>Score_Group</pre><p>Conceptually:</p><pre>Continuous Numerical Data<br>            ↓<br>          Binning<br>            ↓<br>Categorical Groups</pre><h3>2. Continuous Data vs Categorical Data</h3><p>Understanding this distinction is important.</p><h3>Continuous numerical data</h3><p>A numerical variable can contain many possible values.</p><p>Examples:</p><pre>Age<br>Salary<br>Height<br>Weight<br>Temperature<br>Score</pre><p>For example:</p><pre>Age<br>21<br>21.5<br>21.8<br>22<br>22.3</pre><h3>Categorical data</h3><p>Categorical data represents groups or labels.</p><p>Examples:</p><pre>Department<br>Gender<br>City<br>Grade<br>Age_Group<br>Score_Group</pre><p>For example:</p><pre>Age_Group<br>Young<br>Adult<br>Middle Age<br>Senior</pre><p>Binning provides a way to derive categorical information from numerical data.</p><h3>3. Why Use Binning?</h3><p>Binning can make numerical data easier to understand and analyze.</p><p>It can help with:</p><ul><li>descriptive analysis</li><li>reporting</li><li>dashboards</li><li>segmentation</li><li>visualization</li><li>frequency analysis</li><li>business analysis</li><li>exploratory data analysis</li></ul><p>For example, instead of asking:</p><blockquote><em>What is the average score of every individual student?</em></blockquote><p>we might ask:</p><blockquote><em>How many students are in each performance category?</em></blockquote><p>That can often be easier to communicate.</p><h3>4. pd.cut()</h3><p>The first important Pandas function is:</p><pre>pd.cut()</pre><p>pd.cut() divides numerical values according to explicitly defined intervals.</p><p>For example:</p><pre>import pandas as pd</pre><pre>scores = pd.Series([<br>    45,<br>    52,<br>    61,<br>    67,<br>    73,<br>    81,<br>    89,<br>    95<br>])</pre><p>We can create score groups:</p><pre>score_groups = pd.cut(<br>    scores,<br>    bins=[0, 50, 60, 70, 80, 100]<br>)</pre><p>Now each score belongs to one interval.</p><h3>5. Understanding bins</h3><p>The most important parameter in pd.cut() is:</p><pre>bins=</pre><p>For example:</p><pre>bins=[<br>    0,<br>    50,<br>    60,<br>    70,<br>    80,<br>    100<br>]</pre><p>These boundaries define the intervals.</p><p>Conceptually:</p><pre>0 ─── 50 ─── 60 ─── 70 ─── 80 ─── 100</pre><p>This creates five intervals.</p><p>By default, pd.cut() uses right-closed intervals:</p><pre>(0, 50]<br>(50, 60]<br>(60, 70]<br>(70, 80]<br>(80, 100]</pre><p>That means:</p><pre>50 → belongs to (0, 50]<br>60 → belongs to (50, 60]</pre><p>Understanding interval boundaries becomes important when values fall exactly on a boundary.</p><h3>6. Adding Meaningful Labels</h3><p>The interval representation is useful, but labels are often easier to understand.</p><pre>scores = pd.Series([<br>    45,<br>    52,<br>    61,<br>    67,<br>    73,<br>    81,<br>    89,<br>    95<br>])</pre><pre>scores_group = pd.cut(<br>    scores,<br>    bins=[0, 50, 60, 70, 80, 100],<br>    labels=[<br>        &quot;Fail&quot;,<br>        &quot;Pass&quot;,<br>        &quot;Good&quot;,<br>        &quot;Very Good&quot;,<br>        &quot;Excellent&quot;<br>    ]<br>)</pre><p>Now the result is easier to interpret:</p><pre>45 → Fail<br>52 → Pass<br>61 → Good<br>67 → Good<br>73 → Very Good<br>81 → Excellent<br>89 → Excellent<br>95 → Excellent</pre><p>This is one of the most common uses of pd.cut().</p><h3>7. Binning a DataFrame Column</h3><p>Instead of working with a standalone Series, we can add a categorical column to a DataFrame.</p><pre>students = pd.DataFrame({<br>    &quot;Name&quot;: [<br>        &quot;Alice&quot;,<br>        &quot;Bob&quot;,<br>        &quot;Charlie&quot;,<br>        &quot;David&quot;,<br>        &quot;Emma&quot;<br>    ],<br>    &quot;Score&quot;: [<br>        45,<br>        58,<br>        67,<br>        82,<br>        94<br>    ]<br>})</pre><p>Create a new column:</p><pre>students[&quot;Score_Group&quot;] = pd.cut(<br>    students[&quot;Score&quot;],<br>    bins=[0, 50, 60, 70, 80, 100],<br>    labels=[<br>        &quot;Fail&quot;,<br>        &quot;Pass&quot;,<br>        &quot;Good&quot;,<br>        &quot;Very Good&quot;,<br>        &quot;Excellent&quot;<br>    ]<br>)</pre><p>Now the DataFrame contains:</p><pre>Name<br>Score<br>Score_Group</pre><p>The original score is preserved.</p><p>This is generally preferable to replacing the original numerical feature immediately.</p><h3>8. Continuous → Categorical</h3><p>This transformation can be represented as:</p><pre>Score<br>  ↓<br>45<br>58<br>67<br>82<br>94<br>  ↓<br>Binning<br>  ↓<br>Fail<br>Pass<br>Good<br>Excellent<br>Excellent</pre><p>The important idea is:</p><blockquote><strong><em>Binning converts numerical ranges into categorical groups.</em></strong></blockquote><p>The original score contains precise numerical information.</p><p>The new category provides a higher-level interpretation.</p><h3>9. Age Grouping with pd.cut()</h3><p>Age is another common variable for binning.</p><p>Suppose:</p><pre>df = pd.DataFrame({<br>    &quot;Age&quot;: [<br>        18,<br>        22,<br>        27,<br>        34,<br>        41,<br>        49,<br>        56,<br>        68<br>    ]<br>})</pre><p>We can define age groups:</p><pre>df[&quot;Age_Group&quot;] = pd.cut(<br>    df[&quot;Age&quot;],<br>    bins=[<br>        0,<br>        18,<br>        30,<br>        45,<br>        60,<br>        100<br>    ],<br>    labels=[<br>        &quot;Teen&quot;,<br>        &quot;Young Adult&quot;,<br>        &quot;Adult&quot;,<br>        &quot;Middle Age&quot;,<br>        &quot;Senior&quot;<br>    ]<br>)</pre><p>Now we have:</p><pre>Age<br>↓<br>Age_Group</pre><p>For example:</p><pre>22 → Young Adult<br>34 → Adult<br>49 → Middle Age<br>68 → Senior</pre><p>The exact age boundaries should always reflect the purpose of the analysis.</p><h3>10. include_lowest=True</h3><p>By default, the first interval may exclude its lower boundary.</p><p>If you want the lowest value to be included explicitly, use:</p><pre>df[&quot;Age_Group&quot;] = pd.cut(<br>    df[&quot;Age&quot;],<br>    bins=[0, 18, 30, 45, 60, 100],<br>    labels=[<br>        &quot;Teen&quot;,<br>        &quot;Young Adult&quot;,<br>        &quot;Adult&quot;,<br>        &quot;Middle Age&quot;,<br>        &quot;Senior&quot;<br>    ],<br>    include_lowest=True<br>)</pre><p>This is useful when your first boundary represents the actual minimum of the intended range.</p><h3>11. Changing Interval Closure with right=False</h3><p>By default:</p><pre>pd.cut()</pre><p>uses right-closed intervals.</p><p>If you want left-closed intervals instead, use:</p><pre>pd.cut(<br>    df[&quot;Age&quot;],<br>    bins=[0, 20, 30, 40, 50],<br>    right=False<br>)</pre><p>The intervals become conceptually:</p><pre>[0, 20)<br>[20, 30)<br>[30, 40)<br>[40, 50)</pre><p>Now:</p><pre>20 → belongs to [20, 30)</pre><p>This can be useful when your business or analytical definition naturally uses left-closed ranges.</p><h3>12. Equal-Width Binning</h3><p>One important concept behind pd.cut() is <strong>equal-width binning</strong>.</p><p>Suppose the numerical range is:</p><pre>0 → 100</pre><p>and we want four bins.</p><p>The total range is:</p><pre>100</pre><p>Divide by:</p><pre>4</pre><p>and each bin has width:</p><pre>25</pre><p>So we get approximately:</p><pre>0–25<br>25–50<br>50–75<br>75–100</pre><p>Each interval has the same numerical width.</p><p>This is called:</p><blockquote><strong><em>Equal-width binning</em></strong></blockquote><h3>13. Let pd.cut() Create Equal-Width Bins Automatically</h3><p>You don’t always have to manually specify the boundaries.</p><p>For example:</p><pre>scores = pd.Series([<br>    10,<br>    20,<br>    35,<br>    45,<br>    55,<br>    70,<br>    80,<br>    95<br>])</pre><p>We can request four bins:</p><pre>pd.cut(<br>    scores,<br>    bins=4<br>)</pre><p>Pandas calculates the interval boundaries across the observed range.</p><p>This is useful when you want equal-width numerical intervals without manually defining every boundary.</p><h3>14. Equal-Width Binning with Labels</h3><p>We can also provide labels:</p><pre>scores_group = pd.cut(<br>    scores,<br>    bins=4,<br>    labels=[<br>        &quot;Low&quot;,<br>        &quot;Medium&quot;,<br>        &quot;High&quot;,<br>        &quot;Very High&quot;<br>    ]<br>)</pre><p>Now the numerical range is divided into four equal-width intervals and represented using meaningful labels.</p><h3>15. When Equal-Width Binning Is Useful</h3><p>Equal-width bins work well when the numerical range itself has meaningful interpretation.</p><p>For example:</p><pre>Temperature<br>0–10<br>10–20<br>20–30<br>30–40</pre><p>or:</p><pre>Age<br>0–20<br>20–40<br>40–60<br>60–80</pre><p>The important characteristic is:</p><blockquote><em>Each interval covers approximately the same numerical range.</em></blockquote><h3>16. A Problem with Equal-Width Binning</h3><p>Equal-width bins do not guarantee that each group contains a similar number of observations.</p><p>Suppose:</p><pre>Scores:<br>10, 11, 12, 13, 14, 15, 90, 95</pre><p>If we create four equal-width bins, most observations may fall into the lower bins while only a few observations fall into the upper bins.</p><p>You might get something conceptually like:</p><pre>Bin 1 → many values<br>Bin 2 → very few values<br>Bin 3 → 0 values<br>Bin 4 → 2 values</pre><p>This is where:</p><pre>pd.qcut()</pre><p>becomes useful.</p><h3>17. What Is pd.qcut()?</h3><p>pd.qcut() performs <strong>quantile-based binning</strong>.</p><p>Instead of dividing the numerical range into equal-width intervals, it attempts to divide the observations into groups with approximately equal numbers of observations.</p><p>For example:</p><pre>scores = pd.Series([<br>    10,<br>    20,<br>    30,<br>    40,<br>    50,<br>    60,<br>    70,<br>    80,<br>    90,<br>    100<br>])</pre><p>We can create four quantile groups:</p><pre>pd.qcut(<br>    scores,<br>    q=4<br>)</pre><p>This creates approximately:</p><pre>Q1 → lowest 25%<br>Q2 → next 25%<br>Q3 → next 25%<br>Q4 → highest 25%</pre><h3>18. Quantile-Based Binning</h3><p>The word <strong>quantile</strong> is important here.</p><p>If we use:</p><pre>q=4</pre><p>we are asking for four groups based on the distribution of the observations.</p><p>Conceptually:</p><pre>Lowest 25%<br>     ↓<br>Q1</pre><pre>Next 25%<br>     ↓<br>Q2</pre><pre>Next 25%<br>     ↓<br>Q3</pre><pre>Highest 25%<br>     ↓<br>Q4</pre><p>The intervals themselves may have very different numerical widths.</p><p>That is the key difference from equal-width binning.</p><h3>19. qcut() with Labels</h3><p>We can give meaningful labels:</p><pre>scores_group = pd.qcut(<br>    scores,<br>    q=4,<br>    labels=[<br>        &quot;Q1&quot;,<br>        &quot;Q2&quot;,<br>        &quot;Q3&quot;,<br>        &quot;Q4&quot;<br>    ]<br>)</pre><p>Or:</p><pre>scores_group = pd.qcut(<br>    scores,<br>    q=4,<br>    labels=[<br>        &quot;Low&quot;,<br>        &quot;Medium-Low&quot;,<br>        &quot;Medium-High&quot;,<br>        &quot;High&quot;<br>    ]<br>)</pre><p>The labels describe the relative position of each observation in the distribution.</p><h3>20. qcut() for Customer Segmentation</h3><p>Suppose we have customer spending:</p><pre>customers = pd.DataFrame({<br>    &quot;Customer&quot;: [<br>        &quot;A&quot;,<br>        &quot;B&quot;,<br>        &quot;C&quot;,<br>        &quot;D&quot;,<br>        &quot;E&quot;,<br>        &quot;F&quot;,<br>        &quot;G&quot;,<br>        &quot;H&quot;<br>    ],<br>    &quot;Spending&quot;: [<br>        100,<br>        150,<br>        200,<br>        300,<br>        500,<br>        700,<br>        1000,<br>        2500<br>    ]<br>})</pre><p>We can divide customers into four spending groups:</p><pre>customers[&quot;Spending_Group&quot;] = pd.qcut(<br>    customers[&quot;Spending&quot;],<br>    q=4,<br>    labels=[<br>        &quot;Low&quot;,<br>        &quot;Medium-Low&quot;,<br>        &quot;Medium-High&quot;,<br>        &quot;High&quot;<br>    ]<br>)</pre><p>This creates relative customer segments.</p><p>For example:</p><pre>Low<br>Medium-Low<br>Medium-High<br>High</pre><p>The groups are based on the distribution of customers rather than fixed spending ranges.</p><h3>21. Age Groups with Quantiles</h3><p>Suppose we want to divide a population into four equally sized age segments.</p><pre>df[&quot;Age_Quartile&quot;] = pd.qcut(<br>    df[&quot;Age&quot;],<br>    q=4,<br>    labels=[<br>        &quot;Q1&quot;,<br>        &quot;Q2&quot;,<br>        &quot;Q3&quot;,<br>        &quot;Q4&quot;<br>    ]<br>)</pre><p>Here:</p><pre>Q1 → youngest group<br>Q2 → lower-middle group<br>Q3 → upper-middle group<br>Q4 → oldest group</pre><p>Notice that the age boundaries are determined by the actual distribution of the data.</p><h3>22. cut() vs qcut()</h3><p>The easiest way to remember the difference is:</p><pre>pd.cut()<br>→ Divide the numerical range</pre><pre>pd.qcut()<br>→ Divide the observations by quantiles</pre><p>So:</p><pre>cut()<br>→ Equal-width / manually defined intervals</pre><pre>qcut()<br>→ Quantile-based intervals</pre><p>This is the core distinction.</p><h3>23. Example: Same Data, Different Binning</h3><p>Consider:</p><pre>scores = pd.Series([<br>    10,<br>    12,<br>    15,<br>    18,<br>    20,<br>    22,<br>    80,<br>    85,<br>    90,<br>    95<br>])</pre><h3>Using cut()</h3><pre>pd.cut(<br>    scores,<br>    bins=4<br>)</pre><p>The range is divided into equal-width intervals.</p><p>Because the data contains a large gap between 22 and 80, some bins may contain very few or no observations.</p><h3>Using qcut()</h3><pre>pd.qcut(<br>    scores,<br>    q=4<br>)</pre><p>The observations are divided according to their rank in the distribution.</p><p>The groups are therefore more balanced in terms of observation counts.</p><h3>24. When Should You Use pd.cut()?</h3><p>Use pd.cut() when the <strong>actual numerical boundaries matter</strong>.</p><p>Examples:</p><pre>Age:<br>0–18<br>19–30<br>31–45<br>46–60<br>61+</pre><pre>Score:<br>0–49<br>50–59<br>60–69<br>70–79<br>80–100</pre><pre>Income:<br>0–30K<br>30K–60K<br>60K–100K<br>100K+</pre><p>These categories have business or domain meaning.</p><p>In such situations, manually defined boundaries are often more meaningful than quantiles.</p><h3>25. When Should You Use pd.qcut()?</h3><p>Use pd.qcut() when you want groups based on <strong>relative position in the dataset</strong>.</p><p>Examples:</p><ul><li>Top 10% customers</li><li>Bottom 25% students</li><li>Customer spending quartiles</li><li>Income percentiles</li><li>Ranking observations by distribution</li></ul><p>For example:</p><pre>df[&quot;Customer_Segment&quot;] = pd.qcut(<br>    df[&quot;Revenue&quot;],<br>    q=4,<br>    labels=[<br>        &quot;Low&quot;,<br>        &quot;Medium-Low&quot;,<br>        &quot;Medium-High&quot;,<br>        &quot;High&quot;<br>    ]<br>)</pre><p>Here the exact revenue boundaries are determined by the data.</p><h3>26. Percentile-Based Binning</h3><p>qcut() can also be used for more granular segmentation.</p><p>For example:</p><pre>df[&quot;Revenue_Decile&quot;] = pd.qcut(<br>    df[&quot;Revenue&quot;],<br>    q=10,<br>    labels=False<br>)</pre><p>This creates approximately ten groups:</p><pre>0<br>1<br>2<br>...<br>9</pre><p>These represent decile positions.</p><p>You can also provide labels:</p><pre>df[&quot;Revenue_Decile&quot;] = pd.qcut(<br>    df[&quot;Revenue&quot;],<br>    q=10,<br>    labels=[<br>        &quot;D1&quot;,<br>        &quot;D2&quot;,<br>        &quot;D3&quot;,<br>        &quot;D4&quot;,<br>        &quot;D5&quot;,<br>        &quot;D6&quot;,<br>        &quot;D7&quot;,<br>        &quot;D8&quot;,<br>        &quot;D9&quot;,<br>        &quot;D10&quot;<br>    ]<br>)</pre><h3>27. labels=False</h3><p>Sometimes you don’t want text labels.</p><p>For example:</p><pre>df[&quot;Score_Bin&quot;] = pd.cut(<br>    df[&quot;Score&quot;],<br>    bins=4,<br>    labels=False<br>)</pre><p>Pandas returns integer-coded bin positions.</p><p>Similarly:</p><pre>df[&quot;Score_Quartile&quot;] = pd.qcut(<br>    df[&quot;Score&quot;],<br>    q=4,<br>    labels=False<br>)</pre><p>This can be useful for further numerical processing.</p><p>However, remember that these integers represent categories or ordered bins; they do not automatically imply equal numerical differences between categories.</p><h3>28. Checking Bin Frequencies</h3><p>After binning, one of the most important steps is checking how many observations fall into each group.</p><p>For example:</p><pre>df[&quot;Score_Group&quot;].value_counts()</pre><p>You can also preserve the bin order:</p><pre>df[&quot;Score_Group&quot;].value_counts(<br>    sort=False<br>)</pre><p>This is useful because the categories may have a natural order such as:</p><pre>Fail<br>Pass<br>Good<br>Very Good<br>Excellent</pre><h3>29. Binning and value_counts()</h3><p>Binning becomes particularly powerful when combined with frequency analysis.</p><p>For example:</p><pre>score_distribution = (<br>    students[&quot;Score_Group&quot;]<br>    .value_counts(sort=False)<br>)</pre><p>Now we can answer:</p><blockquote><em>How many students belong to each performance group?</em></blockquote><p>This is often more useful for reporting than looking at every individual score.</p><h3>30. Binning with GroupBy</h3><p>Once a categorical bin has been created, it can be used with:</p><pre>groupby()</pre><p>For example:</p><pre>students.groupby(<br>    &quot;Score_Group&quot;,<br>    observed=True<br>)[&quot;Score&quot;].mean()</pre><p>This can answer:</p><blockquote><em>What is the average score within each score group?</em></blockquote><p>Similarly:</p><pre>customers.groupby(<br>    &quot;Spending_Group&quot;,<br>    observed=True<br>)[&quot;Spending&quot;].mean()</pre><p>can help compare average spending across customer segments.</p><p>Using observed=True is useful with categorical groupers when you want the result focused on categories actually observed in the data.</p><h3>31. Binning and Aggregation</h3><p>Suppose we have:</p><pre>sales = pd.DataFrame({<br>    &quot;Sales&quot;: [<br>        120,<br>        250,<br>        450,<br>        600,<br>        900,<br>        1200<br>    ]<br>})</pre><p>Create sales categories:</p><pre>sales[&quot;Sales_Group&quot;] = pd.cut(<br>    sales[&quot;Sales&quot;],<br>    bins=[<br>        0,<br>        300,<br>        700,<br>        1500<br>    ],<br>    labels=[<br>        &quot;Low&quot;,<br>        &quot;Medium&quot;,<br>        &quot;High&quot;<br>    ]<br>)</pre><p>Then:</p><pre>sales.groupby(<br>    &quot;Sales_Group&quot;,<br>    observed=True<br>)[&quot;Sales&quot;].agg(<br>    [&quot;count&quot;, &quot;mean&quot;, &quot;sum&quot;]<br>)</pre><p>Now the bins become analytical groups.</p><p>This is where binning becomes more than just categorization.</p><h3>32. Binning for Visualization</h3><p>Binned data can also make visualization easier.</p><p>For example, instead of plotting hundreds of unique ages, you might analyze:</p><pre>Age Group<br>18–25<br>26–35<br>36–45<br>46–55<br>56+</pre><p>This can make patterns easier to communicate in:</p><ul><li>bar charts</li><li>histograms</li><li>dashboards</li><li>reports</li></ul><p>The appropriate visualization still depends on the analytical question.</p><h3>33. Binning Is Not the Same as a Histogram</h3><p>A histogram groups numerical observations into bins for visualization.</p><p>Binning with:</p><pre>pd.cut()</pre><p>or:</p><pre>pd.qcut()</pre><p>creates a new categorical representation that can be stored and analyzed.</p><p>For example:</p><pre>df[&quot;Age_Group&quot;] = pd.cut(...)</pre><p>creates an actual DataFrame column.</p><p>So:</p><pre>Histogram<br>→ primarily a visualization</pre><pre>Binning<br>→ data transformation / categorization</pre><p>The two concepts are related but not identical.</p><h3>34. Handling Boundary Values Carefully</h3><p>Suppose:</p><pre>df[&quot;Score&quot;] = [<br>    50,<br>    60,<br>    70,<br>    80,<br>    90<br>]</pre><p>and:</p><pre>pd.cut(<br>    df[&quot;Score&quot;],<br>    bins=[0, 50, 60, 70, 80, 100]<br>)</pre><p>You need to understand which interval contains values exactly equal to the boundaries.</p><p>By default, the intervals are right-closed.</p><p>If your business rule says:</p><pre>50–59<br>60–69<br>70–79<br>80–100</pre><p>you need to define your bins and interval closure carefully.</p><p>Never assume boundary behavior without checking it.</p><h3>35. Custom Business Categories</h3><p>Binning is especially useful when categories come from business rules.</p><p>For example:</p><pre>df[&quot;Customer_Value&quot;] = pd.cut(<br>    df[&quot;Annual_Spending&quot;],<br>    bins=[<br>        0,<br>        10000,<br>        50000,<br>        100000,<br>        float(&quot;inf&quot;)<br>    ],<br>    labels=[<br>        &quot;Low Value&quot;,<br>        &quot;Medium Value&quot;,<br>        &quot;High Value&quot;,<br>        &quot;Premium&quot;<br>    ],<br>    include_lowest=True<br>)</pre><p>This creates business-oriented customer segments.</p><p>The boundaries are meaningful because they were defined according to a business rule.</p><h3>36. Using float(&quot;inf&quot;)</h3><p>Sometimes the final category has no practical upper limit.</p><p>For example:</p><pre>100,000+</pre><p>Instead of choosing an arbitrary maximum, we can use:</p><pre>float(&quot;inf&quot;)</pre><p>Example:</p><pre>bins = [<br>    0,<br>    10000,<br>    50000,<br>    100000,<br>    float(&quot;inf&quot;)<br>]</pre><p>This means:</p><pre>0–10K<br>10K–50K<br>50K–100K<br>100K+</pre><p>This is useful for open-ended categories.</p><h3>37. Handling Values Outside the Defined Range</h3><p>Suppose:</p><pre>df[&quot;Age&quot;] = [<br>    18,<br>    25,<br>    40,<br>    70,<br>    110<br>]</pre><p>and your bins only cover:</p><pre>bins = [<br>    0,<br>    18,<br>    30,<br>    50,<br>    80<br>]</pre><p>The value 110 does not belong to any interval.</p><p>pd.cut() will therefore produce:</p><pre>NaN</pre><p>for that value.</p><p>Always make sure your bin boundaries cover the expected data range.</p><h3>38. duplicates=&quot;drop&quot; with qcut()</h3><p>qcut() depends on quantile boundaries.</p><p>If many values are identical, some quantile boundaries can become identical too.</p><p>For example:</p><pre>scores = pd.Series([<br>    50,<br>    50,<br>    50,<br>    60,<br>    60,<br>    70,<br>    70,<br>    70<br>])</pre><p>Trying to create many quantile bins can result in duplicate boundaries.</p><p>In such cases, you may encounter an error.</p><p>One option is:</p><pre>pd.qcut(<br>    scores,<br>    q=4,<br>    duplicates=&quot;drop&quot;<br>)</pre><p>This tells Pandas to drop duplicate bin edges rather than failing because of them.</p><p>However, note that the requested number of bins may be reduced.</p><h3>39. precision</h3><p>For interval-based output, you can control displayed precision:</p><pre>pd.cut(<br>    df[&quot;Age&quot;],<br>    bins=4,<br>    precision=1<br>)</pre><p>This mainly affects how interval boundaries are displayed.</p><p>It does not change the underlying conceptual binning logic.</p><h3>40. Ordered Categories</h3><p>The output of pd.cut() and pd.qcut() is typically categorical and ordered.</p><p>For example:</p><pre>Low<br>Medium<br>High</pre><p>has a natural order.</p><p>This is useful because:</p><pre>df[&quot;Score_Group&quot;].dtype</pre><p>will show a categorical dtype.</p><p>You can inspect the categories:</p><pre>df[&quot;Score_Group&quot;].cat.categories</pre><p>This helps when working with ordered categorical data.</p><h3>41. Practical Example: Student Performance</h3><p>Let’s build a more complete example.</p><pre>students = pd.DataFrame({<br>    &quot;Name&quot;: [<br>        &quot;Alice&quot;,<br>        &quot;Bob&quot;,<br>        &quot;Charlie&quot;,<br>        &quot;David&quot;,<br>        &quot;Emma&quot;,<br>        &quot;Frank&quot;<br>    ],<br>    &quot;Score&quot;: [<br>        45,<br>        58,<br>        67,<br>        74,<br>        86,<br>        95<br>    ]<br>})</pre><p>Create performance groups:</p><pre>students[&quot;Performance&quot;] = pd.cut(<br>    students[&quot;Score&quot;],<br>    bins=[<br>        0,<br>        50,<br>        60,<br>        70,<br>        80,<br>        100<br>    ],<br>    labels=[<br>        &quot;Fail&quot;,<br>        &quot;Pass&quot;,<br>        &quot;Good&quot;,<br>        &quot;Very Good&quot;,<br>        &quot;Excellent&quot;<br>    ],<br>    include_lowest=True<br>)</pre><p>Now count students:</p><pre>students[&quot;Performance&quot;].value_counts(<br>    sort=False<br>)</pre><p>This gives us a simple performance distribution.</p><h3>42. Practical Example: Age Segmentation</h3><pre>people = pd.DataFrame({<br>    &quot;Age&quot;: [<br>        19,<br>        22,<br>        28,<br>        34,<br>        42,<br>        51,<br>        63,<br>        72<br>    ]<br>})</pre><p>Create groups:</p><pre>people[&quot;Age_Group&quot;] = pd.cut(<br>    people[&quot;Age&quot;],<br>    bins=[<br>        0,<br>        18,<br>        30,<br>        45,<br>        60,<br>        float(&quot;inf&quot;)<br>    ],<br>    labels=[<br>        &quot;Teen&quot;,<br>        &quot;Young Adult&quot;,<br>        &quot;Adult&quot;,<br>        &quot;Middle Age&quot;,<br>        &quot;Senior&quot;<br>    ],<br>    include_lowest=True<br>)</pre><p>Now we can analyze:</p><pre>people[&quot;Age_Group&quot;].value_counts(<br>    sort=False<br>)</pre><p>This turns individual ages into meaningful segments.</p><h3>43. Practical Example: Customer Quartiles</h3><pre>customers = pd.DataFrame({<br>    &quot;Customer&quot;: [<br>        &quot;A&quot;,<br>        &quot;B&quot;,<br>        &quot;C&quot;,<br>        &quot;D&quot;,<br>        &quot;E&quot;,<br>        &quot;F&quot;,<br>        &quot;G&quot;,<br>        &quot;H&quot;<br>    ],<br>    &quot;Revenue&quot;: [<br>        100,<br>        150,<br>        200,<br>        250,<br>        400,<br>        600,<br>        1000,<br>        2000<br>    ]<br>})</pre><p>Create revenue quartiles:</p><pre>customers[&quot;Revenue_Group&quot;] = pd.qcut(<br>    customers[&quot;Revenue&quot;],<br>    q=4,<br>    labels=[<br>        &quot;Low&quot;,<br>        &quot;Medium-Low&quot;,<br>        &quot;Medium-High&quot;,<br>        &quot;High&quot;<br>    ]<br>)</pre><p>Now we can analyze customer groups based on relative revenue position.</p><h3>44. Choosing Between cut() and qcut()</h3><p>Ask yourself one question:</p><blockquote><strong><em>Do I care about fixed numerical ranges or balanced groups?</em></strong></blockquote><p>If you care about:</p><pre>0–50<br>50–100<br>100–150<br>150–200</pre><p>use:</p><pre>pd.cut()</pre><p>If you care about:</p><pre>Bottom 25%<br>Next 25%<br>Next 25%<br>Top 25%</pre><p>use:</p><pre>pd.qcut()</pre><p>A simple mental model:</p><pre>cut()<br>→ &quot;Where does this value fall on the numerical scale?&quot;</pre><pre>qcut()<br>→ &quot;Where does this value rank within the distribution?&quot;</pre><h3>45. Common Mistakes</h3><h3>Mistake 1: Choosing Arbitrary Bins</h3><p>Don’t create categories without understanding the analytical purpose.</p><p>Bad reasoning:</p><pre>Let&#39;s divide everything into five groups because five looks good.</pre><p>Better reasoning:</p><pre>What question am I trying to answer?</pre><h3>Mistake 2: Forgetting Boundary Behavior</h3><p>Always understand:</p><pre>right=<br>include_lowest=</pre><p>when exact boundaries matter.</p><h3>Mistake 3: Assuming qcut() Creates Equal-Width Ranges</h3><p>It does not.</p><p>qcut() creates quantile-based groups.</p><p>The numerical width of each group can be very different.</p><h3>Mistake 4: Assuming cut() Creates Equal Number of Observations</h3><p>It does not.</p><p>Equal-width intervals can contain very different numbers of observations.</p><h3>Mistake 5: Ignoring Duplicate Values with qcut()</h3><p>If many observations have the same value, quantile boundaries may overlap.</p><p>Consider:</p><pre>duplicates=&quot;drop&quot;</pre><p>when appropriate.</p><h3>Mistake 6: Throwing Away the Original Numerical Column</h3><p>The original numerical variable often contains valuable information.</p><p>Prefer:</p><pre>df[&quot;Age_Group&quot;] = ...</pre><p>instead of immediately replacing:</p><pre>df[&quot;Age&quot;]</pre><p>unless you have a specific reason.</p><h3>46. A Practical Binning Workflow</h3><p>A useful workflow is:</p><pre>Load Data<br>    ↓<br>Inspect Numerical Variable<br>    ↓<br>Understand Distribution<br>    ↓<br>Define Analytical Goal<br>    ↓<br>Choose cut() or qcut()<br>    ↓<br>Define Bins / Quantiles<br>    ↓<br>Create Categorical Column<br>    ↓<br>Check Value Counts<br>    ↓<br>Validate Boundaries<br>    ↓<br>Analyze by Group</pre><p>For example:</p><pre>df[&quot;Age_Group&quot;] = pd.cut(...)</pre><p>then:</p><pre>df[&quot;Age_Group&quot;].value_counts(<br>    sort=False<br>)</pre><p>and finally:</p><pre>df.groupby(<br>    &quot;Age_Group&quot;,<br>    observed=True<br>)[&quot;Salary&quot;].mean()</pre><p>This turns a simple transformation into a useful analytical workflow.</p><h3>47. Quick Reference</h3><h3>Manual intervals with cut()</h3><pre>df[&quot;Age_Group&quot;] = pd.cut(<br>    df[&quot;Age&quot;],<br>    bins=[0, 18, 30, 45, 60, float(&quot;inf&quot;)],<br>    labels=[<br>        &quot;Teen&quot;,<br>        &quot;Young Adult&quot;,<br>        &quot;Adult&quot;,<br>        &quot;Middle Age&quot;,<br>        &quot;Senior&quot;<br>    ],<br>    include_lowest=True<br>)</pre><h3>Equal-width bins</h3><pre>df[&quot;Score_Group&quot;] = pd.cut(<br>    df[&quot;Score&quot;],<br>    bins=4<br>)</pre><h3>Quantile-based bins</h3><pre>df[&quot;Score_Quartile&quot;] = pd.qcut(<br>    df[&quot;Score&quot;],<br>    q=4<br>)</pre><h3>Quantile bins with labels</h3><pre>df[&quot;Score_Quartile&quot;] = pd.qcut(<br>    df[&quot;Score&quot;],<br>    q=4,<br>    labels=[<br>        &quot;Q1&quot;,<br>        &quot;Q2&quot;,<br>        &quot;Q3&quot;,<br>        &quot;Q4&quot;<br>    ]<br>)</pre><h3>Integer bin codes</h3><pre>df[&quot;Score_Bin&quot;] = pd.cut(<br>    df[&quot;Score&quot;],<br>    bins=4,<br>    labels=False<br>)</pre><h3>Count categories</h3><pre>df[&quot;Score_Group&quot;].value_counts(<br>    sort=False<br>)</pre><h3>Analyze by bins</h3><pre>df.groupby(<br>    &quot;Score_Group&quot;,<br>    observed=True<br>)[&quot;Score&quot;].mean()</pre><h3>Handle duplicate quantile edges</h3><pre>df[&quot;Score_Quartile&quot;] = pd.qcut(<br>    df[&quot;Score&quot;],<br>    q=4,<br>    duplicates=&quot;drop&quot;<br>)</pre><h3>48. Final Takeaways</h3><p>Binning is a powerful technique for converting continuous numerical data into meaningful categories.</p><p>The two most important Pandas functions are:</p><pre>pd.cut()<br>pd.qcut()</pre><p>Remember the core distinction:</p><pre>pd.cut()<br>→ fixed numerical intervals<br>→ manually defined or equal-width bins</pre><pre>pd.qcut()<br>→ quantile-based intervals<br>→ approximately balanced number of observations</pre><p>Use pd.cut() when numerical boundaries have meaning.</p><p>For example:</p><pre>Score:<br>0–49<br>50–59<br>60–69<br>70–79<br>80–100</pre><p>Use pd.qcut() when relative position matters.</p><p>For example:</p><pre>Bottom 25%<br>Next 25%<br>Next 25%<br>Top 25%</pre><p>Binning is particularly useful for:</p><ul><li>age segmentation</li><li>score categories</li><li>customer segmentation</li><li>income groups</li><li>sales ranges</li><li>percentile analysis</li><li>EDA</li><li>reporting</li><li>dashboards</li></ul><p>But remember:</p><blockquote><strong><em>Binning simplifies numerical information by grouping values. That simplification should be intentional.</em></strong></blockquote><p>A precise numerical value can contain more information than its category.</p><p>For example:</p><pre>Score = 79</pre><p>contains more information than:</p><pre>Score_Group = &quot;Very Good&quot;</pre><p>So keep the original numerical feature whenever it remains useful.</p><h3>49. Where We Go Next</h3><p>We have now learned how to:</p><pre>Create Features<br>      ↓<br>Transform Numerical Data<br>      ↓<br>Scale Features<br>      ↓<br>Create Categories</pre><p>In <strong>Post #20</strong>, we will move into one of the most important concepts in Pandas data analysis:</p><blockquote><strong><em>Pandas GroupBy Basics: Grouping Data for Analysis</em></strong></blockquote><p>We will learn how to group records and calculate meaningful statistics such as:</p><pre>count<br>sum<br>mean<br>min<br>max</pre><p>This will connect naturally with the categorical groups we created using pd.cut() and pd.qcut().</p><h3>About This Series</h3><p>This article is part of <strong>Data Analysis with Pandas</strong>, a practical series designed to help developers and beginners build strong foundations in Python data analysis.</p><p>The goal is not simply to memorize Pandas syntax.</p><p>Instead, the focus is on understanding:</p><blockquote><strong><em>How to transform raw data into meaningful analytical information.</em></strong></blockquote><p>From data cleaning and transformation to grouping, aggregation, visualization, and machine-learning preparation, each article builds on the previous concepts.</p><h3>Author</h3><p><strong>Engr. Md. Monjur Bakth Mazumder</strong></p><p><strong>GitHub:</strong><br><a href="https://github.com/monjurmbm404/Python-Data-Science-Tools">https://github.com/monjurmbm404/Python-Data-Science-Tools</a></p><h3>Final Thought</h3><p>Good data analysis is often about choosing the right level of abstraction.</p><p>Sometimes you need the exact value:</p><pre>Score = 87</pre><p>Sometimes a category is more useful:</p><pre>Score = Excellent</pre><p>And sometimes the best approach is to keep both:</p><pre>Score<br>Score_Group</pre><p>That is the real power of binning.</p><p>With pd.cut() and pd.qcut(), Pandas gives us a flexible way to transform continuous numerical data into categories that can be easier to compare, summarize, visualize, and communicate.</p><p>The key is not simply knowing how to create bins.</p><p>It is knowing <strong>why those bins should exist in the first place.</strong></p><h3>Connect &amp; Follow</h3><h4>Professional Profiles</h4><ul><li>Portfolio: <a href="https://monjurmbm404.github.io/?source=user_about----------------------d31e4eba6166------------------------">https://monjurmbm404.github.io</a></li><li>GitHub: <a href="https://github.com/monjurmbm404?source=user_about----------------------d31e4eba6166------------------------">https://github.com/monjurmbm404</a></li><li>LinkedIn: <a href="https://www.linkedin.com/in/monjurmbm404?source=user_about----------------------d31e4eba6166------------------------">https://www.linkedin.com/in/monjurmbm404</a></li><li>Medium: <a href="https://medium.com/@monjurmbm404?source=user_about----------------------d31e4eba6166------------------------">https://medium.com/@monjurmbm404</a></li></ul><h4>Books &amp; Publications</h4><ul><li>Amazon: <a href="https://www.amazon.com/author/monjurmbm404?source=user_about----------------------d31e4eba6166------------------------">https://www.amazon.com/author/monjurmbm404</a></li></ul><h4>Competitive Programming</h4><ul><li>Codeforces: <a href="https://codeforces.com/profile/monjurmbm404?source=user_about----------------------d31e4eba6166------------------------">https://codeforces.com/profile/monjurmbm404</a></li><li>CodeChef: <a href="https://www.codechef.com/users/monjurmbm404?source=user_about----------------------d31e4eba6166------------------------">https://www.codechef.com/users/monjurmbm404</a></li><li>LeetCode: <a href="https://leetcode.com/u/monjurmbm404/?source=user_about----------------------d31e4eba6166------------------------">https://leetcode.com/u/monjurmbm404/</a></li><li>VJudge: <a href="https://vjudge.net/user/monjurmbm404?source=user_about----------------------d31e4eba6166------------------------">https://vjudge.net/user/monjurmbm404</a></li></ul><h4>Social Media</h4><ul><li>YouTube: <a href="https://youtube.com/@monjurmbm404?source=user_about----------------------d31e4eba6166------------------------">https://youtube.com/@monjurmbm404</a></li><li>X: <a href="https://x.com/monjurmbm404?source=user_about----------------------d31e4eba6166------------------------">https://x.com/monjurmbm404</a></li><li>Facebook: <a href="https://www.facebook.com/monjurmbm404?source=user_about----------------------d31e4eba6166------------------------">https://www.facebook.com/monjurmbm404</a></li></ul><h4>Connect with Me</h4><ul><li>Email<strong>:</strong> <a href="/service/mailto:monjurmbm404@gmail.com">monjurmbm404@gmail.com</a></li></ul><p>— <strong>Engr. Md Monjur Bakth Mazumder<br></strong>Software Engineer | Technical Author | Competitive Programmer | AI/ML Enthusiast</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=189c35604f9a" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Data Scaling with Pandas: Normalization, Min-Max Scaling and Numerical Transformation]]></title>
            <link>https://medium.com/@monjurmbm404/data-scaling-with-pandas-normalization-min-max-scaling-and-numerical-transformation-bb60005a3c1c?source=rss-d31e4eba6166------2</link>
            <guid isPermaLink="false">https://medium.com/p/bb60005a3c1c</guid>
            <dc:creator><![CDATA[Engr. Md Monjur Bakth Mazumder]]></dc:creator>
            <pubDate>Wed, 11 Nov 2026 14:01:02 GMT</pubDate>
            <atom:updated>2026-11-11T14:01:02.146Z</atom:updated>
            <content:encoded><![CDATA[<blockquote><strong><em>Series:</em></strong><em> Data Analysis with Pandas<br></em><strong><em>Part:</em></strong><em> 18</em></blockquote><blockquote>A Practical Guide to Scaling Numerical Data, Transforming Columns and Preparing Features for Machine Learning</blockquote><p>Real-world datasets often contain numerical variables with very different ranges.</p><p>For example:</p><pre>Age          → 18 to 70<br>Salary       → 20,000 to 500,000<br>Experience   → 0 to 30<br>Score        → 0 to 100</pre><p>These variables are all numerical, but they are not on the same scale.</p><p>This difference can matter when performing:</p><ul><li>numerical analysis</li><li>comparisons</li><li>visualization</li><li>statistical modeling</li><li>machine-learning preprocessing</li></ul><p>Pandas gives us flexible tools for transforming numerical columns, while libraries such as scikit-learn provide dedicated preprocessing tools for machine learning.</p><p>In this article, we will learn:</p><ul><li>What scaling means</li><li>What normalization means</li><li>Min-Max scaling</li><li>Numerical transformations</li><li>Rounding</li><li>Creating transformed columns</li><li>How scaling connects to machine-learning preprocessing</li><li>When scaling is useful and when it is not</li></ul><h3>1. What Is Data Scaling?</h3><p>Data scaling means transforming numerical values so that they follow a different numerical range or scale.</p><p>Suppose we have:</p><pre>import pandas as pd</pre><pre>df = pd.DataFrame({<br>    &quot;Age&quot;: [20, 30, 40, 50],<br>    &quot;Salary&quot;: [30000, 50000, 80000, 120000],<br>    &quot;Score&quot;: [60, 70, 85, 95]<br>})</pre><p>Notice the ranges:</p><pre>Age<br>20 → 50</pre><pre>Salary<br>30,000 → 120,000</pre><pre>Score<br>60 → 95</pre><p>The variables have completely different magnitudes.</p><p>A scaling transformation can convert them into a more comparable range.</p><h3>2. Why Does Scaling Matter?</h3><p>Consider:</p><pre>Age = 30<br>Salary = 80,000<br>Score = 85</pre><p>The salary value is numerically much larger than the other values.</p><p>If an algorithm relies on numerical magnitude or distance, this difference can influence the result.</p><p>For example, distance-based algorithms may be sensitive to feature scale.</p><p>Common examples include:</p><ul><li>K-Nearest Neighbors</li><li>K-Means clustering</li><li>Support Vector Machines</li><li>some optimization-based machine-learning models</li></ul><p>However, scaling is <strong>not equally important for every algorithm</strong>.</p><p>Many tree-based models, such as decision trees and random forests, are generally much less sensitive to feature scale.</p><p>So the correct principle is:</p><blockquote><strong><em>Scaling is useful when the model or analysis is sensitive to the scale of numerical features.</em></strong></blockquote><h3>3. Scaling vs Transformation</h3><p>These concepts are related but not identical.</p><h3>Scaling</h3><p>Scaling changes the numerical range or magnitude.</p><p>For example:</p><pre>0 → 100</pre><p>can become:</p><pre>0 → 1</pre><h3>Transformation</h3><p>Transformation changes the representation or distribution of the values.</p><p>Examples include:</p><pre>log transformation<br>square-root transformation<br>power transformation<br>rounding</pre><p>So:</p><pre>Scaling<br>→ Change the scale</pre><pre>Transformation<br>→ Change how the values are represented</pre><p>A transformation may also affect scale, but its primary purpose can be different.</p><h3>4. What Is Normalization?</h3><p>The word <strong>normalization</strong> is used in different contexts, so it is important to define what we mean.</p><p>In many machine-learning and data-analysis contexts, normalization refers to transforming numerical values to a common range, often:</p><pre>0 → 1</pre><p>A common method is <strong>Min-Max scaling</strong>.</p><p>The formula is:</p><pre>x_scaled = (x - min(x)) / (max(x) - min(x))</pre><p>This transforms:</p><pre>minimum → 0<br>maximum → 1</pre><p>and values between them are mapped proportionally into that range.</p><h3>5. Min-Max Scaling</h3><p>Let’s use a simple example:</p><pre>df = pd.DataFrame({<br>    &quot;Score&quot;: [50, 60, 70, 80, 100]<br>})</pre><p>The minimum is:</p><pre>50</pre><p>The maximum is:</p><pre>100</pre><p>For the value 70:</p><pre>(70 - 50) / (100 - 50)</pre><p>which gives:</p><pre>20 / 50 = 0.4</pre><p>So:</p><pre>70 → 0.4</pre><h3>6. Implementing Min-Max Scaling with Pandas</h3><p>We can implement the formula directly:</p><pre>df[&quot;Score_MinMax&quot;] = (<br>    (df[&quot;Score&quot;] - df[&quot;Score&quot;].min())<br>    /<br>    (df[&quot;Score&quot;].max() - df[&quot;Score&quot;].min())<br>)</pre><p>Now:</p><pre>minimum value → 0<br>maximum value → 1</pre><p>This creates a new transformed column while preserving the original data.</p><p>That is often a good practice during analysis:</p><pre>Original column<br>      +<br>Transformed column</pre><p>instead of immediately overwriting the original values.</p><h3>7. Why Create a New Column?</h3><p>Suppose we have:</p><pre>df[&quot;Score&quot;]</pre><p>and create:</p><pre>df[&quot;Score_MinMax&quot;]</pre><p>We now have both:</p><pre>Score<br>Score_MinMax</pre><p>This makes comparison and validation easier.</p><p>For example:</p><pre>print(<br>    df[<br>        [&quot;Score&quot;, &quot;Score_MinMax&quot;]<br>    ]<br>)</pre><p>We can check whether the transformation produced the expected results.</p><p>Once the transformation is validated, you can decide whether the original column should remain.</p><h3>8. Min-Max Scaling with a Different Range</h3><p>Min-Max scaling does not have to be limited to:</p><pre>0 → 1</pre><p>A generalized formula can map values to another range:</p><pre>x_scaled =<br>((x - min) / (max - min))<br>× (new_max - new_min)<br>+ new_min</pre><p>For example, suppose we want:</p><pre>0 → 100</pre><p>We can write:</p><pre>df[&quot;Score_100&quot;] = (<br>    (<br>        (df[&quot;Score&quot;] - df[&quot;Score&quot;].min())<br>        /<br>        (df[&quot;Score&quot;].max() - df[&quot;Score&quot;].min())<br>    ) * 100<br>)</pre><p>Now the minimum becomes approximately:</p><pre>0</pre><p>and the maximum:</p><pre>100</pre><p>This can be useful when a normalized percentage-like scale is easier to interpret.</p><h3>9. Min-Max Scaling with a Reusable Function</h3><p>If you need to apply Min-Max scaling repeatedly, defining a function can improve readability.</p><pre>def min_max_scale(series):<br>    return (<br>        (series - series.min())<br>        /<br>        (series.max() - series.min())<br>    )</pre><p>Then:</p><pre>df[&quot;Score_Scaled&quot;] = min_max_scale(<br>    df[&quot;Score&quot;]<br>)</pre><p>This is useful when working with several numerical columns.</p><h3>10. Scaling Multiple Columns</h3><p>Suppose:</p><pre>df = pd.DataFrame({<br>    &quot;Age&quot;: [20, 30, 40, 50],<br>    &quot;Salary&quot;: [30000, 50000, 80000, 120000],<br>    &quot;Score&quot;: [60, 70, 85, 95]<br>})</pre><p>We can scale selected columns:</p><pre>columns = [<br>    &quot;Age&quot;,<br>    &quot;Salary&quot;,<br>    &quot;Score&quot;<br>]</pre><pre>for column in columns:<br>    df[f&quot;{column}_Scaled&quot;] = (<br>        (df[column] - df[column].min())<br>        /<br>        (df[column].max() - df[column].min())<br>    )</pre><p>This creates:</p><pre>Age_Scaled<br>Salary_Scaled<br>Score_Scaled</pre><p>The original columns remain available for interpretation.</p><h3>11. Important Edge Case: Constant Columns</h3><p>Consider:</p><pre>df = pd.DataFrame({<br>    &quot;Age&quot;: [30, 30, 30, 30]<br>})</pre><p>Here:</p><pre>min = max = 30</pre><p>The Min-Max denominator becomes:</p><pre>30 - 30 = 0</pre><p>So the standard formula is undefined.</p><p>Before scaling, it is useful to check whether a column has any variation:</p><pre>df[&quot;Age&quot;].nunique()</pre><p>If the result is:</p><pre>1</pre><p>then every value is identical.</p><p>Such a feature usually provides no variation for many analytical or modeling tasks.</p><h3>12. Normalization Does Not Mean Making Data “Normal”</h3><p>This is an important terminology point.</p><p><strong>Normalization</strong> in the context of Min-Max scaling does not mean:</p><blockquote><em>Make the data follow a normal/Gaussian distribution.</em></blockquote><p>Min-Max scaling changes the range.</p><p>It does not automatically make the distribution normal.</p><p>For example:</p><pre>Original distribution<br>      ↓<br>Min-Max scaling<br>      ↓<br>Range becomes 0 → 1</pre><p>The overall shape of the distribution can remain similar.</p><h3>13. Standardization Is Different</h3><p>Another common preprocessing technique is <strong>standardization</strong>.</p><p>Standardization transforms values using:</p><pre>z = (x - mean) / standard_deviation</pre><p>The resulting values are centered around:</p><pre>mean ≈ 0</pre><p>with:</p><pre>standard deviation ≈ 1</pre><p>This is different from Min-Max scaling.</p><p>Conceptually:</p><pre>Min-Max Scaling<br>→ fixed range, commonly 0 to 1</pre><pre>Standardization<br>→ centered around 0 with scale related to standard deviation</pre><p>This distinction becomes important in machine-learning preprocessing.</p><h3>14. Numerical Transformation</h3><p>Scaling is only one type of numerical transformation.</p><p>We can also transform numerical columns using mathematical operations.</p><p>For example:</p><pre>df[&quot;Salary_K&quot;] = (<br>    df[&quot;Salary&quot;] / 1000<br>)</pre><p>This transforms:</p><pre>85000</pre><p>into:</p><pre>85</pre><p>The underlying information remains the same, but the representation becomes easier to read.</p><h3>15. Percentage Transformation</h3><p>Suppose:</p><pre>df = pd.DataFrame({<br>    &quot;Passed&quot;: [80, 70, 95],<br>    &quot;Total&quot;: [100, 100, 100]<br>})</pre><p>We can create a percentage:</p><pre>df[&quot;Pass_Percentage&quot;] = (<br>    df[&quot;Passed&quot;] /<br>    df[&quot;Total&quot;]<br>) * 100</pre><p>This converts a ratio into a percentage representation.</p><h3>16. Log Transformation</h3><p>Some numerical variables have highly skewed distributions.</p><p>For example:</p><pre>Income<br>Revenue<br>Transaction Amount<br>Population</pre><p>can sometimes contain a small number of very large values.</p><p>A logarithmic transformation can compress large values.</p><p>Using NumPy:</p><pre>import numpy as np</pre><pre>df[&quot;Log_Revenue&quot;] = np.log1p(<br>    df[&quot;Revenue&quot;]<br>)</pre><p>np.log1p(x) calculates:</p><pre>log(1 + x)</pre><p>and is often convenient when zero values may be present.</p><p>However, log transformation changes the interpretation of the variable.</p><p>It should therefore be used when there is a clear analytical reason.</p><h3>17. Square-Root Transformation</h3><p>Another possible transformation is:</p><pre>df[&quot;Sqrt_Value&quot;] = np.sqrt(<br>    df[&quot;Value&quot;]<br>)</pre><p>This can reduce the influence of larger values in some datasets.</p><p>But it requires non-negative values.</p><p>For example:</p><pre>0<br>1<br>4<br>9<br>16</pre><p>become:</p><pre>0<br>1<br>2<br>3<br>4</pre><p>Again, the transformation should be chosen based on the data and analytical objective.</p><h3>18. Absolute Value Transformation</h3><p>Suppose:</p><pre>df = pd.DataFrame({<br>    &quot;Change&quot;: [-10, 5, -7, 12]<br>})</pre><p>We can create:</p><pre>df[&quot;Absolute_Change&quot;] = (<br>    df[&quot;Change&quot;].abs()<br>)</pre><p>Now:</p><pre>-10 → 10<br>5   → 5<br>-7  → 7<br>12  → 12</pre><p>This is useful when the magnitude matters more than the direction.</p><h3>19. Rounding Numerical Values</h3><p>Another common transformation is rounding.</p><p>Suppose:</p><pre>df = pd.DataFrame({<br>    &quot;Average&quot;: [<br>        87.45678,<br>        72.89123,<br>        91.23456<br>    ]<br>})</pre><p>We can round to two decimal places:</p><pre>df[&quot;Average_Rounded&quot;] = (<br>    df[&quot;Average&quot;].round(2)<br>)</pre><p>Now the values become easier to display:</p><pre>87.46<br>72.89<br>91.23</pre><h3>20. Rounding to Zero Decimal Places</h3><p>You can also round to the nearest integer:</p><pre>df[&quot;Rounded&quot;] = (<br>    df[&quot;Average&quot;].round(0)<br>)</pre><p>Or:</p><pre>df[&quot;Rounded&quot;] = (<br>    df[&quot;Average&quot;].round().astype(int)<br>)</pre><p>Be careful with the second approach when missing values are present, because standard integer dtype cannot represent NaN in the same way as nullable Pandas integer types.</p><h3>21. Rounding and Data Accuracy</h3><p>Rounding is usually a <strong>presentation transformation</strong>, not something you should automatically do to the underlying analytical data.</p><p>For example:</p><pre>87.45678</pre><p>might be displayed as:</p><pre>87.46</pre><p>but the original precision may still be valuable for calculations.</p><p>A good practice is often:</p><pre>Raw value<br>   ↓<br>Keep precision<br>   ↓<br>Round for presentation</pre><p>rather than rounding too early and permanently losing information.</p><h3>22. Decimal Transformation</h3><p>Pandas can also round different columns to different decimal places.</p><p>For example:</p><pre>df[&quot;Score&quot;] = df[&quot;Score&quot;].round(2)</pre><p>Or:</p><pre>df = df.round({<br>    &quot;Score&quot;: 2,<br>    &quot;Revenue&quot;: 0,<br>    &quot;Profit_Margin&quot;: 3<br>})</pre><p>This is useful when preparing an analytical output or report.</p><h3>23. Scaling and Missing Values</h3><p>Suppose:</p><pre>df = pd.DataFrame({<br>    &quot;Score&quot;: [50, 60, None, 80, 100]<br>})</pre><p>Before scaling, inspect missing values:</p><pre>df[&quot;Score&quot;].isna().sum()</pre><p>Then decide how to handle them.</p><p>Possible strategies include:</p><pre>Drop<br>Fill with a meaningful value<br>Use an appropriate imputation strategy<br>Leave missing and use a preprocessing method that handles it</pre><p>Do not automatically assume that missing values should become zero.</p><p>The correct strategy depends on what the missing value means.</p><h3>24. Scaling and Outliers</h3><p>Min-Max scaling is sensitive to extreme values.</p><p>Suppose:</p><pre>10<br>20<br>30<br>40<br>10000</pre><p>The value 10000 becomes the maximum.</p><p>As a result, the smaller values may be compressed into a very narrow part of the 0 → 1 range.</p><p>For example:</p><pre>10 → near 0<br>20 → near 0<br>30 → near 0<br>40 → near 0<br>10000 → 1</pre><p>Therefore, before using Min-Max scaling, it is often useful to inspect the distribution and potential outliers.</p><h3>25. Why Outliers Matter</h3><p>Suppose we have:</p><pre>df = pd.DataFrame({<br>    &quot;Income&quot;: [<br>        30000,<br>        35000,<br>        40000,<br>        45000,<br>        1000000<br>    ]<br>})</pre><p>Min-Max scaling uses:</p><pre>min = 30000<br>max = 1000000</pre><p>The very large value strongly influences the range.</p><p>This does not mean Min-Max scaling is wrong.</p><p>It means you should understand the data before choosing the transformation.</p><p>Depending on the problem, you might consider:</p><ul><li>investigating the outlier</li><li>correcting data-quality issues</li><li>transforming the distribution</li><li>using a different scaling strategy</li></ul><h3>26. Creating a Transformed Dataset</h3><p>Instead of modifying the original DataFrame, you can create a separate transformed version.</p><p>For example:</p><pre>scaled_df = df.copy()</pre><p>Then:</p><pre>scaled_df[&quot;Score&quot;] = (<br>    (scaled_df[&quot;Score&quot;] - scaled_df[&quot;Score&quot;].min())<br>    /<br>    (<br>        scaled_df[&quot;Score&quot;].max()<br>        - scaled_df[&quot;Score&quot;].min()<br>    )<br>)</pre><p>This preserves the original dataset.</p><p>This approach can be useful when you need both:</p><pre>Original data<br>+<br>Transformed data</pre><p>for comparison.</p><h3>27. Naming Transformed Columns Clearly</h3><p>Good column names make transformations easier to understand.</p><p>For example:</p><pre>df[&quot;Salary_K&quot;]<br>df[&quot;Score_Scaled&quot;]<br>df[&quot;Revenue_Log&quot;]<br>df[&quot;Profit_Margin&quot;]<br>df[&quot;Score_Rounded&quot;]</pre><p>These names communicate what happened to the original variable.</p><p>Avoid vague names such as:</p><pre>df[&quot;New_Score&quot;]</pre><p>when the actual transformation is important.</p><p>A useful feature name should make the transformation obvious.</p><h3>28. Min-Max Scaling Function for Multiple Columns</h3><p>We can create a reusable function:</p><pre>def min_max_scale(series):<br>    min_value = series.min()<br>    max_value = series.max()</pre><pre>    if min_value == max_value:<br>        return pd.Series(<br>            0.0,<br>            index=series.index<br>        )</pre><pre>    return (<br>        (series - min_value)<br>        /<br>        (max_value - min_value)<br>    )</pre><p>Then:</p><pre>columns = [<br>    &quot;Age&quot;,<br>    &quot;Salary&quot;,<br>    &quot;Score&quot;<br>]</pre><pre>for column in columns:<br>    df[f&quot;{column}_Scaled&quot;] = (<br>        min_max_scale(df[column])<br>    )</pre><p>This makes the transformation reusable.</p><p>The handling of a constant column is an explicit design choice; in a real preprocessing pipeline, you may instead choose to remove constant features or handle them according to the downstream tool’s behavior.</p><h3>29. ML Preprocessing Connection</h3><p>Now we reach an important connection.</p><p>Data scaling is frequently used during <strong>machine-learning preprocessing</strong>.</p><p>Suppose a dataset contains:</p><pre>Age<br>Salary<br>Experience<br>Score</pre><p>Before training certain machine-learning models, you may transform these numerical features so that their scales are more comparable.</p><p>For example:</p><pre>Raw Features<br>     ↓<br>Data Cleaning<br>     ↓<br>Feature Engineering<br>     ↓<br>Scaling / Transformation<br>     ↓<br>Train ML Model</pre><p>This is part of the broader preprocessing workflow.</p><h3>30. Pandas vs Scikit-Learn for Scaling</h3><p>Pandas can absolutely perform a Min-Max calculation:</p><pre>df[&quot;Score_Scaled&quot;] = (<br>    (df[&quot;Score&quot;] - df[&quot;Score&quot;].min())<br>    /<br>    (df[&quot;Score&quot;].max() - df[&quot;Score&quot;].min())<br>)</pre><p>But when preparing data for an actual machine-learning model, scikit-learn provides dedicated preprocessing tools.</p><p>For example:</p><pre>from sklearn.preprocessing import MinMaxScaler</pre><p>Then:</p><pre>scaler = MinMaxScaler()</pre><pre>X_scaled = scaler.fit_transform(X)</pre><p>The important distinction is:</p><pre>Pandas<br>→ Data manipulation and transformation</pre><pre>Scikit-learn<br>→ ML-oriented preprocessing and modeling workflow</pre><p>They often work together.</p><h3>31. Why fit() and transform() Matter in ML</h3><p>Machine-learning preprocessing introduces an important concept:</p><pre>fit<br>transform</pre><p>Suppose we have:</p><pre>Training Data<br>Testing Data</pre><p>A scaler should generally be fitted on the training data:</p><pre>scaler.fit(X_train)</pre><p>Then applied to both training and test data:</p><pre>X_train_scaled = scaler.transform(X_train)<br>X_test_scaled = scaler.transform(X_test)</pre><p>This helps prevent information from the test set from influencing the scaling parameters.</p><p>This is called avoiding <strong>data leakage</strong>.</p><h3>32. Why Not Calculate Min and Max on the Entire Dataset?</h3><p>Suppose you calculate:</p><pre>df[&quot;Score&quot;].min()<br>df[&quot;Score&quot;].max()</pre><p>using both training and test data before splitting.</p><p>Then the test data has influenced the scaling parameters.</p><p>In a machine-learning workflow, this can introduce information leakage.</p><p>A safer conceptual workflow is:</p><pre>Raw Dataset<br>     ↓<br>Train / Test Split<br>     ↓<br>Fit scaler on Training Data<br>     ↓<br>Transform Training Data<br>     ↓<br>Transform Test Data using same scaler</pre><p>This distinction is extremely important when scaling is used for predictive modeling.</p><h3>33. Scaling Is Not Always Required</h3><p>One common misconception is:</p><blockquote><em>Every machine-learning dataset must be scaled.</em></blockquote><p>That is not true.</p><p>Scaling is especially important for algorithms that depend on:</p><ul><li>distances</li><li>feature magnitudes</li><li>gradient-based optimization</li><li>regularization</li></ul><p>Examples can include:</p><pre>KNN<br>K-Means<br>SVM<br>Logistic Regression<br>Linear Regression<br>Neural Networks</pre><p>depending on the specific implementation and preprocessing setup.</p><p>Tree-based models are generally less sensitive to feature scaling:</p><pre>Decision Tree<br>Random Forest<br>Gradient-Boosted Trees</pre><p>The exact preprocessing pipeline should depend on the algorithm and dataset.</p><h3>34. Practical Example: Student Dataset</h3><p>Let’s create a realistic dataset:</p><pre>students = pd.DataFrame({<br>    &quot;Name&quot;: [<br>        &quot;Alice&quot;,<br>        &quot;Bob&quot;,<br>        &quot;Charlie&quot;,<br>        &quot;David&quot;,<br>        &quot;Emma&quot;<br>    ],<br>    &quot;Age&quot;: [<br>        18,<br>        19,<br>        20,<br>        18,<br>        21<br>    ],<br>    &quot;Study_Hours&quot;: [<br>        3,<br>        5,<br>        8,<br>        2,<br>        6<br>    ],<br>    &quot;Score&quot;: [<br>        65,<br>        78,<br>        92,<br>        55,<br>        88<br>    ]<br>})</pre><p>Suppose we want to scale:</p><pre>Age<br>Study_Hours<br>Score</pre><p>We can use:</p><pre>numeric_columns = [<br>    &quot;Age&quot;,<br>    &quot;Study_Hours&quot;,<br>    &quot;Score&quot;<br>]</pre><pre>for column in numeric_columns:<br>    students[f&quot;{column}_Scaled&quot;] = (<br>        (<br>            students[column]<br>            - students[column].min()<br>        )<br>        /<br>        (<br>            students[column].max()<br>            - students[column].min()<br>        )<br>    )</pre><p>Now we have both original and scaled versions.</p><h3>35. Practical Example: Salary Transformation</h3><p>Consider:</p><pre>employees = pd.DataFrame({<br>    &quot;Name&quot;: [<br>        &quot;Alice&quot;,<br>        &quot;Bob&quot;,<br>        &quot;Charlie&quot;,<br>        &quot;David&quot;<br>    ],<br>    &quot;Salary&quot;: [<br>        45000,<br>        60000,<br>        85000,<br>        120000<br>    ]<br>})</pre><p>Create salary in thousands:</p><pre>employees[&quot;Salary_K&quot;] = (<br>    employees[&quot;Salary&quot;] / 1000<br>)</pre><p>Create Min-Max scaled salary:</p><pre>employees[&quot;Salary_Scaled&quot;] = (<br>    (<br>        employees[&quot;Salary&quot;]<br>        - employees[&quot;Salary&quot;].min()<br>    )<br>    /<br>    (<br>        employees[&quot;Salary&quot;].max()<br>        - employees[&quot;Salary&quot;].min()<br>    )<br>)</pre><p>Round the scaled value:</p><pre>employees[&quot;Salary_Scaled&quot;] = (<br>    employees[&quot;Salary_Scaled&quot;].round(3)<br>)</pre><p>Now we have:</p><pre>Salary<br>Salary_K<br>Salary_Scaled</pre><p>Each representation serves a different purpose.</p><h3>36. Transformation Should Match the Goal</h3><p>Different transformations answer different questions.</p><h3>Want comparable range?</h3><p>Use:</p><pre>Min-Max scaling</pre><h3>Want a centered standardized feature?</h3><p>Consider:</p><pre>Standardization</pre><h3>Want to reduce the influence of large positive values?</h3><p>Consider:</p><pre>Log transformation</pre><p>when appropriate.</p><h3>Want an easier-to-read representation?</h3><p>Use:</p><pre>Rounding</pre><h3>Want a ratio or percentage?</h3><p>Use:</p><pre>Arithmetic transformation</pre><p>The transformation should always follow the analytical objective.</p><h3>37. Common Mistakes</h3><h3>Mistake 1: Confusing Scaling with Normal Distribution</h3><p>Min-Max scaling does not make data normally distributed.</p><p>It changes the range.</p><h3>Mistake 2: Scaling Without Checking Outliers</h3><p>Extreme values can strongly influence Min-Max scaling.</p><p>Inspect the data first.</p><h3>Mistake 3: Scaling Before Handling Data Problems</h3><p>If a column contains:</p><pre>missing values<br>invalid values<br>incorrect data types<br>extreme data-quality errors</pre><p>fix or understand those issues before applying transformations.</p><h3>Mistake 4: Rounding Too Early</h3><p>If you round raw numerical data before calculations, you may lose useful precision.</p><p>Prefer to keep the original precision and round the final presentation when appropriate.</p><h3>Mistake 5: Fitting an ML Scaler on Test Data</h3><p>Do not calculate preprocessing parameters from the test set.</p><p>Fit on training data and transform the test data using the same fitted transformation.</p><h3>Mistake 6: Scaling Every Feature Automatically</h3><p>Not every feature needs scaling.</p><p>First understand the algorithm and analytical objective.</p><h3>38. A Practical Scaling Workflow</h3><p>A good workflow is:</p><pre>Load Data<br>    ↓<br>Inspect Data Types<br>    ↓<br>Handle Missing Values<br>    ↓<br>Check Outliers<br>    ↓<br>Select Numerical Features<br>    ↓<br>Choose Transformation<br>    ↓<br>Create / Fit Transformation<br>    ↓<br>Transform Data<br>    ↓<br>Validate Result<br>    ↓<br>Use for Analysis or ML</pre><p>For machine learning:</p><pre>Split Data<br>    ↓<br>Fit Preprocessor on Training Data<br>    ↓<br>Transform Training Data<br>    ↓<br>Transform Test Data<br>    ↓<br>Train Model</pre><p>This separation is critical for avoiding leakage.</p><h3>39. Quick Reference</h3><h3>Min-Max scaling</h3><pre>df[&quot;Score_Scaled&quot;] = (<br>    (df[&quot;Score&quot;] - df[&quot;Score&quot;].min())<br>    /<br>    (df[&quot;Score&quot;].max() - df[&quot;Score&quot;].min())<br>)</pre><h3>Convert to percentage</h3><pre>df[&quot;Percentage&quot;] = (<br>    df[&quot;Ratio&quot;] * 100<br>)</pre><h3>Scale to 0–100</h3><pre>df[&quot;Score_100&quot;] = (<br>    (<br>        (df[&quot;Score&quot;] - df[&quot;Score&quot;].min())<br>        /<br>        (df[&quot;Score&quot;].max() - df[&quot;Score&quot;].min())<br>    ) * 100<br>)</pre><h3>Round values</h3><pre>df[&quot;Score_Rounded&quot;] = (<br>    df[&quot;Score&quot;].round(2)<br>)</pre><h3>Log transformation</h3><pre>df[&quot;Log_Value&quot;] = np.log1p(<br>    df[&quot;Value&quot;]<br>)</pre><h3>Square-root transformation</h3><pre>df[&quot;Sqrt_Value&quot;] = np.sqrt(<br>    df[&quot;Value&quot;]<br>)</pre><h3>Absolute transformation</h3><pre>df[&quot;Absolute_Value&quot;] = (<br>    df[&quot;Value&quot;].abs()<br>)</pre><h3>Scikit-learn Min-Max scaling</h3><pre>from sklearn.preprocessing import MinMaxScaler</pre><pre>scaler = MinMaxScaler()</pre><pre>X_train_scaled = scaler.fit_transform(<br>    X_train<br>)</pre><pre>X_test_scaled = scaler.transform(<br>    X_test<br>)</pre><h3>40. Final Takeaways</h3><p>Scaling and transformation are important parts of practical data preparation.</p><p>With Pandas, we can easily create transformed numerical columns using:</p><pre>+<br>-<br>*<br>/</pre><p>as well as functions such as:</p><pre>round()<br>abs()<br>np.log1p()<br>np.sqrt()</pre><p>Min-Max scaling can transform numerical values into a common range, commonly:</p><pre>0 → 1</pre><p>using:</p><pre>(x - min) / (max - min)</pre><p>But scaling is only one form of transformation.</p><p>The key ideas to remember are:</p><ul><li>Scaling changes the numerical range or magnitude.</li><li>Min-Max scaling commonly maps values to 0–1.</li><li>Normalization in this context does not mean making data normally distributed.</li><li>Standardization is different from Min-Max scaling.</li><li>Numerical transformations can create more useful representations.</li><li>Rounding is usually better treated as a presentation step unless the analytical requirement says otherwise.</li><li>Outliers can strongly affect Min-Max scaling.</li><li>Missing values and invalid data should be considered before transformation.</li><li>Pandas is excellent for data manipulation and exploratory transformations.</li><li>Scikit-learn provides dedicated preprocessing tools for ML workflows.</li><li>In ML, preprocessing parameters should generally be learned from training data only.</li><li>Not every machine-learning algorithm requires feature scaling.</li></ul><p>The most important principle is:</p><blockquote><strong><em>Choose the transformation based on the data, the analytical objective, and the downstream model — not simply because scaling is available.</em></strong></blockquote><h3>41. Where We Go Next</h3><p>Our Pandas workflow is becoming more powerful:</p><pre>Load<br>  ↓<br>Inspect<br>  ↓<br>Select<br>  ↓<br>Filter<br>  ↓<br>Clean<br>  ↓<br>Transform<br>  ↓<br>Engineer Features<br>  ↓<br>Scale<br>  ↓<br>Analyze</pre><p>In <strong>Post #19</strong>, we will explore another important technique for numerical data:</p><blockquote><strong><em>Pandas Binning: </em></strong><strong><em>cut() and </em></strong><strong><em>qcut() for Categorical Data Analysis</em></strong></blockquote><p>We will learn how to convert continuous numerical values into meaningful groups and understand the difference between fixed-range bins and quantile-based bins.</p><h3>About This Series</h3><p>This article is part of <strong>Data Analysis with Pandas</strong>, a practical series designed to help developers and beginners understand Pandas through real-world data-analysis workflows.</p><p>The goal is not simply to memorize syntax.</p><p>It is to understand:</p><blockquote><strong><em>What transformation should I use, why should I use it, and what effect will it have on my data?</em></strong></blockquote><p>That mindset is essential when moving from basic Pandas operations toward exploratory data analysis, visualization, and machine-learning preparation.</p><h3>Author</h3><p><strong>Engr. Md. Monjur Bakth Mazumder</strong></p><p><strong>GitHub:</strong><br><a href="https://github.com/monjurmbm404/Python-Data-Science-Tools">https://github.com/monjurmbm404/Python-Data-Science-Tools</a></p><h3>Final Thought</h3><p>Data transformation is not about changing numbers just for the sake of changing them.</p><p>Every transformation should have a reason.</p><p>Sometimes we need:</p><pre>0 → 1</pre><p>for comparable feature scales.</p><p>Sometimes we need:</p><pre>Raw value → Percentage</pre><p>for easier interpretation.</p><p>Sometimes we need:</p><pre>Large skewed value → Log-transformed value</pre><p>to make a distribution easier to analyze.</p><p>And sometimes we simply need:</p><pre>87.45678 → 87.46</pre><p>for clean presentation.</p><p>The important skill is knowing <strong>which transformation fits the problem</strong>.</p><p>That is what turns numerical preprocessing from a collection of formulas into a meaningful data-analysis practice.</p><h3>Connect &amp; Follow</h3><h4>Professional Profiles</h4><ul><li>Portfolio: <a href="https://monjurmbm404.github.io/?source=user_about----------------------d31e4eba6166------------------------">https://monjurmbm404.github.io</a></li><li>GitHub: <a href="https://github.com/monjurmbm404?source=user_about----------------------d31e4eba6166------------------------">https://github.com/monjurmbm404</a></li><li>LinkedIn: <a href="https://www.linkedin.com/in/monjurmbm404?source=user_about----------------------d31e4eba6166------------------------">https://www.linkedin.com/in/monjurmbm404</a></li><li>Medium: <a href="https://medium.com/@monjurmbm404?source=user_about----------------------d31e4eba6166------------------------">https://medium.com/@monjurmbm404</a></li></ul><h4>Books &amp; Publications</h4><ul><li>Amazon: <a href="https://www.amazon.com/author/monjurmbm404?source=user_about----------------------d31e4eba6166------------------------">https://www.amazon.com/author/monjurmbm404</a></li></ul><h4>Competitive Programming</h4><ul><li>Codeforces: <a href="https://codeforces.com/profile/monjurmbm404?source=user_about----------------------d31e4eba6166------------------------">https://codeforces.com/profile/monjurmbm404</a></li><li>CodeChef: <a href="https://www.codechef.com/users/monjurmbm404?source=user_about----------------------d31e4eba6166------------------------">https://www.codechef.com/users/monjurmbm404</a></li><li>LeetCode: <a href="https://leetcode.com/u/monjurmbm404/?source=user_about----------------------d31e4eba6166------------------------">https://leetcode.com/u/monjurmbm404/</a></li><li>VJudge: <a href="https://vjudge.net/user/monjurmbm404?source=user_about----------------------d31e4eba6166------------------------">https://vjudge.net/user/monjurmbm404</a></li></ul><h4>Social Media</h4><ul><li>YouTube: <a href="https://youtube.com/@monjurmbm404?source=user_about----------------------d31e4eba6166------------------------">https://youtube.com/@monjurmbm404</a></li><li>X: <a href="https://x.com/monjurmbm404?source=user_about----------------------d31e4eba6166------------------------">https://x.com/monjurmbm404</a></li><li>Facebook: <a href="https://www.facebook.com/monjurmbm404?source=user_about----------------------d31e4eba6166------------------------">https://www.facebook.com/monjurmbm404</a></li></ul><h4>Connect with Me</h4><ul><li>Email<strong>:</strong> <a href="/service/mailto:monjurmbm404@gmail.com">monjurmbm404@gmail.com</a></li></ul><p>— <strong>Engr. Md Monjur Bakth Mazumder<br></strong>Software Engineer | Technical Author | Competitive Programmer | AI/ML Enthusiast</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=bb60005a3c1c" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Feature Engineering with Pandas: Creating and Transforming DataFrame Columns]]></title>
            <link>https://medium.com/@monjurmbm404/feature-engineering-with-pandas-creating-and-transforming-dataframe-columns-18dab33a85bb?source=rss-d31e4eba6166------2</link>
            <guid isPermaLink="false">https://medium.com/p/18dab33a85bb</guid>
            <dc:creator><![CDATA[Engr. Md Monjur Bakth Mazumder]]></dc:creator>
            <pubDate>Tue, 10 Nov 2026 14:01:02 GMT</pubDate>
            <atom:updated>2026-11-10T14:01:02.282Z</atom:updated>
            <content:encoded><![CDATA[<blockquote><strong><em>Series:</em></strong><em> Data Analysis with Pandas<br></em><strong><em>Part:</em></strong><em> 17</em></blockquote><blockquote>A Practical Guide to Creating New Columns, Conditional Features and Derived Variables with Python Pandas</blockquote><p>Real-world datasets are rarely ready for analysis exactly as they arrive.</p><p>A dataset may contain:</p><pre>Age<br>Salary<br>Quantity<br>Price<br>Score<br>Experience</pre><p>But the questions we want to answer are often more meaningful:</p><ul><li>What is the total sales amount?</li><li>Which customers are high-value?</li><li>Which students are performing well?</li><li>How should employees be categorized?</li><li>What is the profit margin?</li><li>How many years of experience does someone have?</li><li>Which customers should receive special attention?</li></ul><p>To answer these questions, we often need to create <strong>new variables from existing data</strong>.</p><p>This process is commonly called:</p><blockquote><strong><em>Feature Engineering</em></strong></blockquote><p>In Pandas, feature engineering often means creating new DataFrame columns by transforming, combining, or categorizing existing columns.</p><p>In this article, we will learn how to create:</p><ul><li>New columns</li><li>Arithmetic transformations</li><li>Conditional features</li><li>Combined features</li><li>Derived variables</li><li>Practical real-world features</li></ul><p>We will also discuss an important principle:</p><blockquote><strong><em>A good feature should add useful information — not simply add more columns.</em></strong></blockquote><h3>1. What Is Feature Engineering?</h3><p>Feature engineering is the process of creating or transforming variables so that the data becomes more useful for analysis, reporting, or downstream modeling.</p><p>Suppose we have:</p><pre>import pandas as pd</pre><pre>df = pd.DataFrame({<br>    &quot;Product&quot;: [&quot;Laptop&quot;, &quot;Phone&quot;, &quot;Tablet&quot;],<br>    &quot;Price&quot;: [1000, 600, 400],<br>    &quot;Quantity&quot;: [2, 5, 3]<br>})</pre><p>The dataset contains:</p><pre>Price<br>Quantity</pre><p>But we may want:</p><pre>Total_Sales</pre><p>We can create it:</p><pre>df[&quot;Total_Sales&quot;] = (<br>    df[&quot;Price&quot;] * df[&quot;Quantity&quot;]<br>)</pre><p>Now we have a new variable derived from existing information.</p><p>This is a simple example of feature engineering.</p><h3>2. Why Feature Engineering Matters</h3><p>Raw columns often do not directly answer the business or analytical question.</p><p>For example:</p><pre>Price = 1000<br>Quantity = 5</pre><p>Individually, these values are useful.</p><p>But:</p><pre>Price × Quantity = Total Sales</pre><p>may be much more useful for analysis.</p><p>Similarly:</p><pre>Date of Birth</pre><p>can be transformed into:</p><pre>Age</pre><p>And:</p><pre>Math Score<br>Science Score<br>English Score</pre><p>can be transformed into:</p><pre>Average Score</pre><p>Feature engineering helps transform raw variables into information that better represents the problem we are trying to understand.</p><h3>3. Creating a New Column</h3><p>The simplest form of feature engineering is assigning a new column.</p><p>For example:</p><pre>df[&quot;Discount&quot;] = 0.10</pre><p>This creates a new column named:</p><pre>Discount</pre><p>with the same scalar value assigned to every row.</p><p>You can also create a column from an existing column:</p><pre>df[&quot;Price_With_Tax&quot;] = (<br>    df[&quot;Price&quot;] * 1.15<br>)</pre><p>Now every product has a calculated price including 15% tax.</p><h3>4. Arithmetic Transformation</h3><p>One of the most common feature-engineering techniques is arithmetic transformation.</p><p>Suppose:</p><pre>df = pd.DataFrame({<br>    &quot;Product&quot;: [&quot;Laptop&quot;, &quot;Phone&quot;, &quot;Tablet&quot;],<br>    &quot;Price&quot;: [1000, 600, 400],<br>    &quot;Quantity&quot;: [2, 5, 3]<br>})</pre><p>We can calculate total sales:</p><pre>df[&quot;Total_Sales&quot;] = (<br>    df[&quot;Price&quot;] * df[&quot;Quantity&quot;]<br>)</pre><p>Conceptually:</p><pre>Total Sales = Price × Quantity</pre><p>This is a vectorized Pandas operation.</p><p>No loop is required.</p><p>No apply() is required.</p><h3>5. Addition Between Columns</h3><p>Suppose we have:</p><pre>df = pd.DataFrame({<br>    &quot;Product&quot;: [&quot;A&quot;, &quot;B&quot;, &quot;C&quot;],<br>    &quot;Online_Sales&quot;: [1000, 1500, 1200],<br>    &quot;Store_Sales&quot;: [800, 900, 1100]<br>})</pre><p>We can calculate total sales:</p><pre>df[&quot;Total_Sales&quot;] = (<br>    df[&quot;Online_Sales&quot;] +<br>    df[&quot;Store_Sales&quot;]<br>)</pre><p>This creates a new derived variable:</p><pre>Total Sales</pre><p>from two existing variables.</p><h3>6. Subtracting Columns</h3><p>Suppose we have:</p><pre>df = pd.DataFrame({<br>    &quot;Revenue&quot;: [10000, 15000, 12000],<br>    &quot;Cost&quot;: [6000, 9000, 7000]<br>})</pre><p>We can calculate profit:</p><pre>df[&quot;Profit&quot;] = (<br>    df[&quot;Revenue&quot;] -<br>    df[&quot;Cost&quot;]<br>)</pre><p>The formula is:</p><pre>Profit = Revenue - Cost</pre><p>This is one of the most common derived variables in business analysis.</p><h3>7. Multiplication Between Columns</h3><p>Suppose:</p><pre>df = pd.DataFrame({<br>    &quot;Units&quot;: [10, 20, 15],<br>    &quot;Unit_Price&quot;: [100, 80, 120]<br>})</pre><p>Create revenue:</p><pre>df[&quot;Revenue&quot;] = (<br>    df[&quot;Units&quot;] *<br>    df[&quot;Unit_Price&quot;]<br>)</pre><p>This is a good example of why understanding vectorized operations is important.</p><p>Pandas automatically performs the calculation element by element.</p><h3>8. Division and Ratios</h3><p>Feature engineering is also useful for creating ratios.</p><p>Suppose:</p><pre>df = pd.DataFrame({<br>    &quot;Profit&quot;: [2000, 3000, 2500],<br>    &quot;Revenue&quot;: [10000, 15000, 12500]<br>})</pre><p>We can calculate profit margin:</p><pre>df[&quot;Profit_Margin&quot;] = (<br>    df[&quot;Profit&quot;] /<br>    df[&quot;Revenue&quot;]<br>)</pre><p>If we want percentage:</p><pre>df[&quot;Profit_Margin_Percent&quot;] = (<br>    df[&quot;Profit&quot;] /<br>    df[&quot;Revenue&quot;]<br>) * 100</pre><p>Now we have a more interpretable business metric.</p><h3>9. Conditional Features</h3><p>Not every feature is created through arithmetic.</p><p>Sometimes we want to categorize records based on a condition.</p><p>For example:</p><pre>df = pd.DataFrame({<br>    &quot;Name&quot;: [&quot;Alice&quot;, &quot;Bob&quot;, &quot;Charlie&quot;, &quot;David&quot;],<br>    &quot;Score&quot;: [92, 76, 58, 35]<br>})</pre><p>We can create:</p><pre>Pass / Fail</pre><p>using:</p><pre>df[&quot;Result&quot;] = (<br>    df[&quot;Score&quot;] &gt;= 40<br>)</pre><p>This produces Boolean values:</p><pre>True<br>False</pre><p>Boolean features can themselves be useful.</p><h3>10. Conditional Categories with np.where()</h3><p>For a text category, we can use NumPy’s where():</p><pre>import numpy as np</pre><pre>df[&quot;Result&quot;] = np.where(<br>    df[&quot;Score&quot;] &gt;= 40,<br>    &quot;Pass&quot;,<br>    &quot;Fail&quot;<br>)</pre><p>The logic is:</p><pre>Score &gt;= 40<br>    ↓<br>Pass</pre><pre>Otherwise<br>    ↓<br>Fail</pre><p>This is a simple and efficient way to create a two-category feature.</p><h3>11. Multiple Conditions with np.select()</h3><p>Sometimes we need more than two categories.</p><p>For example:</p><pre>90+  → Excellent<br>75+  → Good<br>50+  → Average<br>Below 50 → Needs Improvement</pre><p>We can use:</p><pre>conditions = [<br>    df[&quot;Score&quot;] &gt;= 90,<br>    df[&quot;Score&quot;] &gt;= 75,<br>    df[&quot;Score&quot;] &gt;= 50<br>]</pre><pre>choices = [<br>    &quot;Excellent&quot;,<br>    &quot;Good&quot;,<br>    &quot;Average&quot;<br>]</pre><pre>df[&quot;Performance_Level&quot;] = np.select(<br>    conditions,<br>    choices,<br>    default=&quot;Needs Improvement&quot;<br>)</pre><p>This is often cleaner than writing a long nested conditional expression.</p><h3>12. Conditional Features with pd.cut()</h3><p>Pandas also provides:</p><pre>pd.cut()</pre><p>which is useful when we want to divide numerical values into ranges.</p><p>Suppose:</p><pre>df = pd.DataFrame({<br>    &quot;Age&quot;: [18, 25, 34, 42, 58, 67]<br>})</pre><p>We can create age groups:</p><pre>df[&quot;Age_Group&quot;] = pd.cut(<br>    df[&quot;Age&quot;],<br>    bins=[0, 18, 30, 45, 60, 100],<br>    labels=[<br>        &quot;Teen&quot;,<br>        &quot;Young Adult&quot;,<br>        &quot;Adult&quot;,<br>        &quot;Middle Age&quot;,<br>        &quot;Senior&quot;<br>    ]<br>)</pre><p>Now a continuous numerical variable has been transformed into a categorical feature.</p><p>This is useful when ranges have analytical meaning.</p><h3>13. Combining Multiple Columns</h3><p>Feature engineering often involves combining several columns.</p><p>Suppose we have:</p><pre>students = pd.DataFrame({<br>    &quot;Name&quot;: [&quot;Alice&quot;, &quot;Bob&quot;, &quot;Charlie&quot;],<br>    &quot;Math&quot;: [90, 70, 85],<br>    &quot;Science&quot;: [88, 75, 92],<br>    &quot;English&quot;: [95, 68, 80]<br>})</pre><p>We can create:</p><pre>students[&quot;Total&quot;] = (<br>    students[&quot;Math&quot;] +<br>    students[&quot;Science&quot;] +<br>    students[&quot;English&quot;]<br>)</pre><p>This creates a total score.</p><h3>14. Creating an Average Feature</h3><p>From the same dataset:</p><pre>students[&quot;Average&quot;] = (<br>    students[<br>        [&quot;Math&quot;, &quot;Science&quot;, &quot;English&quot;]<br>    ].mean(axis=1)<br>)</pre><p>Here:</p><pre>axis=1</pre><p>means we calculate across the columns for each row.</p><p>This is generally preferable to using row-wise apply() for a simple mathematical operation.</p><h3>15. Creating a Weighted Feature</h3><p>Sometimes different variables have different importance.</p><p>For example:</p><pre>Math      → 40%<br>Science   → 35%<br>English   → 25%</pre><p>We can create a weighted score:</p><pre>students[&quot;Weighted_Score&quot;] = (<br>    students[&quot;Math&quot;] * 0.40<br>    + students[&quot;Science&quot;] * 0.35<br>    + students[&quot;English&quot;] * 0.25<br>)</pre><p>This creates a new feature representing a custom scoring system.</p><p>Weighted features are common in:</p><ul><li>education</li><li>finance</li><li>business analytics</li><li>customer scoring</li><li>risk assessment</li></ul><h3>16. Combining Text Columns</h3><p>Feature engineering is not limited to numerical data.</p><p>Suppose:</p><pre>customers = pd.DataFrame({<br>    &quot;First_Name&quot;: [&quot;Alice&quot;, &quot;Bob&quot;, &quot;Charlie&quot;],<br>    &quot;Last_Name&quot;: [&quot;Smith&quot;, &quot;Jones&quot;, &quot;Brown&quot;]<br>})</pre><p>We can combine them:</p><pre>customers[&quot;Full_Name&quot;] = (<br>    customers[&quot;First_Name&quot;]<br>    + &quot; &quot;<br>    + customers[&quot;Last_Name&quot;]<br>)</pre><p>Now we have:</p><pre>Full_Name</pre><p>This can be useful for:</p><ul><li>reporting</li><li>display</li><li>exporting</li><li>customer communication</li></ul><h3>17. Creating Features from Strings</h3><p>Suppose we have email addresses:</p><pre>customers = pd.DataFrame({<br>    &quot;Email&quot;: [<br>        &quot;alice@gmail.com&quot;,<br>        &quot;bob@yahoo.com&quot;,<br>        &quot;charlie@outlook.com&quot;<br>    ]<br>})</pre><p>We can extract the domain:</p><pre>customers[&quot;Email_Domain&quot;] = (<br>    customers[&quot;Email&quot;]<br>    .str.split(&quot;@&quot;)<br>    .str[-1]<br>)</pre><p>Now we have a new feature:</p><pre>Email_Domain</pre><p>This can help us analyze customer groups based on email providers.</p><p>This connects directly with the string operations covered earlier in the series.</p><h3>18. Creating Features from Dates</h3><p>Dates are another major source of derived variables.</p><p>Suppose:</p><pre>df = pd.DataFrame({<br>    &quot;Join_Date&quot;: [<br>        &quot;2023-01-15&quot;,<br>        &quot;2024-06-20&quot;,<br>        &quot;2025-03-10&quot;<br>    ]<br>})</pre><p>First convert the column:</p><pre>df[&quot;Join_Date&quot;] = pd.to_datetime(<br>    df[&quot;Join_Date&quot;]<br>)</pre><p>Now we can extract:</p><pre>df[&quot;Join_Year&quot;] = (<br>    df[&quot;Join_Date&quot;].dt.year<br>)</pre><pre>df[&quot;Join_Month&quot;] = (<br>    df[&quot;Join_Date&quot;].dt.month<br>)</pre><pre>df[&quot;Join_Day&quot;] = (<br>    df[&quot;Join_Date&quot;].dt.day<br>)</pre><p>We can also extract:</p><pre>df[&quot;Day_Name&quot;] = (<br>    df[&quot;Join_Date&quot;].dt.day_name()<br>)</pre><p>Date-derived features are extremely useful in time-based analysis.</p><h3>19. Creating an Experience Feature</h3><p>Suppose we have:</p><pre>df = pd.DataFrame({<br>    &quot;Join_Year&quot;: [2018, 2021, 2023, 2015]<br>})</pre><p>If the analysis year is 2026:</p><pre>current_year = 2026</pre><pre>df[&quot;Experience&quot;] = (<br>    current_year -<br>    df[&quot;Join_Year&quot;]<br>)</pre><p>Now we have:</p><pre>Experience</pre><p>as a derived variable.</p><p>In a real project, you would normally derive experience from an actual date and an explicitly defined reference date rather than hard-coding a year without context.</p><h3>20. Feature Engineering from Business Rules</h3><p>A feature becomes especially valuable when it represents a meaningful business concept.</p><p>Suppose a company has:</p><pre>customers = pd.DataFrame({<br>    &quot;Customer&quot;: [<br>        &quot;A&quot;,<br>        &quot;B&quot;,<br>        &quot;C&quot;,<br>        &quot;D&quot;<br>    ],<br>    &quot;Orders&quot;: [25, 8, 18, 3],<br>    &quot;Revenue&quot;: [120000, 30000, 85000, 10000]<br>})</pre><p>We may want a customer segment.</p><p>For example:</p><pre>Revenue &gt;= 100000 and Orders &gt;= 20<br>→ Premium</pre><pre>Revenue &gt;= 50000<br>→ Regular</pre><pre>Otherwise<br>→ Basic</pre><p>We can create this feature using:</p><pre>conditions = [<br>    (<br>        (customers[&quot;Revenue&quot;] &gt;= 100000)<br>        &amp; (customers[&quot;Orders&quot;] &gt;= 20)<br>    ),<br>    customers[&quot;Revenue&quot;] &gt;= 50000<br>]</pre><pre>choices = [<br>    &quot;Premium&quot;,<br>    &quot;Regular&quot;<br>]</pre><pre>customers[&quot;Segment&quot;] = np.select(<br>    conditions,<br>    choices,<br>    default=&quot;Basic&quot;<br>)</pre><p>This is a meaningful derived variable because it represents a business concept.</p><h3>21. Feature Engineering vs Simple Column Creation</h3><p>Not every new column is necessarily useful feature engineering.</p><p>For example:</p><pre>df[&quot;Copy&quot;] = df[&quot;Score&quot;]</pre><p>creates a column, but it adds no new information.</p><p>Compare that with:</p><pre>df[&quot;Pass_Rate&quot;] = (<br>    df[&quot;Passed&quot;] /<br>    df[&quot;Total_Exams&quot;]<br>)</pre><p>This creates a new metric with analytical meaning.</p><p>A useful feature should ideally:</p><ul><li>represent useful information</li><li>simplify analysis</li><li>capture a meaningful relationship</li><li>support a specific business or analytical question</li></ul><h3>22. Avoiding Redundant Features</h3><p>More columns do not automatically mean better analysis.</p><p>Suppose we already have:</p><pre>Revenue<br>Cost<br>Profit</pre><p>and:</p><pre>Profit = Revenue - Cost</pre><p>The Profit column is useful because it directly represents a business metric.</p><p>But creating many duplicate versions of the same information can make the dataset harder to understand.</p><p>Feature engineering should therefore be intentional.</p><p>Ask:</p><blockquote><strong><em>What question will this feature help answer?</em></strong></blockquote><h3>23. Feature Engineering and Missing Values</h3><p>Derived features can also produce missing values.</p><p>For example:</p><pre>df[&quot;Profit_Margin&quot;] = (<br>    df[&quot;Profit&quot;] /<br>    df[&quot;Revenue&quot;]<br>)</pre><p>If:</p><pre>Revenue = 0</pre><p>the calculation can become problematic.</p><p>Therefore, before creating ratio-based features, check the underlying data.</p><p>For example:</p><pre>df[&quot;Revenue&quot;].isnull().sum()</pre><p>and:</p><pre>(df[&quot;Revenue&quot;] == 0).sum()</pre><p>Data quality should always be considered before feature creation.</p><h3>24. Handling Division by Zero</h3><p>Suppose:</p><pre>df = pd.DataFrame({<br>    &quot;Profit&quot;: [100, 200, 50],<br>    &quot;Revenue&quot;: [1000, 0, 500]<br>})</pre><p>Instead of blindly calculating:</p><pre>df[&quot;Margin&quot;] = (<br>    df[&quot;Profit&quot;] /<br>    df[&quot;Revenue&quot;]<br>)</pre><p>we can explicitly handle zero revenue.</p><p>For example:</p><pre>df[&quot;Margin&quot;] = np.where(<br>    df[&quot;Revenue&quot;] != 0,<br>    df[&quot;Profit&quot;] / df[&quot;Revenue&quot;],<br>    np.nan<br>)</pre><p>Now rows with zero revenue receive a missing value rather than an invalid business metric.</p><p>This is an important feature-engineering habit:</p><blockquote><strong><em>Think about edge cases before creating derived variables.</em></strong></blockquote><h3>25. Practical Example: Employee Dataset</h3><p>Let’s combine several feature-engineering techniques.</p><pre>employees = pd.DataFrame({<br>    &quot;Name&quot;: [<br>        &quot;Alice&quot;,<br>        &quot;Bob&quot;,<br>        &quot;Charlie&quot;,<br>        &quot;David&quot;<br>    ],<br>    &quot;Salary&quot;: [<br>        85000,<br>        55000,<br>        95000,<br>        62000<br>    ],<br>    &quot;Performance&quot;: [<br>        92,<br>        76,<br>        96,<br>        84<br>    ],<br>    &quot;Years_Experience&quot;: [<br>        7,<br>        3,<br>        10,<br>        5<br>    ]<br>})</pre><h3>Step 1 — Salary in Thousands</h3><pre>employees[&quot;Salary_K&quot;] = (<br>    employees[&quot;Salary&quot;] / 1000<br>)</pre><p>This creates a more compact representation for some analyses.</p><h3>Step 2 — Performance Level</h3><pre>conditions = [<br>    employees[&quot;Performance&quot;] &gt;= 90,<br>    employees[&quot;Performance&quot;] &gt;= 80,<br>    employees[&quot;Performance&quot;] &gt;= 70<br>]</pre><pre>choices = [<br>    &quot;Excellent&quot;,<br>    &quot;Good&quot;,<br>    &quot;Average&quot;<br>]</pre><pre>employees[&quot;Performance_Level&quot;] = np.select(<br>    conditions,<br>    choices,<br>    default=&quot;Needs Improvement&quot;<br>)</pre><h3>Step 3 — Experience Level</h3><pre>employees[&quot;Experience_Level&quot;] = pd.cut(<br>    employees[&quot;Years_Experience&quot;],<br>    bins=[0, 2, 5, 10, float(&quot;inf&quot;)],<br>    labels=[<br>        &quot;Junior&quot;,<br>        &quot;Mid-Level&quot;,<br>        &quot;Senior&quot;,<br>        &quot;Expert&quot;<br>    ]<br>)</pre><h3>Step 4 — Performance per Experience</h3><p>We can create a ratio:</p><pre>employees[&quot;Performance_per_Year&quot;] = (<br>    employees[&quot;Performance&quot;] /<br>    employees[&quot;Years_Experience&quot;]<br>)</pre><p>This is a derived metric.</p><p>However, it must be interpreted carefully because dividing performance by years of experience does not automatically produce a universally meaningful measure. Feature engineering should always be guided by domain context.</p><h3>26. A More Realistic Business Example</h3><p>Consider a sales dataset:</p><pre>sales = pd.DataFrame({<br>    &quot;Product&quot;: [<br>        &quot;Laptop&quot;,<br>        &quot;Phone&quot;,<br>        &quot;Tablet&quot;,<br>        &quot;Monitor&quot;<br>    ],<br>    &quot;Units&quot;: [<br>        10,<br>        25,<br>        15,<br>        8<br>    ],<br>    &quot;Unit_Price&quot;: [<br>        1000,<br>        600,<br>        400,<br>        300<br>    ],<br>    &quot;Unit_Cost&quot;: [<br>        750,<br>        400,<br>        250,<br>        180<br>    ]<br>})</pre><p>We can build several meaningful features.</p><h3>Revenue</h3><pre>sales[&quot;Revenue&quot;] = (<br>    sales[&quot;Units&quot;] *<br>    sales[&quot;Unit_Price&quot;]<br>)</pre><h3>Total Cost</h3><pre>sales[&quot;Total_Cost&quot;] = (<br>    sales[&quot;Units&quot;] *<br>    sales[&quot;Unit_Cost&quot;]<br>)</pre><h3>Profit</h3><pre>sales[&quot;Profit&quot;] = (<br>    sales[&quot;Revenue&quot;] -<br>    sales[&quot;Total_Cost&quot;]<br>)</pre><h3>Profit Margin</h3><pre>sales[&quot;Profit_Margin&quot;] = np.where(<br>    sales[&quot;Revenue&quot;] != 0,<br>    sales[&quot;Profit&quot;] /<br>    sales[&quot;Revenue&quot;],<br>    np.nan<br>)</pre><p>Now the original raw sales information has been transformed into a richer analytical dataset.</p><h3>27. Creating a High-Value Product Feature</h3><p>Suppose we want to identify products generating significant revenue.</p><p>We can create:</p><pre>sales[&quot;High_Revenue&quot;] = (<br>    sales[&quot;Revenue&quot;] &gt;= 10000<br>)</pre><p>This produces:</p><pre>True<br>False</pre><p>Boolean features are often useful for filtering:</p><pre>high_revenue_products = sales[<br>    sales[&quot;High_Revenue&quot;]<br>]</pre><p>This creates a simple analytical workflow:</p><pre>Create Feature<br>    ↓<br>Filter Dataset<br>    ↓<br>Analyze Selected Records</pre><h3>28. Feature Engineering with assign()</h3><p>Pandas also provides:</p><pre>assign()</pre><p>for creating new columns.</p><p>For example:</p><pre>df = df.assign(<br>    Revenue=df[&quot;Units&quot;] * df[&quot;Unit_Price&quot;]<br>)</pre><p>You can create multiple features:</p><pre>df = df.assign(<br>    Revenue=df[&quot;Units&quot;] * df[&quot;Unit_Price&quot;],<br>    Total_Cost=df[&quot;Units&quot;] * df[&quot;Unit_Cost&quot;]<br>)</pre><p>This can be useful when building transformation pipelines.</p><p>However, direct assignment remains perfectly valid:</p><pre>df[&quot;Revenue&quot;] = (<br>    df[&quot;Units&quot;] *<br>    df[&quot;Unit_Price&quot;]<br>)</pre><p>Choose the style that keeps your workflow readable.</p><h3>29. Feature Engineering with apply()</h3><p>Some features require custom logic.</p><p>For example:</p><pre>def customer_type(row):<br>    if row[&quot;Revenue&quot;] &gt;= 100000 and row[&quot;Orders&quot;] &gt;= 20:<br>        return &quot;Premium&quot;<br>    elif row[&quot;Revenue&quot;] &gt;= 50000:<br>        return &quot;Regular&quot;<br>    return &quot;Basic&quot;</pre><p>Then:</p><pre>df[&quot;Customer_Type&quot;] = df.apply(<br>    customer_type,<br>    axis=1<br>)</pre><p>This is a legitimate use of apply() because the feature depends on multiple columns and custom business rules.</p><p>But if a vectorized approach can express the same logic clearly, it is often preferable.</p><h3>30. Feature Engineering and Data Science</h3><p>Feature engineering is an important concept in data science and machine learning.</p><p>For example, a raw dataset might contain:</p><pre>Date<br>Price<br>Quantity<br>Customer_ID</pre><p>After feature engineering, we may create:</p><pre>Year<br>Month<br>Revenue<br>Customer_Order_Count<br>Average_Order_Value<br>Customer_Segment</pre><p>These derived variables can make patterns easier to analyze.</p><p>However:</p><blockquote><strong><em>Feature engineering is not limited to machine learning.</em></strong></blockquote><p>It is equally useful for:</p><ul><li>exploratory data analysis</li><li>business reporting</li><li>dashboards</li><li>descriptive analytics</li><li>data visualization</li><li>decision support</li></ul><h3>31. Good Feature Engineering Principles</h3><p>When creating a new feature, ask:</p><h3>1. What does this feature represent?</h3><p>It should have a clear meaning.</p><h3>2. Why do I need it?</h3><p>It should answer a real analytical question.</p><h3>3. Is the formula correct?</h3><p>Validate the transformation.</p><h3>4. What happens with missing values?</h3><p>Consider NaN and incomplete records.</p><h3>5. What happens with zero or negative values?</h3><p>Check edge cases.</p><h3>6. Is the feature redundant?</h3><p>Avoid unnecessary duplication.</p><h3>7. Is the feature interpretable?</h3><p>A useful feature should be understandable to the people using the analysis.</p><h3>32. Common Mistakes</h3><h3>Mistake 1: Creating Columns Without a Purpose</h3><p>Adding many columns does not automatically improve a dataset.</p><p>Always ask:</p><blockquote><em>What analytical question does this feature answer?</em></blockquote><h3>Mistake 2: Ignoring Data Types</h3><p>Before performing arithmetic, make sure numerical columns are actually numeric.</p><p>For example:</p><pre>df[&quot;Price&quot;].dtype</pre><p>If a numeric-looking column is stored as strings, arithmetic may fail or behave unexpectedly.</p><h3>Mistake 3: Ignoring Missing Values</h3><p>For example:</p><pre>df[&quot;Profit_Margin&quot;] = (<br>    df[&quot;Profit&quot;] /<br>    df[&quot;Revenue&quot;]<br>)</pre><p>may produce missing values or problematic results if revenue is missing or zero.</p><h3>Mistake 4: Using apply() When Vectorization Is Enough</h3><p>Avoid unnecessarily writing:</p><pre>df[&quot;Revenue&quot;].apply(<br>    lambda x: x * 1.15<br>)</pre><p>when this is enough:</p><pre>df[&quot;Revenue&quot;] * 1.15</pre><h3>Mistake 5: Creating Features That Leak Future Information</h3><p>This becomes especially important in predictive modeling.</p><p>A feature should only use information that would legitimately be available at the time of prediction.</p><p>For example, using a customer’s future purchase amount to predict whether they will make that purchase would introduce <strong>data leakage</strong>.</p><p>Feature engineering should respect the timing and context of the problem.</p><h3>33. A Practical Feature Engineering Workflow</h3><p>A strong workflow looks like:</p><pre>Understand the Dataset<br>        ↓<br>Identify the Analytical Question<br>        ↓<br>Select Relevant Raw Columns<br>        ↓<br>Define the Feature Formula / Rule<br>        ↓<br>Create the Feature<br>        ↓<br>Handle Edge Cases<br>        ↓<br>Validate the Result<br>        ↓<br>Analyze the New Feature</pre><p>For example:</p><pre>df[&quot;Revenue&quot;] = (<br>    df[&quot;Price&quot;] *<br>    df[&quot;Quantity&quot;]<br>)</pre><pre>df[&quot;Profit&quot;] = (<br>    df[&quot;Revenue&quot;] -<br>    df[&quot;Cost&quot;]<br>)</pre><pre>df[&quot;Profit_Margin&quot;] = np.where(<br>    df[&quot;Revenue&quot;] != 0,<br>    df[&quot;Profit&quot;] / df[&quot;Revenue&quot;],<br>    np.nan<br>)</pre><p>Then validate:</p><pre>df[[<br>    &quot;Revenue&quot;,<br>    &quot;Profit&quot;,<br>    &quot;Profit_Margin&quot;<br>]].head()</pre><p>Feature engineering should always be followed by validation.</p><h3>34. Quick Reference</h3><h3>Create a new column</h3><pre>df[&quot;New_Column&quot;] = value</pre><h3>Arithmetic transformation</h3><pre>df[&quot;Total&quot;] = (<br>    df[&quot;Price&quot;] *<br>    df[&quot;Quantity&quot;]<br>)</pre><h3>Add columns</h3><pre>df[&quot;Total&quot;] = (<br>    df[&quot;Online&quot;] +<br>    df[&quot;Store&quot;]<br>)</pre><h3>Subtract columns</h3><pre>df[&quot;Profit&quot;] = (<br>    df[&quot;Revenue&quot;] -<br>    df[&quot;Cost&quot;]<br>)</pre><h3>Ratio</h3><pre>df[&quot;Margin&quot;] = (<br>    df[&quot;Profit&quot;] /<br>    df[&quot;Revenue&quot;]<br>)</pre><h3>Boolean feature</h3><pre>df[&quot;High_Value&quot;] = (<br>    df[&quot;Revenue&quot;] &gt;= 100000<br>)</pre><h3>Two-category feature</h3><pre>df[&quot;Result&quot;] = np.where(<br>    df[&quot;Score&quot;] &gt;= 40,<br>    &quot;Pass&quot;,<br>    &quot;Fail&quot;<br>)</pre><h3>Multiple categories</h3><pre>df[&quot;Category&quot;] = np.select(<br>    conditions,<br>    choices,<br>    default=&quot;Other&quot;<br>)</pre><h3>Numeric binning</h3><pre>df[&quot;Age_Group&quot;] = pd.cut(<br>    df[&quot;Age&quot;],<br>    bins=[0, 18, 30, 45, 60, 100]<br>)</pre><h3>Row-wise average</h3><pre>df[&quot;Average&quot;] = df[<br>    [&quot;Math&quot;, &quot;Science&quot;, &quot;English&quot;]<br>].mean(axis=1)</pre><h3>Date feature</h3><pre>df[&quot;Year&quot;] = (<br>    df[&quot;Date&quot;].dt.year<br>)</pre><h3>Multiple derived columns with assign()</h3><pre>df = df.assign(<br>    Revenue=df[&quot;Units&quot;] * df[&quot;Price&quot;],<br>    Profit=df[&quot;Revenue&quot;] - df[&quot;Cost&quot;]<br>)</pre><h3>35. Final Takeaways</h3><p>Feature engineering is the process of transforming raw variables into more meaningful and useful features.</p><p>With Pandas, we can create features using:</p><pre>+<br>-<br>*<br>/</pre><p>and more advanced tools such as:</p><pre>np.where()<br>np.select()<br>pd.cut()<br>assign()</pre><p>We can also combine columns, transform text, extract date information, create ratios, build categories, and implement custom business rules.</p><p>The most important concepts are:</p><ul><li>New columns can be created directly with df[&quot;New&quot;] = ....</li><li>Arithmetic operations are usually handled efficiently through vectorized Pandas operations.</li><li>Conditional features can be created with Boolean logic, np.where(), or np.select().</li><li>Multiple columns can be combined to create meaningful derived variables.</li><li>Date and string columns are valuable sources of new features.</li><li>apply() can be useful for genuinely custom row-level logic.</li><li>Feature engineering should be driven by analytical questions, not by the desire to create more columns.</li><li>Missing values, zero values, data types, edge cases, and data leakage should be considered.</li><li>Every newly created feature should be validated.</li></ul><p>The central idea is simple:</p><blockquote><strong><em>Raw data tells you what happened. Well-designed features can help you understand what it means.</em></strong></blockquote><h3>36. Where We Go Next</h3><p>We have now covered:</p><pre>Pandas Basics<br>    ↓<br>Data Selection<br>    ↓<br>Filtering<br>    ↓<br>Missing Data<br>    ↓<br>Data Cleaning<br>    ↓<br>Data Types<br>    ↓<br>String Operations<br>    ↓<br>Statistics<br>    ↓<br>Sorting &amp; Ranking<br>    ↓<br>apply() &amp; Lambda<br>    ↓<br>Feature Engineering</pre><p>The next step is to explore another important transformation technique:</p><blockquote><strong><em>Data Scaling with Pandas: Normalization, Min-Max Scaling and Transformation</em></strong></blockquote><p>We will look at how numerical values can be transformed to comparable scales and when scaling is useful for data analysis and machine-learning preparation.</p><h3>About This Series</h3><p>This article is part of <strong>Data Analysis with Pandas</strong>, a practical series designed to help beginners and developers move from Pandas fundamentals to real-world data analysis.</p><p>The goal is not simply to learn individual functions.</p><p>It is to understand how different Pandas operations work together:</p><pre>Load<br>  ↓<br>Inspect<br>  ↓<br>Clean<br>  ↓<br>Transform<br>  ↓<br>Engineer Features<br>  ↓<br>Analyze<br>  ↓<br>Visualize<br>  ↓<br>Extract Insights</pre><p>That workflow is at the heart of practical data analysis with Python.</p><h3>Author</h3><p><strong>Engr. Md. Monjur Bakth Mazumder</strong></p><p><strong>GitHub:</strong><br><a href="https://github.com/monjurmbm404/Python-Data-Science-Tools">https://github.com/monjurmbm404/Python-Data-Science-Tools</a></p><h3>Final Thought</h3><p>Feature engineering is not about creating as many variables as possible.</p><p>It is about creating the <strong>right variables for the right question</strong>.</p><p>A simple transformation such as:</p><pre>Revenue = Price × Quantity</pre><p>can turn two raw measurements into a meaningful business metric.</p><p>A date can become a month.</p><p>A score can become a performance category.</p><p>Revenue and orders can become a customer segment.</p><p>That is the real value of feature engineering:</p><blockquote><strong><em>Turning raw columns into meaningful information.</em></strong></blockquote><h3>Connect &amp; Follow</h3><h4>Professional Profiles</h4><ul><li>Portfolio: <a href="https://monjurmbm404.github.io/?source=user_about----------------------d31e4eba6166------------------------">https://monjurmbm404.github.io</a></li><li>GitHub: <a href="https://github.com/monjurmbm404?source=user_about----------------------d31e4eba6166------------------------">https://github.com/monjurmbm404</a></li><li>LinkedIn: <a href="https://www.linkedin.com/in/monjurmbm404?source=user_about----------------------d31e4eba6166------------------------">https://www.linkedin.com/in/monjurmbm404</a></li><li>Medium: <a href="https://medium.com/@monjurmbm404?source=user_about----------------------d31e4eba6166------------------------">https://medium.com/@monjurmbm404</a></li></ul><h4>Books &amp; Publications</h4><ul><li>Amazon: <a href="https://www.amazon.com/author/monjurmbm404?source=user_about----------------------d31e4eba6166------------------------">https://www.amazon.com/author/monjurmbm404</a></li></ul><h4>Competitive Programming</h4><ul><li>Codeforces: <a href="https://codeforces.com/profile/monjurmbm404?source=user_about----------------------d31e4eba6166------------------------">https://codeforces.com/profile/monjurmbm404</a></li><li>CodeChef: <a href="https://www.codechef.com/users/monjurmbm404?source=user_about----------------------d31e4eba6166------------------------">https://www.codechef.com/users/monjurmbm404</a></li><li>LeetCode: <a href="https://leetcode.com/u/monjurmbm404/?source=user_about----------------------d31e4eba6166------------------------">https://leetcode.com/u/monjurmbm404/</a></li><li>VJudge: <a href="https://vjudge.net/user/monjurmbm404?source=user_about----------------------d31e4eba6166------------------------">https://vjudge.net/user/monjurmbm404</a></li></ul><h4>Social Media</h4><ul><li>YouTube: <a href="https://youtube.com/@monjurmbm404?source=user_about----------------------d31e4eba6166------------------------">https://youtube.com/@monjurmbm404</a></li><li>X: <a href="https://x.com/monjurmbm404?source=user_about----------------------d31e4eba6166------------------------">https://x.com/monjurmbm404</a></li><li>Facebook: <a href="https://www.facebook.com/monjurmbm404?source=user_about----------------------d31e4eba6166------------------------">https://www.facebook.com/monjurmbm404</a></li></ul><h4>Connect with Me</h4><ul><li>Email<strong>:</strong> <a href="/service/mailto:monjurmbm404@gmail.com">monjurmbm404@gmail.com</a></li></ul><p>— <strong>Engr. Md Monjur Bakth Mazumder<br></strong>Software Engineer | Technical Author | Competitive Programmer | AI/ML Enthusiast</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=18dab33a85bb" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Pandas apply(): Lambda Functions and Row-wise vs Column-wise Operations]]></title>
            <link>https://medium.com/@monjurmbm404/pandas-apply-lambda-functions-and-row-wise-vs-column-wise-operations-9a8eaa525592?source=rss-d31e4eba6166------2</link>
            <guid isPermaLink="false">https://medium.com/p/9a8eaa525592</guid>
            <dc:creator><![CDATA[Engr. Md Monjur Bakth Mazumder]]></dc:creator>
            <pubDate>Mon, 09 Nov 2026 14:01:03 GMT</pubDate>
            <atom:updated>2026-11-09T14:01:03.524Z</atom:updated>
            <content:encoded><![CDATA[<blockquote><strong><em>Series:</em></strong><em> Data Analysis with Pandas<br></em><strong><em>Part:</em></strong><em> 16</em></blockquote><blockquote>A Practical Guide to apply(), Lambda Functions, Custom Functions and Efficient Data Transformation in Pandas</blockquote><p>When working with real-world data, built-in Pandas operations can solve many problems.</p><p>For example:</p><pre>df[&quot;Salary&quot;] * 1.10</pre><p>can increase a salary column by 10%.</p><p>But what happens when your transformation requires custom logic?</p><p>For example:</p><ul><li>Assign a category based on a score</li><li>Calculate a custom value from multiple columns</li><li>Convert a value using a business rule</li><li>Apply the same custom function to every value</li><li>Create a new column based on several conditions</li></ul><p>This is where Pandas provides:</p><pre>apply()</pre><p>and Python’s:</p><pre>lambda</pre><p>functions.</p><p>In this article, we will learn how apply() works, how lambda functions fit into the workflow, how to perform column-wise and row-wise operations, and—most importantly—when apply() should and should not be used.</p><h3>1. What Is apply() in Pandas?</h3><p>The apply() method allows you to apply a function to values in a Series or along an axis of a DataFrame.</p><p>At a high level:</p><pre>Data<br> ↓<br>Function<br> ↓<br>Transformed Data</pre><p>For example:</p><pre>import pandas as pd</pre><pre>df = pd.DataFrame({<br>    &quot;Name&quot;: [&quot;Alice&quot;, &quot;Bob&quot;, &quot;Charlie&quot;],<br>    &quot;Score&quot;: [85, 72, 95]<br>})</pre><p>Suppose we want to increase every score by 5.</p><p>We can write:</p><pre>df[&quot;Score&quot;] = df[&quot;Score&quot;].apply(<br>    lambda x: x + 5<br>)</pre><p>Now every value in the Score column has been transformed.</p><h3>2. What Is a Lambda Function?</h3><p>A lambda is a small anonymous function in Python.</p><p>Instead of writing:</p><pre>def add_five(x):<br>    return x + 5</pre><p>we can write:</p><pre>lambda x: x + 5</pre><p>The structure is:</p><pre>lambda arguments: expression</pre><p>For example:</p><pre>lambda x: x * 2</pre><p>means:</p><blockquote><em>Take </em><em>x and return </em><em>x * 2.</em></blockquote><p>Another example:</p><pre>lambda x: x.upper()</pre><p>means:</p><blockquote><em>Take </em><em>x and convert it to uppercase.</em></blockquote><p>Lambda functions are often useful with methods such as:</p><pre>apply()</pre><p>because they allow us to define small transformations directly where they are needed.</p><h3>3. Basic apply() with a Lambda</h3><p>Let’s start with a simple example.</p><pre>df = pd.DataFrame({<br>    &quot;Score&quot;: [70, 80, 90, 60]<br>})</pre><p>Apply a 10-point increase:</p><pre>df[&quot;Score&quot;] = df[&quot;Score&quot;].apply(<br>    lambda x: x + 10<br>)</pre><p>The transformation happens for each value in the Series.</p><p>Conceptually:</p><pre>70 → 80<br>80 → 90<br>90 → 100<br>60 → 70</pre><p>This is a simple example of element-wise application on a Series.</p><h3>4. apply() on a Series</h3><p>When you use:</p><pre>df[&quot;Score&quot;].apply(...)</pre><p>the function is applied to each value in the Series.</p><p>For example:</p><pre>df[&quot;Score&quot;].apply(<br>    lambda x: x * 2<br>)</pre><p>Each value is processed independently.</p><p>This is useful for custom transformations.</p><p>For example:</p><pre>df[&quot;Score_Category&quot;] = df[&quot;Score&quot;].apply(<br>    lambda x: &quot;Pass&quot; if x &gt;= 50 else &quot;Fail&quot;<br>)</pre><p>Now each score is converted into a category.</p><h3>5. Using a Custom Function with apply()</h3><p>Lambda functions are useful for short logic.</p><p>But sometimes your logic becomes more complex.</p><p>Instead of:</p><pre>lambda x: ...</pre><p>you can define a normal Python function.</p><p>Example:</p><pre>def classify_score(score):<br>    if score &gt;= 80:<br>        return &quot;Excellent&quot;<br>    elif score &gt;= 60:<br>        return &quot;Good&quot;<br>    elif score &gt;= 40:<br>        return &quot;Average&quot;<br>    else:<br>        return &quot;Poor&quot;</pre><p>Then:</p><pre>df[&quot;Category&quot;] = df[&quot;Score&quot;].apply(<br>    classify_score<br>)</pre><p>This is often easier to read when the business logic contains multiple conditions.</p><h3>6. Lambda vs Custom Function</h3><p>For very small transformations:</p><pre>df[&quot;Score&quot;].apply(<br>    lambda x: x + 5<br>)</pre><p>can be concise.</p><p>For more complex logic:</p><pre>def classify_score(score):<br>    if score &gt;= 80:<br>        return &quot;Excellent&quot;<br>    elif score &gt;= 60:<br>        return &quot;Good&quot;<br>    elif score &gt;= 40:<br>        return &quot;Average&quot;<br>    else:<br>        return &quot;Poor&quot;</pre><p>is usually easier to understand.</p><p>A useful rule is:</p><pre>Short logic<br>    ↓<br>lambda</pre><pre>Complex or reusable logic<br>    ↓<br>named function</pre><p>The goal is not to use lambda everywhere.</p><p>The goal is to make your transformation clear.</p><h3>7. Column-wise Operation</h3><p>There are two different ideas that beginners often confuse:</p><pre>Applying a function to values in a column</pre><p>and:</p><pre>Applying a function to columns of a DataFrame</pre><p>Let’s start with the first.</p><p>Suppose:</p><pre>df = pd.DataFrame({<br>    &quot;Name&quot;: [&quot;Alice&quot;, &quot;Bob&quot;, &quot;Charlie&quot;],<br>    &quot;Salary&quot;: [70000, 55000, 85000]<br>})</pre><p>We can transform the Salary column:</p><pre>df[&quot;Salary&quot;] = df[&quot;Salary&quot;].apply(<br>    lambda x: x * 1.10<br>)</pre><p>Here the function receives each salary value.</p><h3>8. Applying a Function to Each DataFrame Column</h3><p>DataFrame apply() behaves differently.</p><p>Consider:</p><pre>df = pd.DataFrame({<br>    &quot;Math&quot;: [80, 90, 70],<br>    &quot;Science&quot;: [85, 95, 75]<br>})</pre><p>If we write:</p><pre>df.apply(lambda column: column.max())</pre><p>Pandas applies the function to each column.</p><p>The result gives the maximum value from each column.</p><p>This is a <strong>column-wise DataFrame operation</strong>.</p><p>The default axis is:</p><pre>axis=0</pre><p>So:</p><pre>df.apply(function)</pre><p>is conceptually:</p><pre>Column 1 → function<br>Column 2 → function<br>Column 3 → function</pre><h3>9. Understanding axis=0</h3><p>In DataFrame apply():</p><pre>axis=0</pre><p>means the function is applied along the columns.</p><p>For example:</p><pre>df.apply(<br>    lambda column: column.mean(),<br>    axis=0<br>)</pre><p>calculates the mean of each column.</p><p>You can think of it as:</p><pre>Column → Function → Result</pre><p>For many standard operations, however, you should prefer the direct Pandas method:</p><pre>df.mean()</pre><p>rather than:</p><pre>df.apply(lambda column: column.mean())</pre><p>The direct method is simpler and generally more idiomatic.</p><p>This distinction becomes important when deciding whether apply() is actually necessary.</p><h3>10. Row-wise Operation with axis=1</h3><p>Now suppose we want to calculate something using multiple columns from the same row.</p><p>Example:</p><pre>df = pd.DataFrame({<br>    &quot;Math&quot;: [80, 90, 70],<br>    &quot;Science&quot;: [85, 95, 75]<br>})</pre><p>We want the total score for each student.</p><p>We can write:</p><pre>df[&quot;Total&quot;] = df.apply(<br>    lambda row: row[&quot;Math&quot;] + row[&quot;Science&quot;],<br>    axis=1<br>)</pre><p>Here:</p><pre>axis=1</pre><p>means the function receives one row at a time.</p><p>Conceptually:</p><pre>Row 1 → Function<br>Row 2 → Function<br>Row 3 → Function</pre><p>This is called a <strong>row-wise operation</strong>.</p><h3>11. Why axis=1 Is Important</h3><p>Suppose we have:</p><pre>Name     Math    Science<br>Alice     80       85<br>Bob       90       95<br>Charlie   70       75</pre><p>If we want:</p><pre>Math + Science</pre><p>for each student, the calculation needs values from multiple columns in the same row.</p><p>Therefore:</p><pre>axis=1</pre><p>is appropriate.</p><p>Example:</p><pre>df[&quot;Total&quot;] = df.apply(<br>    lambda row: row[&quot;Math&quot;] + row[&quot;Science&quot;],<br>    axis=1<br>)</pre><p>This is one of the most common practical uses of DataFrame apply().</p><h3>12. Row-wise Average</h3><p>We can also calculate an average:</p><pre>df[&quot;Average&quot;] = df.apply(<br>    lambda row: (row[&quot;Math&quot;] + row[&quot;Science&quot;]) / 2,<br>    axis=1<br>)</pre><p>Now each row gets its own average.</p><p>However, notice something important.</p><p>For simple arithmetic across numeric columns, there is usually a better direct Pandas approach.</p><p>For example:</p><pre>df[&quot;Average&quot;] = df[[&quot;Math&quot;, &quot;Science&quot;]].mean(axis=1)</pre><p>This is generally preferable.</p><p>Why?</p><p>Because the operation is already supported directly by Pandas.</p><h3>13. apply() vs Direct Vectorized Operation</h3><p>This is one of the most important lessons in this article.</p><p>Suppose we want to add 10 to every value:</p><pre>df[&quot;Score&quot;].apply(<br>    lambda x: x + 10<br>)</pre><p>This works.</p><p>But the direct Pandas operation is:</p><pre>df[&quot;Score&quot;] + 10</pre><p>The second version is usually preferable.</p><p>Why?</p><p>Because Pandas and NumPy are designed to perform many operations in a vectorized way.</p><p>Vectorized operations are generally:</p><ul><li>simpler</li><li>more readable</li><li>more idiomatic</li><li>often faster</li><li>easier to optimize</li></ul><p>So:</p><pre>apply(lambda x: x + 10)</pre><p>is not automatically better just because it looks flexible.</p><h3>14. Another Example: Multiplication</h3><p>Instead of:</p><pre>df[&quot;Salary&quot;].apply(<br>    lambda x: x * 1.10<br>)</pre><p>prefer:</p><pre>df[&quot;Salary&quot;] * 1.10</pre><p>The direct expression clearly communicates the operation.</p><p>Similarly:</p><pre>df[&quot;Score&quot;].apply(<br>    lambda x: x * 2<br>)</pre><p>can usually become:</p><pre>df[&quot;Score&quot;] * 2</pre><p>This is an important habit to develop.</p><h3>15. Direct Methods vs apply()</h3><p>Suppose you want the absolute value.</p><p>You might write:</p><pre>df[&quot;Value&quot;].apply(<br>    lambda x: abs(x)<br>)</pre><p>But Pandas provides:</p><pre>df[&quot;Value&quot;].abs()</pre><p>If you want the lowercase version of text:</p><pre>df[&quot;Name&quot;].apply(<br>    lambda x: x.lower()<br>)</pre><p>Pandas provides:</p><pre>df[&quot;Name&quot;].str.lower()</pre><p>If you want to calculate a mean:</p><pre>df[&quot;Score&quot;].apply(<br>    lambda x: ...<br>)</pre><p>is unnecessary when you can use:</p><pre>df[&quot;Score&quot;].mean()</pre><p>The general principle is:</p><blockquote><strong><em>Use the most direct Pandas operation available before reaching for </em></strong><strong><em>apply().</em></strong></blockquote><h3>16. When apply() Is Useful</h3><p>So when should we actually use apply()?</p><p>It becomes useful when the transformation is:</p><ul><li>custom</li><li>conditional</li><li>difficult to express with existing vectorized methods</li><li>dependent on multiple values</li><li>based on a reusable Python function</li></ul><p>For example:</p><pre>def classify_salary(salary):<br>    if salary &gt;= 80000:<br>        return &quot;High&quot;<br>    elif salary &gt;= 60000:<br>        return &quot;Medium&quot;<br>    else:<br>        return &quot;Low&quot;</pre><p>Then:</p><pre>df[&quot;Salary_Level&quot;] = df[&quot;Salary&quot;].apply(<br>    classify_salary<br>)</pre><p>This is a reasonable use of apply().</p><h3>17. Practical Example: Salary Classification</h3><p>Let’s create an employee dataset:</p><pre>employees = pd.DataFrame({<br>    &quot;Name&quot;: [&quot;Alice&quot;, &quot;Bob&quot;, &quot;Charlie&quot;, &quot;David&quot;],<br>    &quot;Salary&quot;: [85000, 55000, 95000, 62000]<br>})</pre><p>Define the business rule:</p><pre>def salary_level(salary):<br>    if salary &gt;= 90000:<br>        return &quot;High&quot;<br>    elif salary &gt;= 60000:<br>        return &quot;Medium&quot;<br>    else:<br>        return &quot;Low&quot;</pre><p>Apply it:</p><pre>employees[&quot;Salary_Level&quot;] = employees[&quot;Salary&quot;].apply(<br>    salary_level<br>)</pre><p>This creates a new categorical feature based on business logic.</p><p>Here apply() is useful because the transformation is custom and conditional.</p><h3>18. Practical Example: Student Classification</h3><p>Consider:</p><pre>students = pd.DataFrame({<br>    &quot;Name&quot;: [&quot;Alice&quot;, &quot;Bob&quot;, &quot;Charlie&quot;, &quot;David&quot;],<br>    &quot;Score&quot;: [92, 74, 58, 35]<br>})</pre><p>Define:</p><pre>def classify(score):<br>    if score &gt;= 80:<br>        return &quot;Excellent&quot;<br>    elif score &gt;= 60:<br>        return &quot;Pass&quot;<br>    elif score &gt;= 40:<br>        return &quot;Needs Improvement&quot;<br>    else:<br>        return &quot;Fail&quot;</pre><p>Then:</p><pre>students[&quot;Result&quot;] = students[&quot;Score&quot;].apply(<br>    classify<br>)</pre><p>This is easy to understand because the business rule is separated from the DataFrame operation.</p><h3>19. The Same Logic with Lambda</h3><p>If the rule is short, we can use:</p><pre>students[&quot;Result&quot;] = students[&quot;Score&quot;].apply(<br>    lambda x: &quot;Pass&quot; if x &gt;= 40 else &quot;Fail&quot;<br>)</pre><p>This is appropriate when the condition is simple.</p><p>For example:</p><pre>score &gt;= 40<br>    ↓<br>Pass</pre><pre>score &lt; 40<br>    ↓<br>Fail</pre><p>But if the classification contains many levels, a named function is usually clearer.</p><h3>20. Multiple Conditions with Lambda</h3><p>Lambda can also handle multiple conditions.</p><p>For example:</p><pre>students[&quot;Category&quot;] = students[&quot;Score&quot;].apply(<br>    lambda x:<br>        &quot;Excellent&quot; if x &gt;= 80<br>        else &quot;Good&quot; if x &gt;= 60<br>        else &quot;Average&quot; if x &gt;= 40<br>        else &quot;Poor&quot;<br>)</pre><p>This works, but nested conditional expressions can become difficult to read.</p><p>If the business logic becomes complicated, use a normal function.</p><p>Readable code is more important than making everything one line.</p><h3>21. Row-wise Custom Function</h3><p>Now consider a dataset with multiple scores:</p><pre>students = pd.DataFrame({<br>    &quot;Name&quot;: [&quot;Alice&quot;, &quot;Bob&quot;, &quot;Charlie&quot;],<br>    &quot;Math&quot;: [90, 70, 85],<br>    &quot;Science&quot;: [88, 75, 92],<br>    &quot;English&quot;: [95, 68, 80]<br>})</pre><p>Suppose we want to calculate a weighted score:</p><pre>Math      → 40%<br>Science   → 35%<br>English   → 25%</pre><p>A custom function can receive the complete row:</p><pre>def weighted_score(row):<br>    return (<br>        row[&quot;Math&quot;] * 0.40 +<br>        row[&quot;Science&quot;] * 0.35 +<br>        row[&quot;English&quot;] * 0.25<br>    )</pre><p>Apply it row-wise:</p><pre>students[&quot;Weighted_Score&quot;] = students.apply(<br>    weighted_score,<br>    axis=1<br>)</pre><p>This is a strong practical use case for row-wise apply().</p><p>The function needs multiple columns from the same row.</p><h3>22. Why Not Always Use Row-wise apply()?</h3><p>Although row-wise apply() is useful, it can be slower on large datasets.</p><p>For example:</p><pre>df.apply(<br>    custom_function,<br>    axis=1<br>)</pre><p>requires Pandas to repeatedly call the Python function for individual rows.</p><p>For small datasets, this may not matter.</p><p>For large datasets, it can become expensive.</p><p>Therefore:</p><blockquote><strong><em>Use row-wise </em></strong><strong><em>apply() when the logic genuinely requires row-level custom processing—not simply because it is convenient.</em></strong></blockquote><h3>23. Vectorized Alternative to Row-wise Calculation</h3><p>Suppose we want:</p><pre>Weighted_Score = (<br>    Math * 0.40<br>    + Science * 0.35<br>    + English * 0.25<br>)</pre><p>Instead of:</p><pre>students.apply(<br>    weighted_score,<br>    axis=1<br>)</pre><p>we can directly write:</p><pre>students[&quot;Weighted_Score&quot;] = (<br>    students[&quot;Math&quot;] * 0.40<br>    + students[&quot;Science&quot;] * 0.35<br>    + students[&quot;English&quot;] * 0.25<br>)</pre><p>This is generally preferable because the calculation can be performed using vectorized column operations.</p><p>So the decision should be:</p><pre>Can Pandas/NumPy express the calculation directly?<br>        ↓<br>Yes → Prefer vectorized operation<br>        ↓<br>No → Consider apply()</pre><h3>24. apply() with Built-in Functions</h3><p>You are not limited to lambda or custom functions.</p><p>For example:</p><pre>df[&quot;Name&quot;].apply(len)</pre><p>calculates the length of each name.</p><p>You could also use:</p><pre>df[&quot;Name&quot;].apply(str.upper)</pre><p>However, when Pandas provides a dedicated vectorized accessor, it is often preferable.</p><p>For example:</p><pre>df[&quot;Name&quot;].str.upper()</pre><p>is clearer for string operations.</p><h3>25. apply() on Multiple Columns</h3><p>Suppose:</p><pre>df = pd.DataFrame({<br>    &quot;Math&quot;: [80, 90, 70],<br>    &quot;Science&quot;: [85, 95, 75],<br>    &quot;English&quot;: [88, 92, 78]<br>})</pre><p>We can calculate the maximum score for each column:</p><pre>df.apply(<br>    lambda column: column.max()<br>)</pre><p>But again, a direct method is simpler:</p><pre>df.max()</pre><p>This is a good example of why understanding apply() is not the same as using it everywhere.</p><h3>26. axis=0 vs axis=1</h3><p>This distinction is worth remembering.</p><h3>axis=0</h3><p>The function works column-wise.</p><pre>df.apply(<br>    function,<br>    axis=0<br>)</pre><p>Conceptually:</p><pre>Column 1 → function<br>Column 2 → function<br>Column 3 → function</pre><h3>axis=1</h3><p>The function works row-wise.</p><pre>df.apply(<br>    function,<br>    axis=1<br>)</pre><p>Conceptually:</p><pre>Row 1 → function<br>Row 2 → function<br>Row 3 → function</pre><p>A simple memory trick:</p><pre>axis=0 → move down rows → process columns<br>axis=1 → move across columns → process rows</pre><h3>27. A Common axis Mistake</h3><p>Beginners often write:</p><pre>df.apply(<br>    lambda row: ...,<br>    axis=0<br>)</pre><p>when they actually want information from multiple columns in each row.</p><p>If your function needs:</p><pre>row[&quot;Math&quot;]<br>row[&quot;Science&quot;]<br>row[&quot;English&quot;]</pre><p>you usually need:</p><pre>axis=1</pre><p>because the function must receive a complete row.</p><h3>28. apply() for Text Transformation</h3><p>Suppose we have:</p><pre>df = pd.DataFrame({<br>    &quot;Name&quot;: [&quot;alice&quot;, &quot;BOB&quot;, &quot;Charlie&quot;]<br>})</pre><p>We could write:</p><pre>df[&quot;Name&quot;].apply(<br>    lambda x: x.title()<br>)</pre><p>This works.</p><p>But Pandas provides:</p><pre>df[&quot;Name&quot;].str.title()</pre><p>which is the more direct vectorized string operation.</p><p>This connects to our previous article on Pandas string operations.</p><p>Whenever a dedicated .str method exists, it is usually the better first choice.</p><h3>29. apply() for Custom Business Rules</h3><p>One of the strongest use cases for apply() is business logic.</p><p>For example:</p><pre>def customer_segment(row):<br>    if row[&quot;Revenue&quot;] &gt;= 100000 and row[&quot;Orders&quot;] &gt;= 20:<br>        return &quot;Premium&quot;<br>    elif row[&quot;Revenue&quot;] &gt;= 50000:<br>        return &quot;Regular&quot;<br>    else:<br>        return &quot;Basic&quot;</pre><p>Then:</p><pre>df[&quot;Segment&quot;] = df.apply(<br>    customer_segment,<br>    axis=1<br>)</pre><p>Here the classification depends on multiple columns.</p><p>This is exactly the kind of custom transformation where apply() can be useful.</p><h3>30. A Practical Decision Framework</h3><p>Before using apply(), ask:</p><h3>Question 1</h3><p>Can the operation be written directly?</p><p>For example:</p><pre>df[&quot;Salary&quot;] * 1.10</pre><p>If yes, prefer the direct operation.</p><h3>Question 2</h3><p>Does Pandas already provide a method?</p><p>For example:</p><pre>df[&quot;Score&quot;].mean()</pre><p>If yes, use the built-in method.</p><h3>Question 3</h3><p>Does the operation involve a custom business rule?</p><p>If yes, apply() may be appropriate.</p><h3>Question 4</h3><p>Does the logic require multiple columns from the same row?</p><p>If yes, consider:</p><pre>axis=1</pre><h3>Question 5</h3><p>Is the transformation simple enough for a lambda?</p><p>If yes:</p><pre>lambda x: ...</pre><p>can be convenient.</p><p>If the logic is complex or reusable, define a normal function.</p><h3>31. A Better Mental Model</h3><p>Think about Pandas transformations in this order:</p><pre>1. Built-in Pandas method<br>        ↓<br>2. Vectorized operation<br>        ↓<br>3. Dedicated accessor<br>        ↓<br>4. apply()<br>        ↓<br>5. Custom Python function</pre><p>This is not an absolute rule, but it is a useful practical guideline.</p><p>For example:</p><pre>df[&quot;Salary&quot;] * 1.10</pre><p>is preferable to:</p><pre>df[&quot;Salary&quot;].apply(lambda x: x * 1.10)</pre><p>And:</p><pre>df[&quot;Name&quot;].str.lower()</pre><p>is preferable to:</p><pre>df[&quot;Name&quot;].apply(lambda x: x.lower())</pre><p>But a complex business rule may genuinely need:</p><pre>df.apply(custom_function, axis=1)</pre><h3>32. Common Mistakes</h3><h3>Mistake 1: Using apply() for Everything</h3><p>Just because apply() can perform a transformation does not mean it should.</p><p>Avoid:</p><pre>df[&quot;Salary&quot;].apply(<br>    lambda x: x * 1.10<br>)</pre><p>when this is enough:</p><pre>df[&quot;Salary&quot;] * 1.10</pre><h3>Mistake 2: Forgetting axis=1</h3><p>If your function needs multiple columns from the same row:</p><pre>row[&quot;Math&quot;]<br>row[&quot;Science&quot;]</pre><p>you usually need:</p><pre>axis=1</pre><h3>Mistake 3: Making Lambda Too Complicated</h3><p>This:</p><pre>lambda x: ...</pre><p>should ideally remain easy to understand.</p><p>If you find yourself writing a long nested expression, use a named function instead.</p><h3>Mistake 4: Ignoring Existing Pandas Methods</h3><p>Before using:</p><pre>apply()</pre><p>check whether Pandas already provides:</p><pre>sum()<br>mean()<br>max()<br>min()<br>abs()<br>astype()<br>str.*</pre><p>or another dedicated operation.</p><h3>Mistake 5: Assuming apply() Is Always Fast</h3><p>apply() often involves Python-level function calls.</p><p>For large datasets, vectorized operations can be significantly more efficient.</p><p>Performance depends on the operation and data, but the general principle remains:</p><blockquote><em>Prefer vectorized operations when they naturally express the task.</em></blockquote><h3>33. Complete Practical Example</h3><p>Let’s combine the concepts.</p><pre>import pandas as pd</pre><pre>employees = pd.DataFrame({<br>    &quot;Name&quot;: [<br>        &quot;Alice&quot;,<br>        &quot;Bob&quot;,<br>        &quot;Charlie&quot;,<br>        &quot;David&quot;<br>    ],<br>    &quot;Department&quot;: [<br>        &quot;IT&quot;,<br>        &quot;HR&quot;,<br>        &quot;IT&quot;,<br>        &quot;Finance&quot;<br>    ],<br>    &quot;Salary&quot;: [<br>        85000,<br>        55000,<br>        95000,<br>        62000<br>    ],<br>    &quot;Performance&quot;: [<br>        92,<br>        76,<br>        96,<br>        84<br>    ]<br>})</pre><h3>Step 1 — Direct vectorized calculation</h3><p>Increase salary by 10%:</p><pre>employees[&quot;Adjusted_Salary&quot;] = (<br>    employees[&quot;Salary&quot;] * 1.10<br>)</pre><p>No apply() is necessary.</p><h3>Step 2 — Custom classification</h3><pre>def performance_level(score):<br>    if score &gt;= 90:<br>        return &quot;Excellent&quot;<br>    elif score &gt;= 80:<br>        return &quot;Good&quot;<br>    elif score &gt;= 70:<br>        return &quot;Average&quot;<br>    else:<br>        return &quot;Needs Improvement&quot;</pre><p>Apply:</p><pre>employees[&quot;Performance_Level&quot;] = (<br>    employees[&quot;Performance&quot;].apply(<br>        performance_level<br>    )<br>)</pre><p>Here apply() makes sense because we are implementing a custom rule.</p><h3>Step 3 — Row-wise custom logic</h3><p>Suppose we want a simple employee score based on:</p><pre>Performance → 70%<br>Salary      → 30%</pre><p>We could define:</p><pre>def employee_score(row):<br>    salary_score = row[&quot;Salary&quot;] / 1000</pre><pre>    return (<br>        row[&quot;Performance&quot;] * 0.70<br>        + salary_score * 0.30<br>    )</pre><p>Then:</p><pre>employees[&quot;Employee_Score&quot;] = employees.apply(<br>    employee_score,<br>    axis=1<br>)</pre><p>This demonstrates a genuine row-wise custom transformation.</p><h3>34. apply() Workflow</h3><p>A practical workflow is:</p><pre>Understand the Transformation<br>        ↓<br>Check for Built-in Pandas Method<br>        ↓<br>Check for Vectorized Operation<br>        ↓<br>Check Dedicated Accessor<br>        ↓<br>Use apply() if Custom Logic Is Needed<br>        ↓<br>Choose Series-wise or Row-wise<br>        ↓<br>Use Lambda or Named Function<br>        ↓<br>Validate the Result</pre><p>This prevents apply() from becoming the default solution to every problem.</p><h3>35. Quick Reference</h3><h3>Apply a lambda to a Series</h3><pre>df[&quot;Score&quot;].apply(<br>    lambda x: x + 5<br>)</pre><h3>Apply a custom function</h3><pre>def classify(x):<br>    return &quot;Pass&quot; if x &gt;= 40 else &quot;Fail&quot;</pre><pre>df[&quot;Result&quot;] = df[&quot;Score&quot;].apply(classify)</pre><h3>DataFrame column-wise operation</h3><pre>df.apply(<br>    lambda column: column.max(),<br>    axis=0<br>)</pre><h3>DataFrame row-wise operation</h3><pre>df.apply(<br>    lambda row: row[&quot;Math&quot;] + row[&quot;Science&quot;],<br>    axis=1<br>)</pre><h3>Custom row-wise function</h3><pre>def calculate(row):<br>    return row[&quot;Math&quot;] * 0.5 + row[&quot;Science&quot;] * 0.5</pre><pre>df[&quot;Final&quot;] = df.apply(<br>    calculate,<br>    axis=1<br>)</pre><h3>Prefer direct vectorization when possible</h3><p>Instead of:</p><pre>df[&quot;Score&quot;].apply(<br>    lambda x: x * 2<br>)</pre><p>prefer:</p><pre>df[&quot;Score&quot;] * 2</pre><h3>36. Final Takeaways</h3><p>The apply() method is one of the most flexible tools in Pandas.</p><p>It allows you to apply custom Python logic to data and is particularly useful when a transformation cannot be expressed easily through standard vectorized operations.</p><p>The key concepts are:</p><ul><li>Series.apply() can process values in a Series.</li><li>DataFrame.apply() can operate across columns or rows.</li><li>axis=0 represents column-wise application.</li><li>axis=1 represents row-wise application.</li><li>lambda is useful for short, simple functions.</li><li>Named functions are better for complex or reusable business logic.</li><li>Row-wise apply() is useful when logic depends on multiple columns from the same row.</li><li>Built-in Pandas methods should generally be preferred when they already solve the problem.</li><li>Vectorized operations are usually preferable to apply() for straightforward numerical transformations.</li><li>apply() should be a deliberate tool—not the default solution.</li></ul><p>The most important principle is:</p><blockquote><strong><em>Do not use </em></strong><strong><em>apply() just because you can. Use it when the problem actually requires custom logic.</em></strong></blockquote><p>Understanding this distinction will help you write Pandas code that is not only functional, but also cleaner, more readable, and often more efficient.</p><h3>37. Where We Go Next</h3><p>We have now covered an important part of the Pandas data-transformation workflow:</p><pre>Selection<br>    ↓<br>Filtering<br>    ↓<br>Cleaning<br>    ↓<br>Data Type Conversion<br>    ↓<br>String Operations<br>    ↓<br>Statistics<br>    ↓<br>Sorting &amp; Ranking<br>    ↓<br>Custom Transformation</pre><p>In <strong>Post #17</strong>, we will move into a major data-analysis concept:</p><blockquote><strong><em>Feature Engineering with Pandas: Creating and Transforming DataFrame Columns</em></strong></blockquote><p>We will learn how to create meaningful new columns from existing data and turn raw variables into features that are more useful for analysis and machine-learning preparation.</p><h3>About This Series</h3><p>This article is part of <strong>Data Analysis with Pandas</strong>, a practical series designed to take you from Pandas fundamentals to real-world data analysis.</p><p>The goal is not simply to memorize functions.</p><p>The goal is to understand:</p><blockquote><strong><em>Which Pandas operation should you use, why you should use it, and when another approach is better.</em></strong></blockquote><p>That mindset becomes increasingly important as datasets become larger and analysis becomes more complex.</p><h3>Author</h3><p><strong>Engr. Md. Monjur Bakth Mazumder</strong></p><p><strong>GitHub:</strong><br><a href="https://github.com/monjurmbm404/Python-Data-Science-Tools">https://github.com/monjurmbm404/Python-Data-Science-Tools</a></p><h3>Final Thought</h3><p>Good Pandas code is not about using the most powerful function available.</p><p>It is about choosing the <strong>right level of abstraction for the problem</strong>.</p><p>If a simple vectorized expression solves the problem, use it.</p><p>If a dedicated Pandas method exists, use it.</p><p>If the logic is genuinely custom, apply() can be an excellent tool.</p><p>That distinction is what turns Pandas from a collection of functions into a practical data-analysis framework.</p><h3>Connect &amp; Follow</h3><h4>Professional Profiles</h4><ul><li>Portfolio: <a href="https://monjurmbm404.github.io/?source=user_about----------------------d31e4eba6166------------------------">https://monjurmbm404.github.io</a></li><li>GitHub: <a href="https://github.com/monjurmbm404?source=user_about----------------------d31e4eba6166------------------------">https://github.com/monjurmbm404</a></li><li>LinkedIn: <a href="https://www.linkedin.com/in/monjurmbm404?source=user_about----------------------d31e4eba6166------------------------">https://www.linkedin.com/in/monjurmbm404</a></li><li>Medium: <a href="https://medium.com/@monjurmbm404?source=user_about----------------------d31e4eba6166------------------------">https://medium.com/@monjurmbm404</a></li></ul><h4>Books &amp; Publications</h4><ul><li>Amazon: <a href="https://www.amazon.com/author/monjurmbm404?source=user_about----------------------d31e4eba6166------------------------">https://www.amazon.com/author/monjurmbm404</a></li></ul><h4>Competitive Programming</h4><ul><li>Codeforces: <a href="https://codeforces.com/profile/monjurmbm404?source=user_about----------------------d31e4eba6166------------------------">https://codeforces.com/profile/monjurmbm404</a></li><li>CodeChef: <a href="https://www.codechef.com/users/monjurmbm404?source=user_about----------------------d31e4eba6166------------------------">https://www.codechef.com/users/monjurmbm404</a></li><li>LeetCode: <a href="https://leetcode.com/u/monjurmbm404/?source=user_about----------------------d31e4eba6166------------------------">https://leetcode.com/u/monjurmbm404/</a></li><li>VJudge: <a href="https://vjudge.net/user/monjurmbm404?source=user_about----------------------d31e4eba6166------------------------">https://vjudge.net/user/monjurmbm404</a></li></ul><h4>Social Media</h4><ul><li>YouTube: <a href="https://youtube.com/@monjurmbm404?source=user_about----------------------d31e4eba6166------------------------">https://youtube.com/@monjurmbm404</a></li><li>X: <a href="https://x.com/monjurmbm404?source=user_about----------------------d31e4eba6166------------------------">https://x.com/monjurmbm404</a></li><li>Facebook: <a href="https://www.facebook.com/monjurmbm404?source=user_about----------------------d31e4eba6166------------------------">https://www.facebook.com/monjurmbm404</a></li></ul><h4>Connect with Me</h4><ul><li>Email<strong>:</strong> <a href="/service/mailto:monjurmbm404@gmail.com">monjurmbm404@gmail.com</a></li></ul><p>— <strong>Engr. Md Monjur Bakth Mazumder<br></strong>Software Engineer | Technical Author | Competitive Programmer | AI/ML Enthusiast</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=9a8eaa525592" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Pandas Sorting and Ranking: sort_values(), sort_index() and rank()]]></title>
            <link>https://medium.com/@monjurmbm404/pandas-sorting-and-ranking-sort-values-sort-index-and-rank-a65b1357a705?source=rss-d31e4eba6166------2</link>
            <guid isPermaLink="false">https://medium.com/p/a65b1357a705</guid>
            <dc:creator><![CDATA[Engr. Md Monjur Bakth Mazumder]]></dc:creator>
            <pubDate>Sun, 08 Nov 2026 14:01:01 GMT</pubDate>
            <atom:updated>2026-11-08T14:01:01.576Z</atom:updated>
            <content:encoded><![CDATA[<blockquote><strong><em>Series:</em></strong><em> Data Analysis with Pandas<br></em><strong><em>Part:</em></strong><em> 15</em></blockquote><blockquote>A Practical Guide to Sorting, Ranking and Finding Top or Bottom Records in Python Pandas</blockquote><p>When working with real-world datasets, simply loading and inspecting data is not enough.</p><p>Sooner or later, you will need to answer questions such as:</p><ul><li>Which employee has the highest salary?</li><li>Which students have the best scores?</li><li>Which products generated the most revenue?</li><li>Which records should appear first?</li><li>How can we sort by more than one column?</li><li>What is the rank of each employee?</li><li>Who belongs to the top 5 or bottom 5?</li></ul><p>Pandas provides several powerful tools for these tasks.</p><p>The most important ones are:</p><pre>sort_values()<br>sort_index()<br>rank()</pre><p>These methods help us organize records, compare observations, identify top and bottom performers, and prepare datasets for further analysis.</p><p>In this article, we will learn how to use them in practical data-analysis workflows.</p><h3>1. Why Sorting and Ranking Matter in Data Analysis</h3><p>Imagine you have a dataset containing employee information:</p><pre>import pandas as pd</pre><pre>df = pd.DataFrame({<br>    &quot;Name&quot;: [&quot;Alice&quot;, &quot;Bob&quot;, &quot;Charlie&quot;, &quot;David&quot;, &quot;Emma&quot;],<br>    &quot;Department&quot;: [&quot;IT&quot;, &quot;HR&quot;, &quot;IT&quot;, &quot;Finance&quot;, &quot;HR&quot;],<br>    &quot;Salary&quot;: [70000, 55000, 85000, 60000, 75000],<br>    &quot;Performance&quot;: [88, 76, 95, 82, 91]<br>})</pre><pre>print(df)</pre><p>Output:</p><pre>Name Department  Salary  Performance<br>0    Alice         IT   70000           88<br>1      Bob         HR   55000           76<br>2  Charlie         IT   85000           95<br>3    David    Finance   60000           82<br>4     Emma         HR   75000           91</pre><p>Suppose we want to answer:</p><blockquote><em>Who has the highest salary?</em></blockquote><p>We can sort the dataset:</p><pre>df.sort_values(&quot;Salary&quot;, ascending=False)</pre><p>Or:</p><blockquote><em>Who has the highest performance score?</em></blockquote><pre>df.sort_values(&quot;Performance&quot;, ascending=False)</pre><p>Or:</p><blockquote><em>What is each employee’s performance rank?</em></blockquote><pre>df[&quot;Performance&quot;].rank(ascending=False)</pre><p>This demonstrates an important distinction:</p><p><strong>Sorting changes the order of records.</strong></p><p><strong>Ranking assigns a relative position to each value.</strong></p><h3>2. Sorting with sort_values()</h3><p>The most commonly used sorting method in Pandas is:</p><pre>sort_values()</pre><p>Basic syntax:</p><pre>df.sort_values(&quot;column_name&quot;)</pre><p>By default, sorting is ascending.</p><p>For example:</p><pre>df.sort_values(&quot;Salary&quot;)</pre><p>This places the lowest salary first.</p><p>Output conceptually:</p><pre>Bob       55000<br>David     60000<br>Alice     70000<br>Emma      75000<br>Charlie   85000</pre><h3>3. Ascending Sorting</h3><p>Ascending means:</p><pre>small → large</pre><p>For numbers:</p><pre>10<br>20<br>30<br>40<br>50</pre><p>For text:</p><pre>Alice<br>Bob<br>Charlie<br>David</pre><p>The following is equivalent to the default behavior:</p><pre>df.sort_values(&quot;Salary&quot;, ascending=True)</pre><p>You can explicitly specify:</p><pre>ascending=True</pre><p>This can make your code easier to understand when working with larger analysis pipelines.</p><h3>4. Descending Sorting</h3><p>Descending means:</p><pre>large → small</pre><p>Use:</p><pre>df.sort_values(&quot;Salary&quot;, ascending=False)</pre><p>Now the highest salary appears first.</p><p>For example:</p><pre>Charlie   85000<br>Emma      75000<br>Alice     70000<br>David     60000<br>Bob       55000</pre><p>This is especially useful when looking for:</p><ul><li>highest salaries</li><li>highest scores</li><li>largest sales</li><li>most expensive products</li><li>highest revenue</li><li>best-performing employees</li></ul><h3>5. Sorting by Multiple Columns</h3><p>Real-world datasets often require more than one sorting rule.</p><p>Suppose we have:</p><pre>df = pd.DataFrame({<br>    &quot;Name&quot;: [&quot;Alice&quot;, &quot;Bob&quot;, &quot;Charlie&quot;, &quot;David&quot;, &quot;Emma&quot;],<br>    &quot;Department&quot;: [&quot;IT&quot;, &quot;HR&quot;, &quot;IT&quot;, &quot;Finance&quot;, &quot;HR&quot;],<br>    &quot;Salary&quot;: [70000, 55000, 85000, 60000, 75000],<br>    &quot;Performance&quot;: [88, 76, 95, 82, 91]<br>})</pre><p>Suppose we want:</p><ol><li>Sort by Department alphabetically.</li><li>Within each department, sort by Salary from highest to lowest.</li></ol><p>We can write:</p><pre>df.sort_values(<br>    by=[&quot;Department&quot;, &quot;Salary&quot;],<br>    ascending=[True, False]<br>)</pre><p>Here:</p><pre>by=[&quot;Department&quot;, &quot;Salary&quot;]</pre><p>defines the sorting priority.</p><p>And:</p><pre>ascending=[True, False]</pre><p>means:</p><ul><li>Department → ascending</li><li>Salary → descending</li></ul><p>This is extremely useful in business datasets.</p><h3>6. Understanding Sorting Priority</h3><p>Consider:</p><pre>df.sort_values(<br>    by=[&quot;Department&quot;, &quot;Performance&quot;],<br>    ascending=[True, False]<br>)</pre><p>Pandas first sorts by:</p><pre>Department</pre><p>Then, if multiple rows belong to the same department, it sorts those rows by:</p><pre>Performance</pre><p>in descending order.</p><p>Think of it as:</p><pre>Primary sorting key<br>        ↓<br>Department</pre><pre>Secondary sorting key<br>        ↓<br>Performance</pre><p>This concept becomes especially useful when analyzing grouped business data.</p><h3>7. Sorting with the Same Direction</h3><p>You can also sort multiple columns in the same direction:</p><pre>df.sort_values(<br>    by=[&quot;Salary&quot;, &quot;Performance&quot;],<br>    ascending=False<br>)</pre><p>This sorts both columns in descending order.</p><p>For clarity, you can also write:</p><pre>df.sort_values(<br>    by=[&quot;Salary&quot;, &quot;Performance&quot;],<br>    ascending=[False, False]<br>)</pre><p>The second version is often easier to read when your sorting rules become more complex.</p><h3>8. Sorting the Index with sort_index()</h3><p>sort_values() sorts based on column values.</p><p>But sometimes you want to sort the DataFrame according to its index.</p><p>That’s where:</p><pre>sort_index()</pre><p>is useful.</p><p>Example:</p><pre>df = pd.DataFrame(<br>    {<br>        &quot;Name&quot;: [&quot;Alice&quot;, &quot;Bob&quot;, &quot;Charlie&quot;],<br>        &quot;Score&quot;: [88, 76, 95]<br>    },<br>    index=[103, 101, 102]<br>)</pre><pre>print(df)</pre><p>The index is:</p><pre>103<br>101<br>102</pre><p>We can sort it:</p><pre>df.sort_index()</pre><p>Now the index becomes:</p><pre>101<br>102<br>103</pre><p>The associated rows move with their index labels.</p><h3>9. Descending Index Sorting</h3><p>Just like sort_values(), sort_index() supports:</p><pre>ascending=False</pre><p>Example:</p><pre>df.sort_index(ascending=False)</pre><p>Now the largest index label appears first.</p><p>This can be useful when working with:</p><ul><li>custom indexes</li><li>date indexes</li><li>time-series data</li><li>hierarchical indexes</li><li>ordered identifiers</li></ul><h3>10. Sorting a Series</h3><p>Sorting is not limited to DataFrames.</p><p>You can also sort a Series.</p><p>Example:</p><pre>scores = pd.Series(<br>    [85, 72, 94, 68, 90],<br>    index=[&quot;Alice&quot;, &quot;Bob&quot;, &quot;Charlie&quot;, &quot;David&quot;, &quot;Emma&quot;]<br>)</pre><p>Sort by values:</p><pre>scores.sort_values()</pre><p>Descending:</p><pre>scores.sort_values(ascending=False)</pre><p>Sort by index:</p><pre>scores.sort_index()</pre><p>This gives you flexibility when working with both Series and DataFrames.</p><h3>11. Finding the Top Records</h3><p>One of the most common tasks in data analysis is finding the top records.</p><p>For example:</p><blockquote><em>Find the top 3 employees by salary.</em></blockquote><p>We could sort:</p><pre>top_3 = df.sort_values(<br>    &quot;Salary&quot;,<br>    ascending=False<br>).head(3)</pre><pre>print(top_3)</pre><p>The workflow is:</p><pre>Sort descending<br>      ↓<br>Take first N rows<br>      ↓<br>Top N records</pre><p>For example:</p><pre>df.sort_values(&quot;Salary&quot;, ascending=False).head(3)</pre><p>This is a very common Pandas pattern.</p><h3>12. Finding the Bottom Records</h3><p>The same idea works for bottom records.</p><p>For example:</p><pre>bottom_3 = df.sort_values(<br>    &quot;Salary&quot;,<br>    ascending=True<br>).head(3)</pre><p>This returns the three employees with the lowest salaries.</p><p>You can also use:</p><pre>df.nsmallest(3, &quot;Salary&quot;)</pre><p>For the largest values:</p><pre>df.nlargest(3, &quot;Salary&quot;)</pre><p>These methods can be convenient when your goal is specifically to find top or bottom N values.</p><h3>13. nlargest() for Top Records</h3><p>Instead of:</p><pre>df.sort_values(<br>    &quot;Performance&quot;,<br>    ascending=False<br>).head(3)</pre><p>you can write:</p><pre>df.nlargest(3, &quot;Performance&quot;)</pre><p>This directly communicates your intention:</p><blockquote><em>Give me the three largest performance values.</em></blockquote><p>For example:</p><pre>top_students = df.nlargest(<br>    3,<br>    &quot;Performance&quot;<br>)</pre><p>This is concise and readable.</p><h3>14. nsmallest() for Bottom Records</h3><p>Similarly:</p><pre>df.nsmallest(3, &quot;Performance&quot;)</pre><p>returns the three smallest performance values.</p><p>This can be useful for:</p><ul><li>identifying low-performing students</li><li>finding products with low sales</li><li>detecting low-revenue categories</li><li>identifying employees who may need support</li></ul><h3>15. Ranking with rank()</h3><p>Sorting and ranking are related, but they are not the same.</p><p>Suppose:</p><pre>df[&quot;Performance&quot;]</pre><p>contains:</p><pre>88<br>76<br>95<br>82<br>91</pre><p>We can calculate rankings using:</p><pre>df[&quot;Performance&quot;].rank(ascending=False)</pre><p>Conceptually:</p><pre>95 → Rank 1<br>91 → Rank 2<br>88 → Rank 3<br>82 → Rank 4<br>76 → Rank 5</pre><p>The original rows do not need to be reordered.</p><p>Instead, Pandas creates a ranking value for each observation.</p><h3>16. Adding a Rank Column</h3><p>Ranking becomes especially useful when we store the result in a new column.</p><pre>df[&quot;Performance_Rank&quot;] = df[&quot;Performance&quot;].rank(<br>    ascending=False<br>)</pre><p>Now the DataFrame contains the original performance value and its rank.</p><p>You can then inspect:</p><pre>print(df)</pre><p>This allows you to answer:</p><blockquote><em>What is each employee’s position based on performance?</em></blockquote><p>without changing the original row order.</p><h3>17. Sorting vs Ranking</h3><p>This distinction is important.</p><h3>Sorting</h3><pre>df.sort_values(&quot;Performance&quot;, ascending=False)</pre><p>changes the order of the rows in the returned DataFrame.</p><h3>Ranking</h3><pre>df[&quot;Performance&quot;].rank(ascending=False)</pre><p>assigns a rank to each value.</p><p>Think about it this way:</p><pre>Sorting<br>→ Reorder the dataset</pre><pre>Ranking<br>→ Assign a position to each observation</pre><p>Both are useful, but they solve different problems.</p><h3>18. Ranking with Ties</h3><p>Real-world data often contains equal values.</p><p>For example:</p><pre>scores = pd.Series([95, 90, 90, 85])</pre><p>Now two observations have the same score.</p><p>Pandas needs a rule for handling this tie.</p><p>By default:</p><pre>scores.rank(ascending=False)</pre><p>uses the average rank for tied values.</p><p>The two 90 values occupy positions 2 and 3, so both receive:</p><pre>2.5</pre><p>This is called average ranking.</p><h3>19. Different Ranking Methods</h3><p>The rank() method provides several ways to handle ties.</p><p>For example:</p><pre>scores.rank(<br>    ascending=False,<br>    method=&quot;average&quot;<br>)</pre><p>This is the default.</p><p>Other useful methods include:</p><pre>method=&quot;min&quot;<br>method=&quot;max&quot;<br>method=&quot;first&quot;<br>method=&quot;dense&quot;</pre><h3>method=&quot;min&quot;</h3><p>Tied values receive the lowest rank in the group.</p><h3>method=&quot;max&quot;</h3><p>Tied values receive the highest rank in the group.</p><h3>method=&quot;first&quot;</h3><p>Ties are ranked according to their original order.</p><h3>method=&quot;dense&quot;</h3><p>Ties receive the same rank, and the next distinct value gets the next consecutive rank.</p><p>For example, conceptually:</p><pre>Scores:<br>100<br>90<br>90<br>80</pre><p>With dense ranking:</p><pre>100 → 1<br>90  → 2<br>90  → 2<br>80  → 3</pre><p>This can be useful when you want rankings without gaps after ties.</p><h3>20. Ranking in Descending Order</h3><p>For performance-based ranking, higher values usually mean better performance.</p><p>So we normally use:</p><pre>df[&quot;Performance&quot;].rank(<br>    ascending=False<br>)</pre><p>This gives:</p><pre>Highest value → Rank 1<br>Lowest value  → Last rank</pre><p>For metrics where smaller values are better, such as:</p><ul><li>error</li><li>response time</li><li>delivery time</li><li>defect count</li></ul><p>you may instead use:</p><pre>ascending=True</pre><p>The correct direction depends on the meaning of the metric.</p><h3>21. Ranking Salaries</h3><p>Suppose we want to rank employees based on salary:</p><pre>df[&quot;Salary_Rank&quot;] = df[&quot;Salary&quot;].rank(<br>    ascending=False<br>)</pre><p>Now we have a new analytical feature:</p><pre>Salary<br>Salary_Rank</pre><p>This allows us to identify:</p><ul><li>highest-paid employee</li><li>second-highest salary</li><li>lowest-paid employee</li><li>relative salary position</li></ul><p>Ranking can therefore become part of a larger analytical workflow.</p><h3>22. Ranking Within Groups</h3><p>Ranking becomes even more powerful when combined with groupby().</p><p>Suppose we want to rank employees <strong>within their department</strong>.</p><p>We can write:</p><pre>df[&quot;Department_Rank&quot;] = (<br>    df.groupby(&quot;Department&quot;)[&quot;Performance&quot;]<br>      .rank(ascending=False)<br>)</pre><p>Now employees are ranked against others in the same department rather than against the entire company.</p><p>This is an important real-world analysis technique.</p><p>For example:</p><pre>Company-wide ranking<br>vs.<br>Department-level ranking</pre><p>These can answer different business questions.</p><h3>23. Practical Example: Student Performance</h3><p>Let’s create a student dataset:</p><pre>students = pd.DataFrame({<br>    &quot;Name&quot;: [<br>        &quot;Alice&quot;,<br>        &quot;Bob&quot;,<br>        &quot;Charlie&quot;,<br>        &quot;David&quot;,<br>        &quot;Emma&quot;<br>    ],<br>    &quot;Math&quot;: [85, 72, 95, 68, 90],<br>    &quot;Science&quot;: [88, 75, 92, 70, 94]<br>})</pre><p>Suppose we want the average score:</p><pre>students[&quot;Average&quot;] = (<br>    students[&quot;Math&quot;] +<br>    students[&quot;Science&quot;]<br>) / 2</pre><p>Now we can rank students:</p><pre>students[&quot;Rank&quot;] = students[&quot;Average&quot;].rank(<br>    ascending=False,<br>    method=&quot;dense&quot;<br>)</pre><p>Then sort them:</p><pre>students.sort_values(<br>    &quot;Average&quot;,<br>    ascending=False<br>)</pre><p>This gives us a practical student-performance ranking.</p><h3>24. Finding the Top 3 Students</h3><p>Once we have an average score:</p><pre>top_students = students.nlargest(<br>    3,<br>    &quot;Average&quot;<br>)</pre><p>This is usually more readable than manually sorting and slicing when the requirement is simply:</p><blockquote><em>Find the top 3.</em></blockquote><h3>25. Finding the Bottom 3 Students</h3><p>Similarly:</p><pre>bottom_students = students.nsmallest(<br>    3,<br>    &quot;Average&quot;<br>)</pre><p>This can help identify students who may need additional support.</p><p>However, remember that ranking alone does not explain <em>why</em> a student is performing poorly.</p><p>Further analysis may be necessary.</p><h3>26. Practical Example: Sales Analysis</h3><p>Consider a sales dataset:</p><pre>sales = pd.DataFrame({<br>    &quot;Product&quot;: [<br>        &quot;Laptop&quot;,<br>        &quot;Phone&quot;,<br>        &quot;Tablet&quot;,<br>        &quot;Monitor&quot;,<br>        &quot;Keyboard&quot;<br>    ],<br>    &quot;Units_Sold&quot;: [<br>        120,<br>        250,<br>        180,<br>        90,<br>        300<br>    ],<br>    &quot;Revenue&quot;: [<br>        120000,<br>        150000,<br>        90000,<br>        45000,<br>        30000<br>    ]<br>})</pre><p>Find the products with the highest revenue:</p><pre>sales.sort_values(<br>    &quot;Revenue&quot;,<br>    ascending=False<br>)</pre><p>Find the top three:</p><pre>sales.nlargest(<br>    3,<br>    &quot;Revenue&quot;<br>)</pre><p>Rank products by revenue:</p><pre>sales[&quot;Revenue_Rank&quot;] = sales[&quot;Revenue&quot;].rank(<br>    ascending=False<br>)</pre><p>Now we have both:</p><pre>Revenue<br>Revenue Rank</pre><p>This can be useful for business reporting.</p><h3>27. Multiple-Column Sorting in Sales Analysis</h3><p>Suppose two products have similar revenue.</p><p>We may want to sort using:</p><ol><li>Revenue — highest first</li><li>Units sold — highest first</li></ol><p>Use:</p><pre>sales.sort_values(<br>    by=[&quot;Revenue&quot;, &quot;Units_Sold&quot;],<br>    ascending=[False, False]<br>)</pre><p>This creates a clear priority:</p><pre>Primary:<br>Revenue</pre><pre>Secondary:<br>Units Sold</pre><p>This technique is useful whenever one column alone does not provide enough ordering information.</p><h3>28. Sorting with Missing Values</h3><p>Real datasets often contain missing values.</p><p>Pandas allows you to control where missing values appear during sorting.</p><p>For example:</p><pre>df.sort_values(<br>    &quot;Salary&quot;,<br>    ascending=False,<br>    na_position=&quot;last&quot;<br>)</pre><p>This places missing values at the end.</p><p>You can also use:</p><pre>na_position=&quot;first&quot;</pre><p>to place missing values first.</p><p>This is useful when you want missing records to remain visible without allowing them to interfere with the main ordering.</p><h3>29. Sorting Without Losing the Original Data</h3><p>A common beginner question is:</p><blockquote><em>Does </em><em>sort_values() permanently change my DataFrame?</em></blockquote><p>By default, it returns a sorted result without modifying the original DataFrame.</p><p>For example:</p><pre>sorted_df = df.sort_values(<br>    &quot;Salary&quot;,<br>    ascending=False<br>)</pre><p>Now:</p><pre>df</pre><p>still has its original row order.</p><p>The sorted result is stored in:</p><pre>sorted_df</pre><p>This is often the safer approach when you want to preserve the original dataset.</p><h3>30. Sorting with inplace=True</h3><p>Pandas also supports:</p><pre>df.sort_values(<br>    &quot;Salary&quot;,<br>    ascending=False,<br>    inplace=True<br>)</pre><p>This changes the DataFrame itself.</p><p>However, in analytical workflows, explicitly assigning the result is often easier to reason about:</p><pre>sorted_df = df.sort_values(<br>    &quot;Salary&quot;,<br>    ascending=False<br>)</pre><p>This makes the transformation visible and reduces accidental changes to your working dataset.</p><h3>31. Sorting and Resetting the Index</h3><p>After sorting, you may notice that the original index remains attached to the rows.</p><p>For example:</p><pre>sorted_df = df.sort_values(<br>    &quot;Salary&quot;,<br>    ascending=False<br>)</pre><p>You might see:</p><pre>2<br>4<br>0<br>3<br>1</pre><p>That is not an error.</p><p>The index identifies the original rows.</p><p>If you want a fresh sequential index:</p><pre>sorted_df = (<br>    df.sort_values(&quot;Salary&quot;, ascending=False)<br>      .reset_index(drop=True)<br>)</pre><p>Now the index becomes:</p><pre>0<br>1<br>2<br>3<br>4</pre><p>This is useful when the sorted result will be treated as a new ordered dataset.</p><h3>32. A Practical Sorting and Ranking Workflow</h3><p>A useful workflow is:</p><pre>Load Data<br>    ↓<br>Inspect Data<br>    ↓<br>Choose the Relevant Metric<br>    ↓<br>Sort or Rank<br>    ↓<br>Find Top/Bottom Records<br>    ↓<br>Validate Results<br>    ↓<br>Analyze Insights</pre><p>For example:</p><pre>df.info()</pre><pre>df.sort_values(<br>    &quot;Performance&quot;,<br>    ascending=False<br>)</pre><pre>df[&quot;Performance_Rank&quot;] = df[&quot;Performance&quot;].rank(<br>    ascending=False<br>)</pre><pre>top_5 = df.nlargest(<br>    5,<br>    &quot;Performance&quot;<br>)</pre><p>This workflow can be reused across many datasets.</p><h3>33. Common Mistakes</h3><h3>Mistake 1: Forgetting ascending=False</h3><p>If you want the highest values first:</p><pre>df.sort_values(&quot;Score&quot;)</pre><p>will not do that.</p><p>Use:</p><pre>df.sort_values(<br>    &quot;Score&quot;,<br>    ascending=False<br>)</pre><h3>Mistake 2: Confusing Sorting with Ranking</h3><p>This:</p><pre>df.sort_values(&quot;Score&quot;)</pre><p>reorders records.</p><p>This:</p><pre>df[&quot;Score&quot;].rank()</pre><p>assigns ranks.</p><p>They are not interchangeable.</p><h3>Mistake 3: Using the Wrong Sorting Priority</h3><p>If you write:</p><pre>df.sort_values(<br>    by=[&quot;Department&quot;, &quot;Salary&quot;]<br>)</pre><p>Department becomes the primary sorting key.</p><p>Salary only determines order among rows with the same department.</p><p>Understanding this priority is important.</p><h3>Mistake 4: Assuming keep-like behavior for rankings</h3><p>Ranking ties require an explicit decision when business rules matter.</p><p>For example:</p><pre>method=&quot;dense&quot;</pre><p>and:</p><pre>method=&quot;average&quot;</pre><p>produce different results.</p><p>Always choose a ranking method appropriate to the analysis.</p><h3>Mistake 5: Forgetting Missing Values</h3><p>Missing values can affect how sorted results appear.</p><p>When necessary, explicitly control:</p><pre>na_position=&quot;first&quot;</pre><p>or:</p><pre>na_position=&quot;last&quot;</pre><h3>Mistake 6: Assuming the Last Row Means the Latest Record</h3><p>Sorting determines order.</p><p>If you are analyzing dates, make sure the data is actually sorted by the relevant date column before interpreting first/last records as earliest/latest.</p><h3>34. Sorting vs Ranking: The Mental Model</h3><p>A simple mental model is:</p><pre>sort_values()<br>    ↓<br>Organize rows by column values</pre><pre>sort_index()<br>    ↓<br>Organize rows by index labels</pre><pre>rank()<br>    ↓<br>Assign relative positions to values</pre><p>And for top/bottom analysis:</p><pre>nlargest()<br>    ↓<br>Find highest N values</pre><pre>nsmallest()<br>    ↓<br>Find lowest N values</pre><p>These methods solve closely related but different problems.</p><h3>35. Quick Reference</h3><h3>Sort by one column</h3><pre>df.sort_values(&quot;Salary&quot;)</pre><h3>Sort descending</h3><pre>df.sort_values(<br>    &quot;Salary&quot;,<br>    ascending=False<br>)</pre><h3>Sort by multiple columns</h3><pre>df.sort_values(<br>    by=[&quot;Department&quot;, &quot;Salary&quot;],<br>    ascending=[True, False]<br>)</pre><h3>Sort by index</h3><pre>df.sort_index()</pre><h3>Descending index</h3><pre>df.sort_index(<br>    ascending=False<br>)</pre><h3>Rank values</h3><pre>df[&quot;Score&quot;].rank(<br>    ascending=False<br>)</pre><h3>Rank with dense method</h3><pre>df[&quot;Score&quot;].rank(<br>    ascending=False,<br>    method=&quot;dense&quot;<br>)</pre><h3>Top N records</h3><pre>df.nlargest(<br>    5,<br>    &quot;Score&quot;<br>)</pre><h3>Bottom N records</h3><pre>df.nsmallest(<br>    5,<br>    &quot;Score&quot;<br>)</pre><h3>Rank within groups</h3><pre>df[&quot;Rank&quot;] = (<br>    df.groupby(&quot;Department&quot;)[&quot;Score&quot;]<br>      .rank(ascending=False)<br>)</pre><h3>36. Final Takeaways</h3><p>Sorting and ranking are fundamental operations in practical data analysis.</p><p>With Pandas, you can use:</p><pre>sort_values()</pre><p>to organize records according to column values.</p><p>Use:</p><pre>sort_index()</pre><p>when the ordering should be based on index labels.</p><p>Use:</p><pre>rank()</pre><p>when you need to assign relative positions to observations.</p><p>And when you specifically need the highest or lowest records,:</p><pre>nlargest()<br>nsmallest()</pre><p>can make the analysis more direct.</p><p>The key concepts to remember are:</p><ul><li>sort_values() → sort by column values</li><li>sort_index() → sort by index</li><li>ascending=True → smallest to largest</li><li>ascending=False → largest to smallest</li><li>multiple columns → define primary and secondary sorting rules</li><li>rank() → assign relative positions</li><li>nlargest() → find top N records</li><li>nsmallest() → find bottom N records</li><li>ranking methods matter when values are tied</li><li>sorting and ranking should be chosen according to the analytical question</li></ul><p>Sorting is not just about making a DataFrame look organized.</p><p>It helps you discover patterns.</p><p>Ranking is not just about assigning numbers.</p><p>It helps you understand relative performance.</p><p>Together, these operations turn a raw dataset into a more interpretable analytical view.</p><h3>37. Where We Go Next</h3><p>So far, the series has covered:</p><ul><li>Loading datasets</li><li>Inspecting data</li><li>Selecting rows and columns</li><li>loc and iloc</li><li>Filtering</li><li>Missing values</li><li>Filling missing values</li><li>Replacing values</li><li>Duplicate handling</li><li>Data type conversion</li><li>String operations</li><li>Descriptive statistics</li><li>Sorting and ranking</li></ul><p>The next step is to start applying functions to data more flexibly.</p><p>In <strong>Post #16</strong>, we will explore:</p><blockquote><strong><em>Pandas </em></strong><strong><em>apply(): Lambda Functions and Row-wise vs Column-wise Operations</em></strong></blockquote><p>We will see how custom logic can be applied to Series and DataFrames — and also why built-in vectorized operations are often preferable when they are available.</p><h3>About This Series</h3><p>This article is part of <strong>Data Analysis with Pandas</strong>, a practical series covering Pandas from beginner fundamentals to advanced data-analysis workflows.</p><p>The goal is not simply to memorize Pandas syntax.</p><p>The goal is to understand <strong>how and why each operation is used when working with real-world data</strong>.</p><p>If you are building your foundation in Python Data Science, this series will gradually take you from:</p><pre>Pandas Basics<br>    ↓<br>Data Cleaning<br>    ↓<br>Data Transformation<br>    ↓<br>Grouping &amp; Aggregation<br>    ↓<br>Data Reshaping<br>    ↓<br>Time Series<br>    ↓<br>Exploratory Data Analysis<br>    ↓<br>Real-World Data Projects</pre><h3>Author</h3><p><strong>Engr. Md. Monjur Bakth Mazumder</strong></p><p><strong>GitHub:</strong><br><a href="https://github.com/monjurmbm404/Python-Data-Science-Tools">https://github.com/monjurmbm404/Python-Data-Science-Tools</a></p><h3>Final Thought</h3><p>Good data analysis is not only about calculating numbers.</p><p>It is also about asking better questions:</p><blockquote><em>Who is performing best?</em></blockquote><blockquote><em>Which records deserve attention?</em></blockquote><blockquote><em>What is the relative position of each observation?</em></blockquote><blockquote><em>Which values should appear first?</em></blockquote><p>Pandas gives us simple tools to answer these questions efficiently.</p><p>Once you understand sorting, ranking, and top/bottom analysis, you have another essential building block for real-world data analysis with Python.</p><h3>Connect &amp; Follow</h3><h4>Professional Profiles</h4><ul><li>Portfolio: <a href="https://monjurmbm404.github.io/?source=user_about----------------------d31e4eba6166------------------------">https://monjurmbm404.github.io</a></li><li>GitHub: <a href="https://github.com/monjurmbm404?source=user_about----------------------d31e4eba6166------------------------">https://github.com/monjurmbm404</a></li><li>LinkedIn: <a href="https://www.linkedin.com/in/monjurmbm404?source=user_about----------------------d31e4eba6166------------------------">https://www.linkedin.com/in/monjurmbm404</a></li><li>Medium: <a href="https://medium.com/@monjurmbm404?source=user_about----------------------d31e4eba6166------------------------">https://medium.com/@monjurmbm404</a></li></ul><h4>Books &amp; Publications</h4><ul><li>Amazon: <a href="https://www.amazon.com/author/monjurmbm404?source=user_about----------------------d31e4eba6166------------------------">https://www.amazon.com/author/monjurmbm404</a></li></ul><h4>Competitive Programming</h4><ul><li>Codeforces: <a href="https://codeforces.com/profile/monjurmbm404?source=user_about----------------------d31e4eba6166------------------------">https://codeforces.com/profile/monjurmbm404</a></li><li>CodeChef: <a href="https://www.codechef.com/users/monjurmbm404?source=user_about----------------------d31e4eba6166------------------------">https://www.codechef.com/users/monjurmbm404</a></li><li>LeetCode: <a href="https://leetcode.com/u/monjurmbm404/?source=user_about----------------------d31e4eba6166------------------------">https://leetcode.com/u/monjurmbm404/</a></li><li>VJudge: <a href="https://vjudge.net/user/monjurmbm404?source=user_about----------------------d31e4eba6166------------------------">https://vjudge.net/user/monjurmbm404</a></li></ul><h4>Social Media</h4><ul><li>YouTube: <a href="https://youtube.com/@monjurmbm404?source=user_about----------------------d31e4eba6166------------------------">https://youtube.com/@monjurmbm404</a></li><li>X: <a href="https://x.com/monjurmbm404?source=user_about----------------------d31e4eba6166------------------------">https://x.com/monjurmbm404</a></li><li>Facebook: <a href="https://www.facebook.com/monjurmbm404?source=user_about----------------------d31e4eba6166------------------------">https://www.facebook.com/monjurmbm404</a></li></ul><h4>Connect with Me</h4><ul><li>Email<strong>:</strong> <a href="/service/mailto:monjurmbm404@gmail.com">monjurmbm404@gmail.com</a></li></ul><p>— <strong>Engr. Md Monjur Bakth Mazumder<br></strong>Software Engineer | Technical Author | Competitive Programmer | AI/ML Enthusiast</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=a65b1357a705" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Pandas Statistics: mean(), median(), mode(), min(), max(), sum() and std()]]></title>
            <link>https://medium.com/@monjurmbm404/pandas-statistics-mean-median-mode-min-max-sum-and-std-c77cd549fdb2?source=rss-d31e4eba6166------2</link>
            <guid isPermaLink="false">https://medium.com/p/c77cd549fdb2</guid>
            <dc:creator><![CDATA[Engr. Md Monjur Bakth Mazumder]]></dc:creator>
            <pubDate>Sat, 07 Nov 2026 14:01:02 GMT</pubDate>
            <atom:updated>2026-11-07T14:01:02.296Z</atom:updated>
            <content:encoded><![CDATA[<blockquote><strong><em>Series:</em></strong><em> Data Analysis with Pandas<br></em><strong><em>Part:</em></strong><em> 14</em></blockquote><blockquote><strong><em>A Practical Guide to Descriptive Statistics, Column-Level Analysis and Numerical Dataset Summary</em></strong></blockquote><p>After loading and cleaning a dataset, one of the next questions is:</p><blockquote><strong><em>What does the data actually tell us?</em></strong></blockquote><p>Before creating visualizations or building advanced analysis workflows, we often need a quick numerical summary.</p><p>For example:</p><ul><li>What is the average salary?</li><li>What is the median age?</li><li>What is the highest score?</li><li>What is the lowest value?</li><li>How many records are there?</li><li>How much does a numeric variable vary?</li><li>Which values appear most frequently?</li></ul><p>Pandas provides several simple statistical methods that make this type of analysis easy.</p><p>In this article, we will work with:</p><pre>mean()<br>median()<br>mode()<br>min()<br>max()<br>sum()<br>std()</pre><p>We will also learn how to perform:</p><ul><li>descriptive statistics</li><li>column-level analysis</li><li>numerical dataset summaries</li><li>practical comparisons between statistical measures</li></ul><h3>1. What Is Descriptive Statistics?</h3><p><strong>Descriptive statistics</strong> is the process of summarizing the important characteristics of a dataset using numerical measures.</p><p>Instead of examining every row individually, we can calculate statistics such as:</p><pre>Mean<br>Median<br>Mode<br>Minimum<br>Maximum<br>Sum<br>Standard Deviation</pre><p>For example, suppose we have these scores:</p><pre>70, 75, 80, 85, 90</pre><p>We can summarize them with:</p><ul><li>average score</li><li>middle score</li><li>highest score</li><li>lowest score</li><li>total score</li><li>variation in scores</li></ul><p>This gives us a quick understanding of the dataset.</p><p>A useful mental model is:</p><pre>Raw Data<br>   ↓<br>Statistical Summary<br>   ↓<br>Understand the Dataset<br>   ↓<br>Analyze<br>   ↓<br>Make Decisions</pre><h3>2. Creating a Sample Dataset</h3><p>Let’s create a practical employee dataset:</p><pre>import pandas as pd</pre><pre>df = pd.DataFrame({<br>    &quot;Employee&quot;: [<br>        &quot;Alice&quot;,<br>        &quot;Bob&quot;,<br>        &quot;Charlie&quot;,<br>        &quot;David&quot;,<br>        &quot;Eva&quot;,<br>        &quot;Frank&quot;<br>    ],<br>    &quot;Age&quot;: [25, 30, 28, 35, 30, 32],<br>    &quot;Salary&quot;: [45000, 60000, 52000, 75000, 62000, 58000],<br>    &quot;Experience&quot;: [2, 5, 3, 10, 6, 7],<br>    &quot;PerformanceScore&quot;: [78, 85, 82, 91, 88, 85]<br>})</pre><pre>print(df)</pre><p>Our numerical columns are:</p><pre>Age<br>Salary<br>Experience<br>PerformanceScore</pre><p>These are the columns we can analyze statistically.</p><h3>3. mean() — Finding the Average</h3><p>The mean is commonly called the <strong>average</strong>.</p><p>For example:</p><pre>df[&quot;Salary&quot;].mean()</pre><p>This calculates the average salary.</p><p>You can also calculate the mean of another column:</p><pre>df[&quot;Age&quot;].mean()</pre><p>or:</p><pre>df[&quot;PerformanceScore&quot;].mean()</pre><p>The basic formula is:</p><pre>Mean = Sum of all values / Number of values</pre><p>For example:</p><pre>10 + 20 + 30 = 60</pre><pre>60 / 3 = 20</pre><p>So the mean is:</p><pre>20</pre><h3>4. Why Mean Is Useful</h3><p>Mean gives us a single number representing the central level of a numerical variable.</p><p>For example:</p><pre>average_salary = df[&quot;Salary&quot;].mean()</pre><pre>print(average_salary)</pre><p>This can answer:</p><blockquote><em>What is the average salary in this dataset?</em></blockquote><p>Similarly:</p><pre>average_score = df[&quot;PerformanceScore&quot;].mean()</pre><p>answers:</p><blockquote><em>What is the average performance score?</em></blockquote><p>Mean is useful for:</p><ul><li>salary analysis</li><li>exam scores</li><li>sales</li><li>revenue</li><li>customer spending</li><li>measurements</li><li>performance metrics</li></ul><p>However, mean can be strongly affected by extreme values.</p><p>We will compare it with median shortly.</p><h3>5. median() — Finding the Middle Value</h3><p>The median is the middle value when data is ordered.</p><p>Example:</p><pre>10, 20, 30, 40, 50</pre><p>The median is:</p><pre>30</pre><p>In Pandas:</p><pre>df[&quot;Salary&quot;].median()</pre><p>You can also calculate:</p><pre>df[&quot;Age&quot;].median()</pre><p>or:</p><pre>df[&quot;PerformanceScore&quot;].median()</pre><h3>6. Mean vs Median</h3><p>Mean and median both describe the center of the data, but they behave differently.</p><p>Consider:</p><pre>20, 25, 30, 35, 1000</pre><p>The value 1000 is an extreme value.</p><p>The mean becomes heavily influenced by it.</p><p>The median remains:</p><pre>30</pre><p>This is why median is often useful when a dataset contains:</p><ul><li>extreme values</li><li>outliers</li><li>highly skewed distributions</li></ul><p>For example, income data is often skewed because a small number of people may earn significantly more than the majority.</p><p>In such cases, median income can sometimes provide a more representative view of the typical observation than mean income.</p><h3>7. mode() — Finding the Most Frequent Value</h3><p>The mode is the value that occurs most frequently.</p><p>Example:</p><pre>80, 85, 85, 90, 92</pre><p>The mode is:</p><pre>85</pre><p>In Pandas:</p><pre>df[&quot;PerformanceScore&quot;].mode()</pre><p>Notice that Pandas returns a <strong>Series</strong>, because a dataset can have more than one mode.</p><p>For example:</p><pre>scores = pd.Series([70, 70, 80, 80, 90])</pre><pre>print(scores.mode())</pre><p>Both 70 and 80 occur twice.</p><p>Therefore, both can be returned as modes.</p><p>If you expect one result but want to safely retrieve the first mode:</p><pre>mode_value = df[&quot;PerformanceScore&quot;].mode().iloc[0]</pre><p>provided the Series is not empty.</p><h3>8. When Is Mode Useful?</h3><p>Mode is particularly useful for categorical or discrete data.</p><p>For example:</p><pre>df = pd.DataFrame({<br>    &quot;Department&quot;: [<br>        &quot;IT&quot;,<br>        &quot;HR&quot;,<br>        &quot;IT&quot;,<br>        &quot;Finance&quot;,<br>        &quot;IT&quot;,<br>        &quot;HR&quot;<br>    ]<br>})</pre><p>You can find the most frequent department:</p><pre>df[&quot;Department&quot;].mode()</pre><p>The result is:</p><pre>IT</pre><p>Mode can therefore be useful for:</p><ul><li>most common category</li><li>most frequent score</li><li>most common product</li><li>most common response</li><li>most common value</li></ul><p>Unlike mean and median, mode can be used naturally with categorical data.</p><h3>9. min() — Finding the Minimum</h3><p>The min() method returns the smallest value.</p><p>For example:</p><pre>df[&quot;Salary&quot;].min()</pre><p>This tells us the lowest salary.</p><p>Similarly:</p><pre>df[&quot;Age&quot;].min()</pre><p>returns the youngest age.</p><p>And:</p><pre>df[&quot;PerformanceScore&quot;].min()</pre><p>returns the lowest performance score.</p><p>Minimum is useful for understanding the lower boundary of a numerical variable.</p><h3>10. max() — Finding the Maximum</h3><p>The max() method returns the largest value.</p><p>Example:</p><pre>df[&quot;Salary&quot;].max()</pre><p>This gives the highest salary.</p><p>You can also use:</p><pre>df[&quot;Age&quot;].max()</pre><p>or:</p><pre>df[&quot;PerformanceScore&quot;].max()</pre><p>Maximum helps us understand the upper boundary of the data.</p><h3>11. Minimum and Maximum Together</h3><p>Using both methods gives us a quick range.</p><pre>minimum = df[&quot;Salary&quot;].min()<br>maximum = df[&quot;Salary&quot;].max()</pre><pre>print(&quot;Minimum:&quot;, minimum)<br>print(&quot;Maximum:&quot;, maximum)</pre><p>You can also calculate the range directly:</p><pre>salary_range = (<br>    df[&quot;Salary&quot;].max()<br>    - df[&quot;Salary&quot;].min()<br>)</pre><pre>print(salary_range)</pre><p>The range tells us the distance between the smallest and largest value.</p><h3>12. sum() — Calculating the Total</h3><p>The sum() method adds numerical values.</p><p>For example:</p><pre>df[&quot;Salary&quot;].sum()</pre><p>calculates the total salary represented by the rows in the dataset.</p><p>Similarly:</p><pre>df[&quot;PerformanceScore&quot;].sum()</pre><p>calculates the total of all performance scores.</p><p>For business datasets, sum() is especially useful for:</p><ul><li>total sales</li><li>total revenue</li><li>total expenses</li><li>total quantity</li><li>total hours</li><li>total salary</li></ul><p>For example:</p><pre>total_sales = df[&quot;Sales&quot;].sum()</pre><h3>13. std() — Measuring Variation</h3><p>Standard deviation measures how spread out values are around their mean.</p><p>In Pandas:</p><pre>df[&quot;Salary&quot;].std()</pre><p>A smaller standard deviation generally means values are closer to the mean.</p><p>A larger standard deviation generally means values are more spread out.</p><p>For example, compare:</p><pre>Dataset A:<br>48, 49, 50, 51, 52</pre><pre>Dataset B:<br>20, 35, 50, 65, 80</pre><p>Both have the same mean:</p><pre>50</pre><p>But Dataset B has much more variation.</p><p>Standard deviation helps quantify that difference.</p><h3>14. Understanding Standard Deviation Intuitively</h3><p>You do not always need to calculate the formula manually to use standard deviation effectively.</p><p>Think of it as:</p><blockquote><strong><em>How much do the values typically vary around their mean?</em></strong></blockquote><p>For example:</p><pre>df[&quot;PerformanceScore&quot;].std()</pre><p>can help answer:</p><blockquote><em>Are employee performance scores relatively similar, or are they widely spread?</em></blockquote><p>This is useful in:</p><ul><li>performance analysis</li><li>financial analysis</li><li>scientific measurements</li><li>sales analysis</li><li>quality control</li><li>statistical analysis</li></ul><h3>15. Population vs Sample Standard Deviation</h3><p>This is an important statistical detail.</p><p>Pandas’ Series.std() uses:</p><pre>ddof=1</pre><p>by default.</p><p>That corresponds to the <strong>sample standard deviation</strong>.</p><p>For example:</p><pre>df[&quot;Salary&quot;].std()</pre><p>uses the default sample calculation.</p><p>If you specifically want the population standard deviation:</p><pre>df[&quot;Salary&quot;].std(ddof=0)</pre><p>The choice depends on what your data represents.</p><p>If your dataset is considered the complete population of interest, ddof=0 may be appropriate.</p><p>If the data is treated as a sample from a larger population, the default sample standard deviation is commonly used.</p><p>For everyday Pandas analysis, understanding that the default is sample standard deviation is usually enough.</p><h3>16. Column-Level Statistical Analysis</h3><p>One of the most common Pandas workflows is analyzing one numerical column at a time.</p><p>For example:</p><pre>df[&quot;Salary&quot;].mean()<br>df[&quot;Salary&quot;].median()<br>df[&quot;Salary&quot;].min()<br>df[&quot;Salary&quot;].max()<br>df[&quot;Salary&quot;].sum()<br>df[&quot;Salary&quot;].std()</pre><p>This gives us several perspectives on salary.</p><p>We can do the same for:</p><pre>df[&quot;Age&quot;]</pre><p>or:</p><pre>df[&quot;PerformanceScore&quot;]</pre><p>This is called <strong>column-level analysis</strong>.</p><h3>17. Analyzing Multiple Columns</h3><p>You can also select several numerical columns:</p><pre>numeric_df = df[<br>    [<br>        &quot;Age&quot;,<br>        &quot;Salary&quot;,<br>        &quot;Experience&quot;,<br>        &quot;PerformanceScore&quot;<br>    ]<br>]</pre><p>Then calculate:</p><pre>numeric_df.mean()</pre><p>This returns the mean for each selected column.</p><p>Similarly:</p><pre>numeric_df.median()</pre><pre>numeric_df.min()</pre><pre>numeric_df.max()</pre><pre>numeric_df.sum()</pre><pre>numeric_df.std()</pre><p>This is useful when you want a quick statistical summary across multiple numerical variables.</p><h3>18. Using describe()</h3><p>Pandas also provides:</p><pre>describe()</pre><p>for a broader descriptive summary.</p><p>For example:</p><pre>df.describe()</pre><p>For numerical columns, you will typically see statistics such as:</p><pre>count<br>mean<br>std<br>min<br>25%<br>50%<br>75%<br>max</pre><p>This is one of the fastest ways to understand a numerical dataset.</p><p>For example:</p><pre>print(df.describe())</pre><p>can quickly show:</p><ul><li>number of observations</li><li>average</li><li>standard deviation</li><li>minimum</li><li>quartiles</li><li>maximum</li></ul><h3>19. Understanding the describe() Output</h3><p>Suppose:</p><pre>df.describe()</pre><p>returns a summary.</p><p>The key values mean:</p><h3>count</h3><p>Number of non-missing observations.</p><h3>mean</h3><p>Average value.</p><h3>std</h3><p>Standard deviation.</p><h3>min</h3><p>Smallest value.</p><h3>25%</h3><p>First quartile.</p><h3>50%</h3><p>Median.</p><h3>75%</h3><p>Third quartile.</p><h3>max</h3><p>Largest value.</p><p>The 50% value is the median.</p><p>So describe() gives you several important statistics in one operation.</p><h3>20. Why describe() Does Not Replace Individual Methods</h3><p>You may wonder:</p><blockquote><em>If </em><em>describe() already gives me statistics, why learn </em><em>mean(), </em><em>median(), </em><em>min() and the others?</em></blockquote><p>Because individual methods give you control.</p><p>For example:</p><pre>average_salary = df[&quot;Salary&quot;].mean()</pre><p>is much more explicit when you need one particular statistic.</p><p>You may also want to perform additional logic:</p><pre>if df[&quot;Salary&quot;].mean() &gt; 60000:<br>    print(&quot;Average salary is above the target.&quot;)</pre><p>Individual methods are therefore important building blocks.</p><p>describe() is a convenient summary tool.</p><h3>21. Working with Missing Values</h3><p>Pandas statistical methods generally skip missing values by default.</p><p>Consider:</p><pre>df = pd.DataFrame({<br>    &quot;Score&quot;: [80, 85, None, 90, 95]<br>})</pre><p>Then:</p><pre>df[&quot;Score&quot;].mean()</pre><p>will calculate the mean using the available numeric values.</p><p>You can check the number of missing values with:</p><pre>df[&quot;Score&quot;].isnull().sum()</pre><p>This is important because a statistical result may be based on fewer observations than the total number of rows.</p><p>For example:</p><pre>df[&quot;Score&quot;].count()</pre><p>tells you how many non-missing values are included.</p><h3>22. A Practical Numerical Summary</h3><p>Let’s build a reusable summary for the salary column:</p><pre>salary_summary = {<br>    &quot;Mean&quot;: df[&quot;Salary&quot;].mean(),<br>    &quot;Median&quot;: df[&quot;Salary&quot;].median(),<br>    &quot;Minimum&quot;: df[&quot;Salary&quot;].min(),<br>    &quot;Maximum&quot;: df[&quot;Salary&quot;].max(),<br>    &quot;Total&quot;: df[&quot;Salary&quot;].sum(),<br>    &quot;Standard Deviation&quot;: df[&quot;Salary&quot;].std()<br>}</pre><pre>print(salary_summary)</pre><p>This creates a compact numerical profile of the column.</p><p>You can use the same pattern for:</p><pre>Age<br>Experience<br>PerformanceScore<br>Sales<br>Revenue</pre><p>or any other appropriate numerical variable.</p><h3>23. Comparing Mean and Median</h3><p>Comparing mean and median can reveal useful information.</p><p>For example:</p><pre>mean_salary = df[&quot;Salary&quot;].mean()<br>median_salary = df[&quot;Salary&quot;].median()</pre><pre>print(&quot;Mean:&quot;, mean_salary)<br>print(&quot;Median:&quot;, median_salary)</pre><p>If mean and median are close:</p><pre>Mean ≈ Median</pre><p>the distribution may be relatively balanced.</p><p>If they differ significantly, the data may be skewed or affected by extreme values.</p><p>This is not a complete test for skewness, but it is a useful first observation.</p><h3>24. Example: Salary Analysis</h3><p>Suppose we have:</p><pre>df = pd.DataFrame({<br>    &quot;Employee&quot;: [<br>        &quot;Alice&quot;,<br>        &quot;Bob&quot;,<br>        &quot;Charlie&quot;,<br>        &quot;David&quot;,<br>        &quot;Eva&quot;,<br>        &quot;Frank&quot;<br>    ],<br>    &quot;Salary&quot;: [<br>        45000,<br>        60000,<br>        52000,<br>        75000,<br>        62000,<br>        58000<br>    ]<br>})</pre><p>Now calculate:</p><pre>mean_salary = df[&quot;Salary&quot;].mean()<br>median_salary = df[&quot;Salary&quot;].median()<br>minimum_salary = df[&quot;Salary&quot;].min()<br>maximum_salary = df[&quot;Salary&quot;].max()<br>total_salary = df[&quot;Salary&quot;].sum()<br>salary_std = df[&quot;Salary&quot;].std()</pre><p>You can print:</p><pre>print(&quot;Mean:&quot;, mean_salary)<br>print(&quot;Median:&quot;, median_salary)<br>print(&quot;Minimum:&quot;, minimum_salary)<br>print(&quot;Maximum:&quot;, maximum_salary)<br>print(&quot;Total:&quot;, total_salary)<br>print(&quot;Standard Deviation:&quot;, salary_std)</pre><p>Now we have a basic statistical profile of employee salaries.</p><h3>25. Example: Student Performance Analysis</h3><p>Statistics are also useful for student datasets.</p><pre>students = pd.DataFrame({<br>    &quot;Student&quot;: [<br>        &quot;A&quot;,<br>        &quot;B&quot;,<br>        &quot;C&quot;,<br>        &quot;D&quot;,<br>        &quot;E&quot;<br>    ],<br>    &quot;Math&quot;: [75, 82, 90, 68, 85],<br>    &quot;English&quot;: [80, 78, 88, 72, 91],<br>    &quot;Science&quot;: [85, 80, 92, 70, 87]<br>})</pre><p>Mean Mathematics score:</p><pre>students[&quot;Math&quot;].mean()</pre><p>Median Mathematics score:</p><pre>students[&quot;Math&quot;].median()</pre><p>Highest Mathematics score:</p><pre>students[&quot;Math&quot;].max()</pre><p>Lowest Mathematics score:</p><pre>students[&quot;Math&quot;].min()</pre><p>Total Mathematics score:</p><pre>students[&quot;Math&quot;].sum()</pre><p>Standard deviation:</p><pre>students[&quot;Math&quot;].std()</pre><p>Full summary:</p><pre>students[&quot;Math&quot;].describe()</pre><p>This gives us a quick understanding of student performance.</p><h3>26. Comparing Multiple Numerical Columns</h3><p>You can calculate the mean of several subjects:</p><pre>students[<br>    [&quot;Math&quot;, &quot;English&quot;, &quot;Science&quot;]<br>].mean()</pre><p>This returns the average for each subject.</p><p>Similarly:</p><pre>students[<br>    [&quot;Math&quot;, &quot;English&quot;, &quot;Science&quot;]<br>].median()</pre><p>or:</p><pre>students[<br>    [&quot;Math&quot;, &quot;English&quot;, &quot;Science&quot;]<br>].std()</pre><p>This is useful when comparing multiple numerical variables.</p><h3>27. Finding the Most Frequent Value</h3><p>For categorical or discrete data, mode() can be particularly useful.</p><p>For example:</p><pre>students = pd.DataFrame({<br>    &quot;Grade&quot;: [<br>        &quot;A&quot;,<br>        &quot;B&quot;,<br>        &quot;A&quot;,<br>        &quot;A&quot;,<br>        &quot;B&quot;,<br>        &quot;C&quot;<br>    ]<br>})</pre><p>Find the most frequent grade:</p><pre>students[&quot;Grade&quot;].mode()</pre><p>This can help answer:</p><blockquote><em>Which grade appears most frequently?</em></blockquote><p>The same idea can be used for:</p><ul><li>most common city</li><li>most common category</li><li>most common product</li><li>most common response</li></ul><h3>28. Statistics by Column Type</h3><p>Not every statistical method makes sense for every column.</p><p>For example:</p><pre>Employee Name<br>Department<br>Salary<br>Age<br>Join Date</pre><p>Mean salary makes sense.</p><p>Mean employee name does not.</p><p>Mean department does not.</p><p>This is why datatype awareness from the previous article is important.</p><p>A good workflow is:</p><pre>Inspect dtype<br>     ↓<br>Identify numerical columns<br>     ↓<br>Apply numerical statistics<br>     ↓<br>Analyze categorical columns separately</pre><h3>29. Numerical Dataset Summary</h3><p>A common data-analysis task is to summarize all numerical columns.</p><p>Suppose:</p><pre>df = pd.DataFrame({<br>    &quot;Age&quot;: [25, 30, 28, 35, 30],<br>    &quot;Salary&quot;: [45000, 60000, 52000, 75000, 62000],<br>    &quot;Experience&quot;: [2, 5, 3, 10, 6]<br>})</pre><p>You can quickly inspect:</p><pre>df.describe()</pre><p>You can also calculate individual summaries:</p><pre>df.mean()</pre><pre>df.median()</pre><pre>df.min()</pre><pre>df.max()</pre><pre>df.std()</pre><p>These operations work column-wise by default.</p><h3>30. Understanding axis</h3><p>Pandas statistical methods often operate column-wise by default.</p><p>For example:</p><pre>df.mean()</pre><p>calculates the mean for each column.</p><p>You can also calculate across columns for each row:</p><pre>df.mean(axis=1)</pre><p>This produces a result for each row.</p><p>For example, in a student dataset:</p><pre>students[<br>    [&quot;Math&quot;, &quot;English&quot;, &quot;Science&quot;]<br>].mean(axis=1)</pre><p>can calculate the average score across subjects for each student.</p><p>This is a useful distinction:</p><pre>axis=0 → calculate down each column<br>axis=1 → calculate across each row</pre><p>Understanding axis becomes increasingly important as you work with Pandas.</p><h3>31. Column-Level vs Row-Level Analysis</h3><p>Consider:</p><pre>students[<br>    [&quot;Math&quot;, &quot;English&quot;, &quot;Science&quot;]<br>]</pre><p>Column-level mean:</p><pre>students[<br>    [&quot;Math&quot;, &quot;English&quot;, &quot;Science&quot;]<br>].mean()</pre><p>This answers:</p><blockquote><em>What is the average score for each subject?</em></blockquote><p>Row-level mean:</p><pre>students[<br>    [&quot;Math&quot;, &quot;English&quot;, &quot;Science&quot;]<br>].mean(axis=1)</pre><p>This answers:</p><blockquote><em>What is the average score for each student?</em></blockquote><p>These are two different analytical questions.</p><h3>32. A Complete Descriptive Statistics Workflow</h3><p>A practical workflow can look like this:</p><pre>Load Dataset<br>      ↓<br>Inspect Columns<br>      ↓<br>Check Data Types<br>      ↓<br>Identify Numerical Columns<br>      ↓<br>Check Missing Values<br>      ↓<br>Calculate Descriptive Statistics<br>      ↓<br>Compare Columns<br>      ↓<br>Identify Patterns / Outliers<br>      ↓<br>Visualize for Deeper Analysis</pre><p>For example:</p><pre>print(df.dtypes)</pre><pre>print(df.isnull().sum())</pre><pre>print(df.describe())</pre><pre>print(df[&quot;Salary&quot;].mean())</pre><pre>print(df[&quot;Salary&quot;].median())</pre><pre>print(df[&quot;Salary&quot;].std())</pre><p>This provides a strong first numerical overview.</p><h3>33. Common Mistakes</h3><h3>Mistake 1 — Using mean without checking the data</h3><p>Mean can be affected by extreme values.</p><p>Always consider:</p><pre>df[&quot;Column&quot;].median()</pre><p>alongside:</p><pre>df[&quot;Column&quot;].mean()</pre><p>when appropriate.</p><h3>Mistake 2 — Treating mode like a single scalar</h3><p>Pandas returns a Series from:</p><pre>df[&quot;Column&quot;].mode()</pre><p>because multiple modes are possible.</p><p>Do not assume there can only be one.</p><h3>Mistake 3 — Ignoring missing values</h3><p>Before interpreting a statistic, check:</p><pre>df[&quot;Column&quot;].isnull().sum()</pre><p>and:</p><pre>df[&quot;Column&quot;].count()</pre><p>You need to know how many valid observations support the result.</p><h3>Mistake 4 — Applying statistics to inappropriate columns</h3><p>Do not calculate averages for columns that represent:</p><ul><li>names</li><li>categories</li><li>identifiers</li><li>arbitrary text</li></ul><p>First understand what the column represents.</p><h3>Mistake 5 — Looking only at the mean</h3><p>A mean alone rarely tells the whole story.</p><p>Consider:</p><pre>Mean<br>Median<br>Minimum<br>Maximum<br>Standard deviation<br>Count</pre><p>together.</p><h3>Mistake 6 — Assuming describe() is complete analysis</h3><p>describe() provides a useful summary, but it does not explain:</p><ul><li>why values differ</li><li>whether outliers are meaningful</li><li>relationships between variables</li><li>trends over time</li><li>business context</li></ul><p>Descriptive statistics are a starting point for analysis, not the entire analysis.</p><h3>34. Statistics as the First Analytical Layer</h3><p>Descriptive statistics are often one of the first analytical steps after data cleaning.</p><p>For example:</p><pre>Raw Data<br>   ↓<br>Cleaning<br>   ↓<br>Datatype Correction<br>   ↓<br>Descriptive Statistics<br>   ↓<br>Visualization<br>   ↓<br>Deeper Analysis</pre><p>Statistics tell you <strong>what the numbers look like</strong>.</p><p>Visualization can then help you understand <strong>how those numbers are distributed and related</strong>.</p><p>This is why statistical analysis naturally connects to the next stages of exploratory data analysis.</p><h3>35. Quick Reference</h3><p>Mean:</p><pre>df[&quot;Column&quot;].mean()</pre><p>Median:</p><pre>df[&quot;Column&quot;].median()</pre><p>Mode:</p><pre>df[&quot;Column&quot;].mode()</pre><p>Minimum:</p><pre>df[&quot;Column&quot;].min()</pre><p>Maximum:</p><pre>df[&quot;Column&quot;].max()</pre><p>Sum:</p><pre>df[&quot;Column&quot;].sum()</pre><p>Standard deviation:</p><pre>df[&quot;Column&quot;].std()</pre><p>Descriptive summary:</p><pre>df[&quot;Column&quot;].describe()</pre><p>Numerical columns summary:</p><pre>df.describe()</pre><p>Column-wise mean:</p><pre>df.mean()</pre><p>Row-wise mean:</p><pre>df.mean(axis=1)</pre><p>Count of non-missing values:</p><pre>df[&quot;Column&quot;].count()</pre><p>Missing values:</p><pre>df[&quot;Column&quot;].isnull().sum()</pre><h3>36. Final Takeaways</h3><p>The most important Pandas statistical methods covered in this article are:</p><pre>mean()<br>median()<br>mode()<br>min()<br>max()<br>sum()<br>std()</pre><p>Each answers a different question.</p><p><strong>Mean</strong> tells you the average.</p><p><strong>Median</strong> tells you the middle value.</p><p><strong>Mode</strong> tells you the most frequent value.</p><p><strong>Min</strong> tells you the smallest value.</p><p><strong>Max</strong> tells you the largest value.</p><p><strong>Sum</strong> tells you the total.</p><p><strong>Standard deviation</strong> tells you how spread out the values are.</p><p>Together, these measures provide a useful numerical summary of a dataset.</p><p>But remember:</p><blockquote><strong><em>A statistical summary is not the same as understanding the data.</em></strong></blockquote><p>You still need to consider:</p><ul><li>data quality</li><li>missing values</li><li>outliers</li><li>data types</li><li>business context</li><li>relationships between variables</li><li>distributions</li></ul><p>A strong analysis workflow is:</p><blockquote><strong><em>Inspect → Clean → Summarize → Compare → Visualize → Interpret</em></strong></blockquote><h3>What Comes Next?</h3><p>We have now learned how to summarize numerical data using descriptive statistics.</p><p>The next step is to understand how to <strong>sort values, rank observations and organize data based on numerical or categorical columns</strong>.</p><p>That brings us to:</p><blockquote><strong><em>Post 15 — Sorting and Ranking Data with Pandas: </em></strong><strong><em>sort_values(), </em></strong><strong><em>sort_index() and </em></strong><strong><em>rank()</em></strong></blockquote><h3>Series Navigation</h3><p><strong>Previous:</strong><br>Post 13 — Pandas String Operations: lower(), upper(), contains(), split() and replace()</p><p><strong>Next:</strong><br>Post 15 — Sorting and Ranking Data with Pandas: sort_values(), sort_index() and rank()</p><p><strong>Complete Series:</strong><br>Data Analysis with Pandas — Beginner to Advanced</p><h3>About the Author</h3><p><strong>Engr. Md. Monjur Bakth Mazumder</strong></p><p><strong>GitHub:</strong> <a href="https://github.com/monjurmbm404/Python-Data-Science-Tools">Python-Data-Science-Tools</a></p><h3>Final Thought</h3><p>Before asking a dataset to tell you a complicated story, start with the basics.</p><p>Calculate the average.</p><p>Find the median.</p><p>Check the minimum and maximum.</p><p>Measure the spread.</p><p>Count the observations.</p><p>These simple statistical operations often reveal important characteristics of your data before you move into visualization or deeper analysis.</p><p><strong>Understand the numbers first. Then investigate the story behind them.</strong></p><h3>Connect &amp; Follow</h3><h4>Professional Profiles</h4><ul><li>Portfolio: <a href="https://monjurmbm404.github.io/?source=user_about----------------------d31e4eba6166------------------------">https://monjurmbm404.github.io</a></li><li>GitHub: <a href="https://github.com/monjurmbm404?source=user_about----------------------d31e4eba6166------------------------">https://github.com/monjurmbm404</a></li><li>LinkedIn: <a href="https://www.linkedin.com/in/monjurmbm404?source=user_about----------------------d31e4eba6166------------------------">https://www.linkedin.com/in/monjurmbm404</a></li><li>Medium: <a href="https://medium.com/@monjurmbm404?source=user_about----------------------d31e4eba6166------------------------">https://medium.com/@monjurmbm404</a></li></ul><h4>Books &amp; Publications</h4><ul><li>Amazon: <a href="https://www.amazon.com/author/monjurmbm404?source=user_about----------------------d31e4eba6166------------------------">https://www.amazon.com/author/monjurmbm404</a></li></ul><h4>Competitive Programming</h4><ul><li>Codeforces: <a href="https://codeforces.com/profile/monjurmbm404?source=user_about----------------------d31e4eba6166------------------------">https://codeforces.com/profile/monjurmbm404</a></li><li>CodeChef: <a href="https://www.codechef.com/users/monjurmbm404?source=user_about----------------------d31e4eba6166------------------------">https://www.codechef.com/users/monjurmbm404</a></li><li>LeetCode: <a href="https://leetcode.com/u/monjurmbm404/?source=user_about----------------------d31e4eba6166------------------------">https://leetcode.com/u/monjurmbm404/</a></li><li>VJudge: <a href="https://vjudge.net/user/monjurmbm404?source=user_about----------------------d31e4eba6166------------------------">https://vjudge.net/user/monjurmbm404</a></li></ul><h4>Social Media</h4><ul><li>YouTube: <a href="https://youtube.com/@monjurmbm404?source=user_about----------------------d31e4eba6166------------------------">https://youtube.com/@monjurmbm404</a></li><li>X: <a href="https://x.com/monjurmbm404?source=user_about----------------------d31e4eba6166------------------------">https://x.com/monjurmbm404</a></li><li>Facebook: <a href="https://www.facebook.com/monjurmbm404?source=user_about----------------------d31e4eba6166------------------------">https://www.facebook.com/monjurmbm404</a></li></ul><h4>Connect with Me</h4><ul><li>Email<strong>:</strong> <a href="/service/mailto:monjurmbm404@gmail.com">monjurmbm404@gmail.com</a></li></ul><p>— <strong>Engr. Md Monjur Bakth Mazumder<br></strong>Software Engineer | Technical Author | Competitive Programmer | AI/ML Enthusiast</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=c77cd549fdb2" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Pandas String Operations: lower(), upper(), contains(), split() and replace()]]></title>
            <link>https://medium.com/@monjurmbm404/pandas-string-operations-lower-upper-contains-split-and-replace-9f55889a68b1?source=rss-d31e4eba6166------2</link>
            <guid isPermaLink="false">https://medium.com/p/9f55889a68b1</guid>
            <dc:creator><![CDATA[Engr. Md Monjur Bakth Mazumder]]></dc:creator>
            <pubDate>Fri, 06 Nov 2026 14:01:03 GMT</pubDate>
            <atom:updated>2026-11-06T14:01:03.200Z</atom:updated>
            <content:encoded><![CDATA[<blockquote><strong><em>Series:</em></strong><em> Data Analysis with Pandas<br></em><strong><em>Part:</em></strong><em> 13</em></blockquote><blockquote><strong><em>A Practical Guide to Cleaning, Searching, Standardizing and Extracting Text with Pandas</em></strong></blockquote><p>Real-world datasets rarely contain perfectly clean text.</p><p>A customer name may contain unnecessary spaces.</p><p>A department may appear as:</p><pre>IT<br>it<br>It<br>I.T.<br>Information Technology</pre><p>An email column may contain inconsistent capitalization.</p><p>A product description may contain useful information hidden inside a longer string.</p><p>For example:</p><pre>&quot;Samsung Galaxy S24 - 256GB&quot;</pre><p>contains information about:</p><ul><li>brand</li><li>product</li><li>storage</li></ul><p>Pandas provides powerful string operations that make this type of data cleaning and transformation much easier.</p><p>In this article, we will focus on five important string methods:</p><pre>str.lower()<br>str.upper()<br>str.contains()<br>str.split()<br>str.replace()</pre><p>We will use them for:</p><ul><li>text cleaning</li><li>searching</li><li>category normalization</li><li>extracting information from strings</li><li>preparing text columns for analysis</li></ul><h3>1. Why String Operations Matter in Pandas</h3><p>Text columns are everywhere in real-world datasets.</p><p>You may have:</p><pre>Name<br>Email<br>Department<br>City<br>Product<br>Address<br>Description<br>Category</pre><p>The problem is that text data is often inconsistent.</p><p>For example:</p><pre>&quot;Dhaka&quot;<br>&quot;dhaka&quot;<br>&quot;DHAKA&quot;<br>&quot; Dhaka &quot;</pre><p>These values may represent the same city.</p><p>If you group them directly:</p><pre>df.groupby(&quot;City&quot;).size()</pre><p>Pandas may treat them as different categories.</p><p>This can produce misleading results.</p><p>String cleaning helps transform inconsistent text into a more standardized form.</p><p>A useful mindset is:</p><blockquote><strong><em>Clean the text before using it for analysis.</em></strong></blockquote><h3>2. Creating a Sample DataFrame</h3><p>Let’s start with a practical dataset:</p><pre>import pandas as pd</pre><pre>df = pd.DataFrame({<br>    &quot;Name&quot;: [<br>        &quot; Alice Smith &quot;,<br>        &quot;BOB JOHNSON&quot;,<br>        &quot;charlie brown&quot;,<br>        &quot;David Wilson&quot;<br>    ],<br>    &quot;Department&quot;: [<br>        &quot;IT&quot;,<br>        &quot;it&quot;,<br>        &quot;Human Resources&quot;,<br>        &quot;HR&quot;<br>    ],<br>    &quot;Email&quot;: [<br>        &quot;alice@example.com&quot;,<br>        &quot;bob@gmail.com&quot;,<br>        &quot;charlie@example.com&quot;,<br>        &quot;david@company.com&quot;<br>    ],<br>    &quot;Product&quot;: [<br>        &quot;Samsung Galaxy S24 - 256GB&quot;,<br>        &quot;Apple iPhone 16 - 128GB&quot;,<br>        &quot;Dell Laptop - 16GB RAM&quot;,<br>        &quot;HP Laptop - 512GB SSD&quot;<br>    ]<br>})</pre><pre>print(df)</pre><p>This dataset contains several common text-cleaning situations.</p><h3>3. Understanding the str Accessor</h3><p>Pandas provides vectorized string operations through the .str accessor.</p><p>For example:</p><pre>df[&quot;Name&quot;].str.lower()</pre><p>The important part is:</p><pre>.str</pre><p>Instead of processing every value with a Python loop, Pandas allows you to apply string operations to an entire Series.</p><p>For example:</p><pre>df[&quot;Name&quot;].str.upper()</pre><p>applies the operation to every value in the Name column.</p><p>This makes string processing concise and convenient.</p><h3>4. str.lower() — Convert Text to Lowercase</h3><p>The first important operation is:</p><pre>str.lower()</pre><p>Example:</p><pre>df[&quot;Name&quot;] = df[&quot;Name&quot;].str.lower()</pre><p>Now:</p><pre>Alice Smith</pre><p>becomes:</p><pre>alice smith</pre><p>Similarly:</p><pre>BOB JOHNSON</pre><p>becomes:</p><pre>bob johnson</pre><p>Lowercase normalization is especially useful for:</p><ul><li>email addresses</li><li>category values</li><li>search operations</li><li>comparisons</li><li>duplicate detection</li></ul><p>For example:</p><pre>df[&quot;Email&quot;] = df[&quot;Email&quot;].str.lower()</pre><p>Now emails have a consistent case.</p><h3>5. str.upper() — Convert Text to Uppercase</h3><p>The opposite operation is:</p><pre>str.upper()</pre><p>Example:</p><pre>df[&quot;Department&quot;] = df[&quot;Department&quot;].str.upper()</pre><p>Values such as:</p><pre>IT<br>it<br>It</pre><p>can all become:</p><pre>IT<br>IT<br>IT</pre><p>This can be useful when a dataset uses uppercase codes or identifiers.</p><p>For example:</p><pre>df[&quot;CountryCode&quot;] = df[&quot;CountryCode&quot;].str.upper()</pre><p>could normalize:</p><pre>bd<br>Bd<br>BD</pre><p>into:</p><pre>BD<br>BD<br>BD</pre><h3>6. str.lower() vs str.upper()</h3><p>Both methods perform normalization.</p><p>Use lowercase when you want:</p><pre>email@example.com<br>dhaka<br>technology</pre><p>Use uppercase when you want:</p><pre>BD<br>USA<br>IT<br>HR</pre><p>The important thing is consistency.</p><p>For example, if you are standardizing a category column, choose one representation and apply it consistently.</p><h3>7. Cleaning Extra Spaces</h3><p>Although not one of the five main methods in this article, whitespace cleaning is an important part of string preprocessing.</p><p>For example:</p><pre>df[&quot;Name&quot;] = df[&quot;Name&quot;].str.strip()</pre><p>This removes unnecessary spaces from the beginning and end.</p><p>Before:</p><pre>&quot; Alice Smith &quot;</pre><p>After:</p><pre>&quot;Alice Smith&quot;</pre><p>A common cleaning pattern is:</p><pre>df[&quot;Name&quot;] = (<br>    df[&quot;Name&quot;]<br>    .str.strip()<br>    .str.lower()<br>)</pre><p>Now the values are both trimmed and normalized.</p><p>String operations can also be chained together.</p><h3>8. str.contains() — Searching Inside Text</h3><p>One of the most useful string methods in Pandas is:</p><pre>str.contains()</pre><p>It allows you to check whether a string contains a particular pattern.</p><p>For example:</p><pre>df[&quot;Email&quot;].str.contains(&quot;gmail&quot;)</pre><p>This returns a Boolean Series:</p><pre>False<br>True<br>False<br>False</pre><p>You can use that Boolean result to filter rows:</p><pre>df[df[&quot;Email&quot;].str.contains(&quot;gmail&quot;)]</pre><p>Now you can find users whose email address contains &quot;gmail&quot;.</p><h3>9. Searching for Text Case-Insensitively</h3><p>Suppose your dataset contains:</p><pre>Python<br>python<br>PYTHON<br>PyThOn</pre><p>Searching with:</p><pre>df[&quot;Skill&quot;].str.contains(&quot;python&quot;)</pre><p>may not match all variations because string matching is case-sensitive by default.</p><p>You can use:</p><pre>df[&quot;Skill&quot;].str.contains(<br>    &quot;python&quot;,<br>    case=False,<br>    na=False<br>)</pre><p>Now different capitalization patterns can be matched.</p><p>The na=False option is also useful when the column contains missing values.</p><p>It tells Pandas to treat missing values as False for this filtering operation.</p><h3>10. Filtering Rows with str.contains()</h3><p>Consider:</p><pre>df = pd.DataFrame({<br>    &quot;Name&quot;: [<br>        &quot;Alice Smith&quot;,<br>        &quot;Bob Johnson&quot;,<br>        &quot;Charlie Brown&quot;,<br>        &quot;David Wilson&quot;<br>    ],<br>    &quot;Department&quot;: [<br>        &quot;Technology&quot;,<br>        &quot;Finance&quot;,<br>        &quot;Technology&quot;,<br>        &quot;Human Resources&quot;<br>    ]<br>})</pre><p>Find employees from Technology:</p><pre>df[df[&quot;Department&quot;].str.contains(<br>    &quot;technology&quot;,<br>    case=False,<br>    na=False<br>)]</pre><p>This is a practical combination of:</p><pre>String search<br>      ↓<br>Boolean condition<br>      ↓<br>DataFrame filtering</pre><h3>11. Searching for Multiple Patterns</h3><p>Sometimes you want to search for more than one keyword.</p><p>For example:</p><pre>df[&quot;Department&quot;].str.contains(<br>    &quot;IT|Technology&quot;,<br>    case=False,<br>    na=False<br>)</pre><p>The | inside the pattern acts as an OR in regular-expression matching.</p><p>This can find values containing either:</p><pre>IT</pre><p>or:</p><pre>Technology</pre><p>For more advanced pattern matching, str.contains() supports regular expressions by default.</p><p>If you want literal text matching instead, you can use:</p><pre>df[&quot;Column&quot;].str.contains(<br>    &quot;text&quot;,<br>    regex=False,<br>    na=False<br>)</pre><p>This distinction becomes useful when your search text contains special regex characters.</p><h3>12. Handling Missing Values with str.contains()</h3><p>Consider:</p><pre>df = pd.DataFrame({<br>    &quot;Email&quot;: [<br>        &quot;alice@gmail.com&quot;,<br>        None,<br>        &quot;bob@example.com&quot;<br>    ]<br>})</pre><p>Using:</p><pre>df[&quot;Email&quot;].str.contains(&quot;gmail&quot;)</pre><p>can produce a missing result for the None value.</p><p>For filtering, it is often safer to write:</p><pre>df[&quot;Email&quot;].str.contains(<br>    &quot;gmail&quot;,<br>    na=False<br>)</pre><p>Now missing values are treated as False.</p><p>This makes the result easier to use as a Boolean filter.</p><h3>13. str.split() — Splitting Strings</h3><p>Another powerful operation is:</p><pre>str.split()</pre><p>It splits a string into separate pieces.</p><p>For example:</p><pre>df[&quot;Name&quot;].str.split()</pre><p>For:</p><pre>Alice Smith</pre><p>the result is conceptually:</p><pre>[&quot;Alice&quot;, &quot;Smith&quot;]</pre><p>This is useful when a single text field contains multiple pieces of structured information.</p><h3>14. Extracting First and Last Names</h3><p>Suppose:</p><pre>df = pd.DataFrame({<br>    &quot;FullName&quot;: [<br>        &quot;Alice Smith&quot;,<br>        &quot;Bob Johnson&quot;,<br>        &quot;Charlie Brown&quot;<br>    ]<br>})</pre><p>You can split the names:</p><pre>df[&quot;FullName&quot;].str.split()</pre><p>To extract the first name:</p><pre>df[&quot;FirstName&quot;] = df[&quot;FullName&quot;].str.split().str[0]</pre><p>Now:</p><pre>Alice<br>Bob<br>Charlie</pre><p>For the last name:</p><pre>df[&quot;LastName&quot;] = df[&quot;FullName&quot;].str.split().str[-1]</pre><p>Now:</p><pre>Smith<br>Johnson<br>Brown</pre><p>This is a simple example of extracting structured information from unstructured text.</p><h3>15. Splitting with a Specific Separator</h3><p>str.split() can also use a specific separator.</p><p>For example:</p><pre>df[&quot;Email&quot;].str.split(&quot;@&quot;)</pre><p>For:</p><pre>alice@example.com</pre><p>the result becomes conceptually:</p><pre>[&quot;alice&quot;, &quot;example.com&quot;]</pre><p>You can extract the username:</p><pre>df[&quot;Username&quot;] = (<br>    df[&quot;Email&quot;]<br>    .str.split(&quot;@&quot;)<br>    .str[0]<br>)</pre><p>And the domain:</p><pre>df[&quot;Domain&quot;] = (<br>    df[&quot;Email&quot;]<br>    .str.split(&quot;@&quot;)<br>    .str[1]<br>)</pre><p>This is a useful example of extracting information from strings.</p><h3>16. Splitting Product Information</h3><p>Consider:</p><pre>df = pd.DataFrame({<br>    &quot;Product&quot;: [<br>        &quot;Samsung Galaxy S24 - 256GB&quot;,<br>        &quot;Apple iPhone 16 - 128GB&quot;,<br>        &quot;Dell Laptop - 16GB RAM&quot;<br>    ]<br>})</pre><p>You can split the product name and specification:</p><pre>df[&quot;Product&quot;].str.split(&quot; - &quot;)</pre><p>The result separates:</p><pre>Product Name</pre><p>from:</p><pre>Specification</pre><p>You can extract them:</p><pre>df[&quot;ProductName&quot;] = (<br>    df[&quot;Product&quot;]<br>    .str.split(&quot; - &quot;)<br>    .str[0]<br>)</pre><pre>df[&quot;Specification&quot;] = (<br>    df[&quot;Product&quot;]<br>    .str.split(&quot; - &quot;)<br>    .str[1]<br>)</pre><p>This transforms semi-structured text into separate analytical columns.</p><h3>17. str.replace() — Replacing Text</h3><p>The next important method is:</p><pre>str.replace()</pre><p>It allows you to replace part of a string with another value.</p><p>Example:</p><pre>df[&quot;Department&quot;] = df[&quot;Department&quot;].str.replace(<br>    &quot;IT&quot;,<br>    &quot;Technology&quot;,<br>    regex=False<br>)</pre><p>This changes:</p><pre>IT</pre><p>into:</p><pre>Technology</pre><h3>18. Cleaning Unwanted Characters</h3><p>Suppose a phone-number column contains:</p><pre>+880-1711-123456<br>+880-1811-654321</pre><p>You may want to remove the hyphens.</p><p>Use:</p><pre>df[&quot;Phone&quot;] = df[&quot;Phone&quot;].str.replace(<br>    &quot;-&quot;,<br>    &quot;&quot;,<br>    regex=False<br>)</pre><p>Now:</p><pre>+8801711123456<br>+8801811654321</pre><p>The same technique can be used for:</p><ul><li>commas</li><li>currency symbols</li><li>unnecessary punctuation</li><li>formatting characters</li></ul><h3>19. Category Normalization</h3><p>One of the most important real-world uses of string operations is category normalization.</p><p>Suppose:</p><pre>df = pd.DataFrame({<br>    &quot;Department&quot;: [<br>        &quot;IT&quot;,<br>        &quot;it&quot;,<br>        &quot;It&quot;,<br>        &quot;I.T.&quot;,<br>        &quot;Information Technology&quot;,<br>        &quot;HR&quot;,<br>        &quot;hr&quot;<br>    ]<br>})</pre><p>These values may represent only two actual categories:</p><pre>Technology<br>Human Resources</pre><p>First normalize case:</p><pre>df[&quot;Department&quot;] = df[&quot;Department&quot;].str.lower()</pre><p>Then remove punctuation where appropriate:</p><pre>df[&quot;Department&quot;] = df[&quot;Department&quot;].str.replace(<br>    &quot;.&quot;,<br>    &quot;&quot;,<br>    regex=False<br>)</pre><p>Now values such as:</p><pre>it<br>i.t.</pre><p>can be standardized more easily.</p><p>Then apply explicit business rules:</p><pre>df[&quot;Department&quot;] = df[&quot;Department&quot;].replace({<br>    &quot;it&quot;: &quot;Technology&quot;,<br>    &quot;information technology&quot;: &quot;Technology&quot;,<br>    &quot;hr&quot;: &quot;Human Resources&quot;<br>})</pre><p>This is an important distinction:</p><blockquote><strong><em>String methods help normalize the text; business rules determine what the normalized categories should mean.</em></strong></blockquote><h3>20. A Better Category Cleaning Workflow</h3><p>For category data, a useful workflow is:</p><pre>Raw Categories<br>      ↓<br>Strip whitespace<br>      ↓<br>Normalize case<br>      ↓<br>Remove known formatting differences<br>      ↓<br>Map known variants<br>      ↓<br>Validate unique values</pre><p>Example:</p><pre>df[&quot;Department&quot;] = (<br>    df[&quot;Department&quot;]<br>    .str.strip()<br>    .str.lower()<br>    .str.replace(&quot;.&quot;, &quot;&quot;, regex=False)<br>)</pre><pre>df[&quot;Department&quot;] = df[&quot;Department&quot;].replace({<br>    &quot;it&quot;: &quot;Technology&quot;,<br>    &quot;information technology&quot;: &quot;Technology&quot;,<br>    &quot;hr&quot;: &quot;Human Resources&quot;<br>})</pre><p>Then inspect:</p><pre>print(df[&quot;Department&quot;].unique())</pre><p>This final validation step is important.</p><h3>21. str.replace() for Standardizing Text</h3><p>Suppose city values contain inconsistent formatting:</p><pre>Dhaka<br>dhaka<br>DHAKA<br>Dhaka City</pre><p>You might first normalize case:</p><pre>df[&quot;City&quot;] = df[&quot;City&quot;].str.lower()</pre><p>Then apply a known replacement:</p><pre>df[&quot;City&quot;] = df[&quot;City&quot;].str.replace(<br>    &quot;dhaka city&quot;,<br>    &quot;dhaka&quot;,<br>    regex=False<br>)</pre><p>Now multiple representations can be brought closer to a common category.</p><p>However, avoid blindly replacing text unless you understand the data.</p><p>For example, a word may appear as part of a larger legitimate value.</p><h3>22. Cleaning Email Addresses</h3><p>Email columns are a common place for text normalization.</p><p>Consider:</p><pre>df = pd.DataFrame({<br>    &quot;Email&quot;: [<br>        &quot; Alice@Example.COM &quot;,<br>        &quot;bob@GMAIL.com&quot;,<br>        &quot;CHARLIE@example.com &quot;<br>    ]<br>})</pre><p>A basic cleaning process:</p><pre>df[&quot;Email&quot;] = (<br>    df[&quot;Email&quot;]<br>    .str.strip()<br>    .str.lower()<br>)</pre><p>Now the values become standardized:</p><pre>alice@example.com<br>bob@gmail.com<br>charlie@example.com</pre><p>This can improve:</p><ul><li>duplicate detection</li><li>searching</li><li>grouping</li><li>comparisons</li><li>validation</li></ul><h3>23. Extracting Information from Email Addresses</h3><p>Once the email is normalized:</p><pre>df[&quot;Email&quot;] = (<br>    df[&quot;Email&quot;]<br>    .str.strip()<br>    .str.lower()<br>)</pre><p>you can extract the domain:</p><pre>df[&quot;EmailDomain&quot;] = (<br>    df[&quot;Email&quot;]<br>    .str.split(&quot;@&quot;)<br>    .str[1]<br>)</pre><p>Now:</p><pre>alice@example.com</pre><p>becomes:</p><pre>example.com</pre><p>You can then analyze:</p><pre>df[&quot;EmailDomain&quot;].value_counts()</pre><p>This turns text into structured information that can be analyzed.</p><h3>24. Extracting Numbers from Text</h3><p>Sometimes useful numerical information is embedded inside strings.</p><p>For example:</p><pre>df = pd.DataFrame({<br>    &quot;Product&quot;: [<br>        &quot;Laptop 16GB RAM&quot;,<br>        &quot;Phone 8GB RAM&quot;,<br>        &quot;Tablet 12GB RAM&quot;<br>    ]<br>})</pre><p>You can search for:</p><pre>df[&quot;Product&quot;].str.contains(<br>    &quot;GB&quot;,<br>    case=False,<br>    na=False<br>)</pre><p>For more advanced extraction, Pandas string methods can work with regular expressions.</p><p>For example:</p><pre>df[&quot;RAM&quot;] = (<br>    df[&quot;Product&quot;]<br>    .str.extract(r&quot;(\d+)\s*GB&quot;, expand=False)<br>)</pre><p>This extracts:</p><pre>16<br>8<br>12</pre><p>You can then convert the result to numeric:</p><pre>df[&quot;RAM&quot;] = pd.to_numeric(<br>    df[&quot;RAM&quot;],<br>    errors=&quot;coerce&quot;<br>)</pre><p>This is a powerful pattern:</p><pre>Text<br> ↓<br>Search / Extract<br> ↓<br>Clean<br> ↓<br>Convert<br> ↓<br>Analyze</pre><h3>25. Searching vs Extracting</h3><p>It is useful to understand the difference.</p><h3>Searching</h3><p>Use:</p><pre>str.contains()</pre><p>when you want to know:</p><blockquote><em>Does this text contain a particular pattern?</em></blockquote><p>Example:</p><pre>df[&quot;Description&quot;].str.contains(<br>    &quot;laptop&quot;,<br>    case=False,<br>    na=False<br>)</pre><p>This returns Boolean values.</p><h3>Extracting</h3><p>Use methods such as:</p><pre>str.split()</pre><p>or:</p><pre>str.extract()</pre><p>when you want to retrieve a piece of information from the text.</p><p>For example:</p><pre>df[&quot;Email&quot;].str.split(&quot;@&quot;).str[1]</pre><p>extracts the email domain.</p><p>The distinction is simple:</p><pre>contains() → Find<br>split()    → Separate<br>extract()  → Retrieve</pre><h3>26. Chaining String Operations</h3><p>Pandas allows you to chain multiple string operations.</p><p>For example:</p><pre>df[&quot;Name&quot;] = (<br>    df[&quot;Name&quot;]<br>    .str.strip()<br>    .str.lower()<br>)</pre><p>Another example:</p><pre>df[&quot;Department&quot;] = (<br>    df[&quot;Department&quot;]<br>    .str.strip()<br>    .str.lower()<br>    .str.replace(&quot;.&quot;, &quot;&quot;, regex=False)<br>)</pre><p>This makes a cleaning pipeline easy to read.</p><p>Instead of writing multiple intermediate variables, you can express the transformation as a sequence of steps.</p><h3>27. A Complete Text-Cleaning Example</h3><p>Let’s create a realistic dataset:</p><pre>df = pd.DataFrame({<br>    &quot;Name&quot;: [<br>        &quot; Alice Smith &quot;,<br>        &quot;BOB JOHNSON&quot;,<br>        &quot;charlie brown&quot;,<br>        &quot; David Wilson &quot;<br>    ],<br>    &quot;Department&quot;: [<br>        &quot;IT&quot;,<br>        &quot;it &quot;,<br>        &quot;Human Resources&quot;,<br>        &quot;HR&quot;<br>    ],<br>    &quot;Email&quot;: [<br>        &quot; Alice@Example.COM &quot;,<br>        &quot;bob@gmail.com&quot;,<br>        &quot;CHARLIE@example.com&quot;,<br>        &quot;david@company.com &quot;<br>    ]<br>})</pre><h3>Step 1 — Clean names</h3><pre>df[&quot;Name&quot;] = (<br>    df[&quot;Name&quot;]<br>    .str.strip()<br>    .str.title()<br>)</pre><p>The result becomes consistently formatted names.</p><h3>Step 2 — Normalize departments</h3><pre>df[&quot;Department&quot;] = (<br>    df[&quot;Department&quot;]<br>    .str.strip()<br>    .str.lower()<br>)</pre><p>Then apply business mapping:</p><pre>df[&quot;Department&quot;] = df[&quot;Department&quot;].replace({<br>    &quot;it&quot;: &quot;Technology&quot;,<br>    &quot;hr&quot;: &quot;Human Resources&quot;<br>})</pre><h3>Step 3 — Normalize emails</h3><pre>df[&quot;Email&quot;] = (<br>    df[&quot;Email&quot;]<br>    .str.strip()<br>    .str.lower()<br>)</pre><h3>Step 4 — Extract email domains</h3><pre>df[&quot;EmailDomain&quot;] = (<br>    df[&quot;Email&quot;]<br>    .str.split(&quot;@&quot;)<br>    .str[1]<br>)</pre><h3>Step 5 — Validate</h3><pre>print(df)<br>print(df[&quot;Department&quot;].unique())<br>print(df[&quot;EmailDomain&quot;].value_counts())</pre><p>This is a small but realistic text-cleaning workflow.</p><h3>28. String Operations and Data Quality</h3><p>String operations are not only about making data look cleaner.</p><p>They can directly affect data quality.</p><p>Consider these values:</p><pre>Dhaka<br>dhaka<br> DHAKA<br>Dhaka</pre><p>Without normalization, you may incorrectly count them as separate categories.</p><p>For example:</p><pre>df[&quot;City&quot;].value_counts()</pre><p>could produce separate entries.</p><p>After:</p><pre>df[&quot;City&quot;] = (<br>    df[&quot;City&quot;]<br>    .str.strip()<br>    .str.lower()<br>)</pre><p>they can be grouped consistently.</p><p>This can improve:</p><ul><li>frequency analysis</li><li>grouping</li><li>duplicate detection</li><li>filtering</li><li>reporting</li><li>visualization</li></ul><h3>29. Common Mistakes</h3><h3>Mistake 1 — Forgetting .str</h3><p>This is incorrect:</p><pre>df[&quot;Name&quot;].lower()</pre><p>For a Pandas Series, use:</p><pre>df[&quot;Name&quot;].str.lower()</pre><h3>Mistake 2 — Ignoring whitespace</h3><p>These values are different:</p><pre>&quot;IT&quot;<br>&quot;IT &quot;</pre><p>Use:</p><pre>df[&quot;Department&quot;].str.strip()</pre><p>when leading/trailing whitespace is not meaningful.</p><h3>Mistake 3 — Case-sensitive searching</h3><p>This:</p><pre>df[&quot;Name&quot;].str.contains(&quot;alice&quot;)</pre><p>may not match:</p><pre>Alice<br>ALICE<br>ALIce</pre><p>Use:</p><pre>df[&quot;Name&quot;].str.contains(<br>    &quot;alice&quot;,<br>    case=False,<br>    na=False<br>)</pre><p>when the search should ignore capitalization.</p><h3>Mistake 4 — Ignoring missing values</h3><p>String operations on columns containing missing values can require special handling.</p><p>For filtering:</p><pre>df[&quot;Email&quot;].str.contains(<br>    &quot;gmail&quot;,<br>    na=False<br>)</pre><p>is often useful.</p><h3>Mistake 5 — Blind replacement</h3><p>Do not replace values simply because they look similar.</p><p>For example:</p><pre>df[&quot;City&quot;].str.replace(&quot;York&quot;, &quot;New York&quot;)</pre><p>could unintentionally alter values containing &quot;York&quot; as part of a larger legitimate string.</p><p>Always understand the data and define the intended cleaning rule.</p><h3>Mistake 6 — Normalizing without a business rule</h3><p>Changing:</p><pre>HR<br>Human Resources<br>Human Resource</pre><p>into one category may be correct.</p><p>But whether:</p><pre>Recruitment</pre><p>should also become &quot;Human Resources&quot; depends on the meaning of the dataset.</p><p>Technical normalization and business classification are not always the same thing.</p><h3>30. A Practical String Cleaning Workflow</h3><p>When working with text columns, use a deliberate process:</p><pre>1. Inspect<br>      ↓<br>2. Identify inconsistencies<br>      ↓<br>3. Strip unnecessary whitespace<br>      ↓<br>4. Normalize case<br>      ↓<br>5. Search or filter<br>      ↓<br>6. Replace known variations<br>      ↓<br>7. Extract useful information<br>      ↓<br>8. Validate unique values<br>      ↓<br>9. Continue analysis</pre><p>For example:</p><pre>df[&quot;Category&quot;] = (<br>    df[&quot;Category&quot;]<br>    .str.strip()<br>    .str.lower()<br>)</pre><p>Then:</p><pre>print(df[&quot;Category&quot;].unique())</pre><p>Then apply known business mappings.</p><h3>31. A Reusable String Operations Template</h3><p>Here is a practical template:</p><pre># Lowercase<br>df[&quot;Name&quot;] = df[&quot;Name&quot;].str.lower()</pre><pre># Uppercase<br>df[&quot;Code&quot;] = df[&quot;Code&quot;].str.upper()</pre><pre># Remove surrounding whitespace<br>df[&quot;Name&quot;] = df[&quot;Name&quot;].str.strip()</pre><pre># Search<br>df[df[&quot;Name&quot;].str.contains(<br>    &quot;alice&quot;,<br>    case=False,<br>    na=False<br>)]</pre><pre># Split<br>df[&quot;Username&quot;] = (<br>    df[&quot;Email&quot;]<br>    .str.split(&quot;@&quot;)<br>    .str[0]<br>)</pre><pre># Replace<br>df[&quot;Department&quot;] = (<br>    df[&quot;Department&quot;]<br>    .str.replace(<br>        &quot;I.T.&quot;,<br>        &quot;IT&quot;,<br>        regex=False<br>    )<br>)</pre><p>The exact operations depend on the dataset, but the overall pattern remains similar.</p><h3>32. The Bigger Picture</h3><p>String operations become especially important when working with datasets collected from:</p><ul><li>forms</li><li>websites</li><li>APIs</li><li>spreadsheets</li><li>databases</li><li>customer systems</li><li>surveys</li><li>business applications</li></ul><p>Raw text often contains inconsistencies that are invisible at first glance.</p><p>Pandas gives you tools to turn that messy text into structured, analysis-ready information.</p><p>For example:</p><pre>Raw Email<br>    ↓<br>strip()<br>    ↓<br>lower()<br>    ↓<br>split()<br>    ↓<br>Domain<br>    ↓<br>Analysis</pre><p>Or:</p><pre>Raw Category<br>    ↓<br>strip()<br>    ↓<br>lower()<br>    ↓<br>replace()<br>    ↓<br>Standard Category<br>    ↓<br>groupby()</pre><p>This is where string processing becomes part of real data analysis rather than just text manipulation.</p><h3>33. Quick Reference</h3><p>Convert to lowercase:</p><pre>df[&quot;Column&quot;].str.lower()</pre><p>Convert to uppercase:</p><pre>df[&quot;Column&quot;].str.upper()</pre><p>Remove surrounding whitespace:</p><pre>df[&quot;Column&quot;].str.strip()</pre><p>Search for text:</p><pre>df[&quot;Column&quot;].str.contains(<br>    &quot;keyword&quot;,<br>    case=False,<br>    na=False<br>)</pre><p>Split text:</p><pre>df[&quot;Column&quot;].str.split()</pre><p>Split using a separator:</p><pre>df[&quot;Column&quot;].str.split(&quot;@&quot;)</pre><p>Extract the first piece:</p><pre>df[&quot;Column&quot;].str.split(&quot;@&quot;).str[0]</pre><p>Extract the second piece:</p><pre>df[&quot;Column&quot;].str.split(&quot;@&quot;).str[1]</pre><p>Replace text:</p><pre>df[&quot;Column&quot;].str.replace(<br>    &quot;old&quot;,<br>    &quot;new&quot;,<br>    regex=False<br>)</pre><p>Extract with a regular expression:</p><pre>df[&quot;Column&quot;].str.extract(<br>    r&quot;(\d+)&quot;,<br>    expand=False<br>)</pre><h3>34. Final Takeaways</h3><p>The most important string operations covered in this article are:</p><pre>str.lower()<br>str.upper()<br>str.contains()<br>str.split()<br>str.replace()</pre><p>But the real goal is not memorizing these methods.</p><p>The goal is learning how to use them as part of a data-cleaning workflow.</p><p>Remember:</p><ul><li>Use str.lower() to normalize text to lowercase.</li><li>Use str.upper() for uppercase normalization.</li><li>Use str.contains() to search and filter text.</li><li>Use str.split() to separate structured pieces of information.</li><li>Use str.replace() to correct or standardize known text patterns.</li><li>Use str.strip() to remove unnecessary surrounding whitespace.</li><li>Use case=False when searches should ignore capitalization.</li><li>Use na=False when missing values should not match a text search.</li><li>Always inspect unique categories after normalization.</li><li>Do not blindly replace values without understanding their meaning.</li><li>Separate technical text normalization from business classification rules.</li></ul><p>A strong text-cleaning mindset is:</p><blockquote><strong><em>Inspect → Normalize → Search → Replace → Extract → Validate</em></strong></blockquote><p>Once you become comfortable with these operations, many messy text columns become much easier to work with.</p><h3>What Comes Next?</h3><p>So far in the Pandas data-cleaning section, we have covered:</p><ul><li>Missing data</li><li>Filling missing values</li><li>Replacing incorrect values</li><li>Duplicate data</li><li>Data types</li><li>String cleaning and transformation</li></ul><p>The next step is to perform <strong>statistical analysis</strong> on numerical columns.</p><p>That brings us to:</p><blockquote><strong><em>Post 14 — Pandas Statistics: Mean, Median, Mode, Min, Max, Sum and Standard Deviation</em></strong></blockquote><h3>Series Navigation</h3><p><strong>Previous:</strong><br>Post 12 — Pandas Data Types: astype(), to_numeric() and to_datetime() Explained</p><p><strong>Next:</strong><br>Post 14 — Pandas Statistics: Mean, Median, Mode, Min, Max, Sum and Standard Deviation</p><p><strong>Complete Series:</strong><br>Data Analysis with Pandas — Beginner to Advanced</p><h3>Author</h3><p><strong>Engr. Md. Monjur Bakth Mazumder</strong></p><p><strong>GitHub:</strong> <a href="https://github.com/monjurmbm404/Python-Data-Science-Tools">Python-Data-Science-Tools</a></p><h3>Final Thought</h3><p>Real-world data is rarely clean when you first receive it.</p><p>Names have inconsistent capitalization. Categories contain multiple representations. Emails contain unnecessary spaces. Product descriptions contain useful information hidden inside larger strings.</p><p>Pandas string operations give you a practical way to transform this messy text into consistent, structured and analysis-ready data.</p><p><strong>Clean the text → Extract the information → Validate the result → Analyze the data.</strong></p><h3>Connect &amp; Follow</h3><h4>Professional Profiles</h4><ul><li>Portfolio: <a href="https://monjurmbm404.github.io/?source=user_about----------------------d31e4eba6166------------------------">https://monjurmbm404.github.io</a></li><li>GitHub: <a href="https://github.com/monjurmbm404?source=user_about----------------------d31e4eba6166------------------------">https://github.com/monjurmbm404</a></li><li>LinkedIn: <a href="https://www.linkedin.com/in/monjurmbm404?source=user_about----------------------d31e4eba6166------------------------">https://www.linkedin.com/in/monjurmbm404</a></li><li>Medium: <a href="https://medium.com/@monjurmbm404?source=user_about----------------------d31e4eba6166------------------------">https://medium.com/@monjurmbm404</a></li></ul><h4>Books &amp; Publications</h4><ul><li>Amazon: <a href="https://www.amazon.com/author/monjurmbm404?source=user_about----------------------d31e4eba6166------------------------">https://www.amazon.com/author/monjurmbm404</a></li></ul><h4>Competitive Programming</h4><ul><li>Codeforces: <a href="https://codeforces.com/profile/monjurmbm404?source=user_about----------------------d31e4eba6166------------------------">https://codeforces.com/profile/monjurmbm404</a></li><li>CodeChef: <a href="https://www.codechef.com/users/monjurmbm404?source=user_about----------------------d31e4eba6166------------------------">https://www.codechef.com/users/monjurmbm404</a></li><li>LeetCode: <a href="https://leetcode.com/u/monjurmbm404/?source=user_about----------------------d31e4eba6166------------------------">https://leetcode.com/u/monjurmbm404/</a></li><li>VJudge: <a href="https://vjudge.net/user/monjurmbm404?source=user_about----------------------d31e4eba6166------------------------">https://vjudge.net/user/monjurmbm404</a></li></ul><h4>Social Media</h4><ul><li>YouTube: <a href="https://youtube.com/@monjurmbm404?source=user_about----------------------d31e4eba6166------------------------">https://youtube.com/@monjurmbm404</a></li><li>X: <a href="https://x.com/monjurmbm404?source=user_about----------------------d31e4eba6166------------------------">https://x.com/monjurmbm404</a></li><li>Facebook: <a href="https://www.facebook.com/monjurmbm404?source=user_about----------------------d31e4eba6166------------------------">https://www.facebook.com/monjurmbm404</a></li></ul><h4>Connect with Me</h4><ul><li>Email<strong>:</strong> <a href="/service/mailto:monjurmbm404@gmail.com">monjurmbm404@gmail.com</a></li></ul><p>— <strong>Engr. Md Monjur Bakth Mazumder<br></strong>Software Engineer | Technical Author | Competitive Programmer | AI/ML Enthusiast</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=9f55889a68b1" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Pandas Data Types: astype(), to_numeric() and to_datetime() Explained]]></title>
            <link>https://medium.com/@monjurmbm404/pandas-data-types-astype-to-numeric-and-to-datetime-explained-af79da6ed1d8?source=rss-d31e4eba6166------2</link>
            <guid isPermaLink="false">https://medium.com/p/af79da6ed1d8</guid>
            <dc:creator><![CDATA[Engr. Md Monjur Bakth Mazumder]]></dc:creator>
            <pubDate>Thu, 05 Nov 2026 14:01:03 GMT</pubDate>
            <atom:updated>2026-11-05T14:01:03.099Z</atom:updated>
            <content:encoded><![CDATA[<blockquote><strong><em>Series:</em></strong><em> Data Analysis with Pandas<br></em><strong><em>Part:</em></strong><em> 12</em></blockquote><blockquote><strong><em>A Practical Guide to Inspecting, Converting and Fixing Data Types in Python Pandas</em></strong></blockquote><p>When you load a real-world dataset with Pandas, the data may not always have the correct data types.</p><p>A salary column may contain numbers stored as strings.</p><p>A date column may contain text such as &quot;2026-01-15&quot;.</p><p>A numeric column may contain invalid values such as &quot;N/A&quot; or &quot;Unknown&quot;.</p><p>These problems can affect calculations, filtering, sorting, grouping, and analysis.</p><p>That is why understanding <strong>Pandas data types</strong> is an essential part of data cleaning.</p><p>In this article, we will learn how to inspect and convert data types using:</p><pre>astype()<br>pd.to_numeric()<br>pd.to_datetime()</pre><p>We will also look at common datatype problems and understand why choosing the correct dtype matters in real-world data analysis.</p><h3>1. What Is a Data Type?</h3><p>A data type tells Pandas what kind of values a column contains.</p><p>For example:</p><pre>Name       → text<br>Age        → integer<br>Salary     → decimal number<br>Joined     → date/time<br>Active     → boolean</pre><p>Consider this DataFrame:</p><pre>import pandas as pd</pre><pre>df = pd.DataFrame({<br>    &quot;Name&quot;: [&quot;Alice&quot;, &quot;Bob&quot;, &quot;Charlie&quot;],<br>    &quot;Age&quot;: [25, 30, 28],<br>    &quot;Salary&quot;: [50000.0, 62000.0, 58000.0]<br>})</pre><pre>print(df)</pre><p>You can inspect the data types with:</p><pre>print(df.dtypes)</pre><p>Output:</p><pre>Name       object<br>Age         int64<br>Salary    float64<br>dtype: object</pre><p>The exact dtype names can vary depending on the data and Pandas version, but the important idea is that each column has a type.</p><h3>2. Why Data Types Matter</h3><p>Correct data types are not just a technical detail.</p><p>They directly affect how Pandas interprets and processes your data.</p><p>For example, suppose a salary column contains:</p><pre>df[&quot;Salary&quot;] = [&quot;50000&quot;, &quot;62000&quot;, &quot;58000&quot;]</pre><p>These values look numeric.</p><p>But they are actually strings.</p><p>If you try:</p><pre>df[&quot;Salary&quot;].mean()</pre><p>you may get an error because Pandas cannot calculate a numeric mean from ordinary string values.</p><p>Similarly, a date stored as text cannot always be used correctly for:</p><ul><li>date filtering</li><li>date subtraction</li><li>time differences</li><li>monthly analysis</li><li>yearly analysis</li><li>time-series operations</li></ul><p>So a useful rule is:</p><blockquote><strong><em>Before analyzing a dataset, make sure important columns have appropriate data types.</em></strong></blockquote><h3>3. Inspecting Data Types with dtypes</h3><p>The first step is inspection.</p><p>Use:</p><pre>df.dtypes</pre><p>Example:</p><pre>print(df.dtypes)</pre><p>You can also inspect the DataFrame structure:</p><pre>df.info()</pre><p>For example:</p><pre>&lt;class &#39;pandas.core.frame.DataFrame&#39;&gt;<br>RangeIndex: 3 entries, 0 to 2<br>Data columns (total 3 columns):<br> Name      3 non-null object<br> Age       3 non-null int64<br> Salary    3 non-null float64</pre><p>dtypes gives you a quick view of column types.</p><p>info() provides additional information such as:</p><ul><li>number of rows</li><li>column names</li><li>non-null counts</li><li>data types</li><li>memory usage</li></ul><p>For data cleaning, both are useful.</p><h3>4. Common Pandas Data Types</h3><p>You will commonly encounter types such as:</p><pre>int64<br>float64<br>object<br>bool<br>datetime64[ns]</pre><p>You may also encounter newer or specialized Pandas dtypes such as:</p><pre>string<br>Int64<br>boolean</pre><p>The important point is not memorizing every dtype name.</p><p>Instead, understand what the column represents.</p><p>For example:</p><pre>Age       → integer<br>Salary    → numeric<br>Name      → string<br>Joined    → datetime<br>Active    → boolean</pre><h3>5. A Common Real-World Problem</h3><p>Let’s create a messy DataFrame:</p><pre>df = pd.DataFrame({<br>    &quot;Name&quot;: [&quot;Alice&quot;, &quot;Bob&quot;, &quot;Charlie&quot;],<br>    &quot;Age&quot;: [&quot;25&quot;, &quot;30&quot;, &quot;28&quot;],<br>    &quot;Salary&quot;: [&quot;50000&quot;, &quot;62000&quot;, &quot;58000&quot;],<br>    &quot;JoinDate&quot;: [&quot;2026-01-10&quot;, &quot;2026-02-15&quot;, &quot;2026-03-20&quot;]<br>})</pre><pre>print(df.dtypes)</pre><p>You may see:</p><pre>Name        object<br>Age         object<br>Salary      object<br>JoinDate    object<br>dtype: object</pre><p>Everything except the name is actually represented as text.</p><p>This is a typical data-cleaning problem.</p><p>The values look correct to a human, but their underlying types are not ideal for analysis.</p><h3>6. Converting Data Types with astype()</h3><p>One of the simplest ways to convert a column is:</p><pre>astype()</pre><p>For example:</p><pre>df[&quot;Age&quot;] = df[&quot;Age&quot;].astype(int)</pre><p>Now:</p><pre>print(df[&quot;Age&quot;].dtype)</pre><p>Output:</p><pre>int64</pre><p>Similarly:</p><pre>df[&quot;Salary&quot;] = df[&quot;Salary&quot;].astype(float)</pre><p>Now salary can be treated as a numeric column.</p><h3>7. Converting Strings to Numeric Values</h3><p>Suppose:</p><pre>df[&quot;Salary&quot;] = [&quot;50000&quot;, &quot;62000&quot;, &quot;58000&quot;]</pre><p>These are strings.</p><p>You can convert them using:</p><pre>df[&quot;Salary&quot;] = df[&quot;Salary&quot;].astype(float)</pre><p>or:</p><pre>df[&quot;Salary&quot;] = df[&quot;Salary&quot;].astype(int)</pre><p>depending on the desired representation.</p><p>After conversion:</p><pre>print(df[&quot;Salary&quot;].dtype)</pre><p>You can now perform calculations:</p><pre>print(df[&quot;Salary&quot;].mean())</pre><p>or:</p><pre>print(df[&quot;Salary&quot;].sum())</pre><h3>8. When astype() Is Useful</h3><p>astype() is excellent when you already know the values are valid for the target type.</p><p>For example:</p><pre>df[&quot;Age&quot;] = df[&quot;Age&quot;].astype(int)</pre><p>is appropriate when every value is a valid integer.</p><p>But real-world datasets are often messy.</p><p>Consider:</p><pre>df[&quot;Age&quot;] = [&quot;25&quot;, &quot;30&quot;, &quot;Unknown&quot;]</pre><p>Now:</p><pre>df[&quot;Age&quot;].astype(int)</pre><p>will fail because &quot;Unknown&quot; cannot be converted into an integer.</p><p>This is where pd.to_numeric() becomes especially useful.</p><h3>9. pd.to_numeric()</h3><p>Pandas provides:</p><pre>pd.to_numeric()</pre><p>for converting values into numeric types.</p><p>Example:</p><pre>df[&quot;Age&quot;] = pd.to_numeric(df[&quot;Age&quot;])</pre><p>For clean numeric strings, this works well.</p><p>But you can also control what happens when invalid values are encountered.</p><h3>10. Handling Invalid Numeric Values</h3><p>Consider:</p><pre>df = pd.DataFrame({<br>    &quot;Age&quot;: [&quot;25&quot;, &quot;30&quot;, &quot;Unknown&quot;, &quot;28&quot;]<br>})</pre><p>Trying:</p><pre>df[&quot;Age&quot;] = pd.to_numeric(df[&quot;Age&quot;])</pre><p>will raise an error because &quot;Unknown&quot; is not numeric.</p><p>Instead, use:</p><pre>df[&quot;Age&quot;] = pd.to_numeric(df[&quot;Age&quot;], errors=&quot;coerce&quot;)</pre><p>Now the invalid value becomes missing:</p><pre>25<br>30<br>NaN<br>28</pre><p>This is extremely useful during data cleaning.</p><p>You can then inspect the missing values:</p><pre>print(df[&quot;Age&quot;].isnull())</pre><p>or:</p><pre>print(df[&quot;Age&quot;].isnull().sum())</pre><p>You can later decide whether to:</p><ul><li>fill the missing value</li><li>remove the row</li><li>investigate the original source</li><li>use another cleaning strategy</li></ul><h3>11. errors=&quot;coerce&quot; vs errors=&quot;raise&quot;</h3><p>By default:</p><pre>pd.to_numeric(series)</pre><p>raises an error when a value cannot be converted.</p><p>You can explicitly use:</p><pre>errors=&quot;raise&quot;</pre><p>to preserve that behavior.</p><p>Or:</p><pre>errors=&quot;coerce&quot;</pre><p>to convert invalid values into missing values.</p><p>There is also:</p><pre>errors=&quot;ignore&quot;</pre><p>but for modern data-cleaning workflows, it is generally better to explicitly handle conversion and validation rather than silently leaving problematic values unchanged.</p><p>A good pattern is:</p><pre>df[&quot;Age&quot;] = pd.to_numeric(<br>    df[&quot;Age&quot;],<br>    errors=&quot;coerce&quot;<br>)</pre><p>Then inspect the resulting missing values.</p><h3>12. Cleaning Numeric Strings</h3><p>Real datasets can contain values such as:</p><pre>&quot;50000&quot;<br>&quot;$62000&quot;<br>&quot;58,000&quot;<br>&quot;Unknown&quot;</pre><p>Only some of these can be directly converted.</p><p>For example:</p><pre>df[&quot;Salary&quot;] = df[&quot;Salary&quot;].str.replace(&quot;,&quot;, &quot;&quot;, regex=False)</pre><p>Then:</p><pre>df[&quot;Salary&quot;] = pd.to_numeric(<br>    df[&quot;Salary&quot;],<br>    errors=&quot;coerce&quot;<br>)</pre><p>For currency symbols, you may need additional cleaning:</p><pre>df[&quot;Salary&quot;] = (<br>    df[&quot;Salary&quot;]<br>    .str.replace(&quot;$&quot;, &quot;&quot;, regex=False)<br>    .str.replace(&quot;,&quot;, &quot;&quot;, regex=False)<br>)</pre><pre>df[&quot;Salary&quot;] = pd.to_numeric(<br>    df[&quot;Salary&quot;],<br>    errors=&quot;coerce&quot;<br>)</pre><p>The general workflow is:</p><pre>Raw text<br>   ↓<br>Clean formatting<br>   ↓<br>Convert to numeric<br>   ↓<br>Handle invalid values<br>   ↓<br>Validate</pre><h3>13. Converting Strings to Datetime</h3><p>Dates are another common datatype problem.</p><p>Suppose you have:</p><pre>df = pd.DataFrame({<br>    &quot;JoinDate&quot;: [<br>        &quot;2026-01-10&quot;,<br>        &quot;2026-02-15&quot;,<br>        &quot;2026-03-20&quot;<br>    ]<br>})</pre><p>Initially:</p><pre>print(df[&quot;JoinDate&quot;].dtype)</pre><p>may show:</p><pre>object</pre><p>These values look like dates, but Pandas is currently treating them as text.</p><p>Use:</p><pre>df[&quot;JoinDate&quot;] = pd.to_datetime(df[&quot;JoinDate&quot;])</pre><p>Now:</p><pre>print(df[&quot;JoinDate&quot;].dtype)</pre><p>You should see a datetime dtype such as:</p><pre>datetime64[ns]</pre><h3>14. Why pd.to_datetime() Is Important</h3><p>Once a column is properly converted to datetime, you can perform date-based operations much more naturally.</p><p>For example:</p><pre>df[&quot;Year&quot;] = df[&quot;JoinDate&quot;].dt.year</pre><p>Month:</p><pre>df[&quot;Month&quot;] = df[&quot;JoinDate&quot;].dt.month</pre><p>Day:</p><pre>df[&quot;Day&quot;] = df[&quot;JoinDate&quot;].dt.day</pre><p>Day name:</p><pre>df[&quot;DayName&quot;] = df[&quot;JoinDate&quot;].dt.day_name()</pre><p>You can also filter dates:</p><pre>df[df[&quot;JoinDate&quot;] &gt;= &quot;2026-02-01&quot;]</pre><p>This becomes extremely useful in time-based data analysis.</p><h3>15. Handling Invalid Dates</h3><p>Real-world date columns can contain invalid values:</p><pre>df = pd.DataFrame({<br>    &quot;JoinDate&quot;: [<br>        &quot;2026-01-10&quot;,<br>        &quot;2026-02-15&quot;,<br>        &quot;Not Available&quot;,<br>        &quot;2026-03-20&quot;<br>    ]<br>})</pre><p>Using:</p><pre>df[&quot;JoinDate&quot;] = pd.to_datetime(<br>    df[&quot;JoinDate&quot;],<br>    errors=&quot;coerce&quot;<br>)</pre><p>will convert the invalid date into a missing datetime value.</p><p>You can then inspect it:</p><pre>print(df[&quot;JoinDate&quot;].isnull())</pre><p>or:</p><pre>print(df[&quot;JoinDate&quot;].isnull().sum())</pre><p>This is similar to numeric conversion with pd.to_numeric().</p><h3>16. astype() vs pd.to_numeric() vs pd.to_datetime()</h3><p>These functions solve related but different problems.</p><h3>astype()</h3><p>Use it when you know the data is compatible with the target dtype.</p><pre>df[&quot;Age&quot;] = df[&quot;Age&quot;].astype(int)</pre><h3>pd.to_numeric()</h3><p>Use it when converting values into numeric data, especially when the source data may contain invalid values.</p><pre>df[&quot;Age&quot;] = pd.to_numeric(<br>    df[&quot;Age&quot;],<br>    errors=&quot;coerce&quot;<br>)</pre><h3>pd.to_datetime()</h3><p>Use it when converting values into dates or timestamps.</p><pre>df[&quot;JoinDate&quot;] = pd.to_datetime(<br>    df[&quot;JoinDate&quot;],<br>    errors=&quot;coerce&quot;<br>)</pre><p>A simple mental model is:</p><pre>Known compatible type<br>        ↓<br>    astype()</pre><pre>Possibly messy numeric data<br>        ↓<br>  to_numeric()</pre><pre>Date/time data<br>        ↓<br> to_datetime()</pre><h3>17. Numeric Conversion: A Practical Example</h3><p>Consider this dataset:</p><pre>df = pd.DataFrame({<br>    &quot;Employee&quot;: [&quot;Alice&quot;, &quot;Bob&quot;, &quot;Charlie&quot;, &quot;David&quot;],<br>    &quot;Age&quot;: [&quot;25&quot;, &quot;30&quot;, &quot;Unknown&quot;, &quot;35&quot;],<br>    &quot;Salary&quot;: [&quot;50000&quot;, &quot;62000&quot;, &quot;58000&quot;, &quot;N/A&quot;]<br>})</pre><pre>print(df)</pre><p>First inspect:</p><pre>print(df.dtypes)</pre><p>Convert Age:</p><pre>df[&quot;Age&quot;] = pd.to_numeric(<br>    df[&quot;Age&quot;],<br>    errors=&quot;coerce&quot;<br>)</pre><p>Convert Salary:</p><pre>df[&quot;Salary&quot;] = pd.to_numeric(<br>    df[&quot;Salary&quot;],<br>    errors=&quot;coerce&quot;<br>)</pre><p>Now inspect:</p><pre>print(df.dtypes)</pre><p>The numeric columns can now participate in calculations.</p><p>For example:</p><pre>print(df[&quot;Age&quot;].mean())<br>print(df[&quot;Salary&quot;].mean())</pre><p>The invalid values have become missing values instead of stopping the conversion process.</p><h3>18. Datatype Problems You Will See in Real Projects</h3><p>Real-world datasets frequently contain datatype problems.</p><h3>Problem 1: Numbers stored as strings</h3><p>Example:</p><pre>&quot;100&quot;<br>&quot;200&quot;<br>&quot;300&quot;</pre><p>Solution:</p><pre>df[&quot;Amount&quot;] = pd.to_numeric(<br>    df[&quot;Amount&quot;],<br>    errors=&quot;coerce&quot;<br>)</pre><h3>Problem 2: Dates stored as strings</h3><p>Example:</p><pre>&quot;2026-01-10&quot;<br>&quot;2026-02-15&quot;</pre><p>Solution:</p><pre>df[&quot;Date&quot;] = pd.to_datetime(<br>    df[&quot;Date&quot;],<br>    errors=&quot;coerce&quot;<br>)</pre><h3>Problem 3: Invalid numeric values</h3><p>Example:</p><pre>&quot;50000&quot;<br>&quot;Unknown&quot;<br>&quot;N/A&quot;<br>&quot;62000&quot;</pre><p>Solution:</p><pre>df[&quot;Salary&quot;] = pd.to_numeric(<br>    df[&quot;Salary&quot;],<br>    errors=&quot;coerce&quot;<br>)</pre><p>Then investigate the resulting missing values.</p><h3>Problem 4: Currency formatting</h3><p>Example:</p><pre>&quot;$50,000&quot;<br>&quot;$62,000&quot;<br>&quot;$58,000&quot;</pre><p>Clean the formatting first:</p><pre>df[&quot;Salary&quot;] = (<br>    df[&quot;Salary&quot;]<br>    .str.replace(&quot;$&quot;, &quot;&quot;, regex=False)<br>    .str.replace(&quot;,&quot;, &quot;&quot;, regex=False)<br>)</pre><p>Then convert:</p><pre>df[&quot;Salary&quot;] = pd.to_numeric(<br>    df[&quot;Salary&quot;],<br>    errors=&quot;coerce&quot;<br>)</pre><h3>Problem 5: Mixed data in one column</h3><p>Example:</p><pre>25<br>30<br>&quot;Unknown&quot;<br>35</pre><p>A column containing mixed representations may be inferred as a general object/string-like dtype rather than a clean numeric dtype.</p><p>This can make analysis more difficult.</p><p>The solution is not simply to force a type.</p><p>First understand the invalid values, then clean and convert them appropriately.</p><h3>19. Why Correct Dtypes Improve Data Analysis</h3><p>Correct data types make many operations safer and more predictable.</p><p>For numeric columns:</p><pre>df[&quot;Salary&quot;].mean()</pre><p>For sorting:</p><pre>df.sort_values(&quot;Salary&quot;)</pre><p>For grouping:</p><pre>df.groupby(&quot;Department&quot;)[&quot;Salary&quot;].mean()</pre><p>For dates:</p><pre>df[&quot;JoinDate&quot;].dt.year</pre><p>For date filtering:</p><pre>df[df[&quot;JoinDate&quot;] &gt;= &quot;2026-01-01&quot;]</pre><p>Correct dtypes also help you detect data-quality problems earlier.</p><p>If a salary column is stored as text, that is often a signal that the raw dataset needs cleaning.</p><h3>20. A Subtle Problem: Sorting Strings vs Numbers</h3><p>This is an important example.</p><p>Suppose:</p><pre>df = pd.DataFrame({<br>    &quot;Score&quot;: [&quot;100&quot;, &quot;20&quot;, &quot;5&quot;, &quot;80&quot;]<br>})</pre><p>If Score remains a string column, sorting may behave lexicographically:</p><pre>df.sort_values(&quot;Score&quot;)</pre><p>The result can be surprising because string ordering is different from numeric ordering.</p><p>Convert it first:</p><pre>df[&quot;Score&quot;] = pd.to_numeric(df[&quot;Score&quot;])</pre><p>Then:</p><pre>df.sort_values(&quot;Score&quot;)</pre><p>Now the values are ordered numerically.</p><p>This is one reason datatype correctness matters even when the values visually look fine.</p><h3>21. Datatype Problems Can Affect Filtering</h3><p>Consider:</p><pre>df = pd.DataFrame({<br>    &quot;Age&quot;: [&quot;18&quot;, &quot;25&quot;, &quot;30&quot;, &quot;40&quot;]<br>})</pre><p>If you want:</p><pre>df[df[&quot;Age&quot;] &gt; 20]</pre><p>the comparison may not work as intended because the values are strings.</p><p>Convert first:</p><pre>df[&quot;Age&quot;] = pd.to_numeric(<br>    df[&quot;Age&quot;],<br>    errors=&quot;coerce&quot;<br>)</pre><p>Now:</p><pre>df[df[&quot;Age&quot;] &gt; 20]</pre><p>works as a numeric comparison.</p><h3>22. Datatype Problems Can Affect Date Analysis</h3><p>Suppose:</p><pre>df[&quot;Date&quot;] = [<br>    &quot;2026-01-10&quot;,<br>    &quot;2026-03-15&quot;,<br>    &quot;2026-02-20&quot;<br>]</pre><p>If these values remain strings, date-related operations are more limited.</p><p>Convert them:</p><pre>df[&quot;Date&quot;] = pd.to_datetime(<br>    df[&quot;Date&quot;],<br>    errors=&quot;coerce&quot;<br>)</pre><p>Now you can use:</p><pre>df[&quot;Date&quot;].dt.year</pre><pre>df[&quot;Date&quot;].dt.month</pre><pre>df[&quot;Date&quot;].dt.day</pre><p>and:</p><pre>df.sort_values(&quot;Date&quot;)</pre><p>This creates a much stronger foundation for time-series analysis.</p><h3>23. A Complete Datatype Cleaning Workflow</h3><p>Let’s combine everything.</p><pre>import pandas as pd</pre><pre>df = pd.DataFrame({<br>    &quot;Name&quot;: [&quot;Alice&quot;, &quot;Bob&quot;, &quot;Charlie&quot;, &quot;David&quot;],<br>    &quot;Age&quot;: [&quot;25&quot;, &quot;30&quot;, &quot;Unknown&quot;, &quot;35&quot;],<br>    &quot;Salary&quot;: [&quot;$50,000&quot;, &quot;$62,000&quot;, &quot;N/A&quot;, &quot;$58,000&quot;],<br>    &quot;JoinDate&quot;: [<br>        &quot;2026-01-10&quot;,<br>        &quot;2026-02-15&quot;,<br>        &quot;Invalid Date&quot;,<br>        &quot;2026-03-20&quot;<br>    ]<br>})</pre><h3>Step 1 — Inspect</h3><pre>print(df.dtypes)</pre><h3>Step 2 — Clean salary formatting</h3><pre>df[&quot;Salary&quot;] = (<br>    df[&quot;Salary&quot;]<br>    .str.replace(&quot;$&quot;, &quot;&quot;, regex=False)<br>    .str.replace(&quot;,&quot;, &quot;&quot;, regex=False)<br>)</pre><h3>Step 3 — Convert numeric columns</h3><pre>df[&quot;Age&quot;] = pd.to_numeric(<br>    df[&quot;Age&quot;],<br>    errors=&quot;coerce&quot;<br>)</pre><pre>df[&quot;Salary&quot;] = pd.to_numeric(<br>    df[&quot;Salary&quot;],<br>    errors=&quot;coerce&quot;<br>)</pre><h3>Step 4 — Convert dates</h3><pre>df[&quot;JoinDate&quot;] = pd.to_datetime(<br>    df[&quot;JoinDate&quot;],<br>    errors=&quot;coerce&quot;<br>)</pre><h3>Step 5 — Inspect again</h3><pre>print(df.dtypes)</pre><h3>Step 6 — Check conversion failures</h3><pre>print(df.isnull().sum())</pre><p>This final step is important.</p><p>Conversion is not the end of cleaning.</p><p>You must inspect what became missing during conversion.</p><h3>24. Validate After Conversion</h3><p>A common mistake is to convert data and immediately continue with analysis.</p><p>Instead, validate.</p><p>For numeric columns:</p><pre>print(df[&quot;Age&quot;].dtype)<br>print(df[&quot;Salary&quot;].dtype)</pre><p>For dates:</p><pre>print(df[&quot;JoinDate&quot;].dtype)</pre><p>Check missing values:</p><pre>print(df.isnull().sum())</pre><p>Check the resulting values:</p><pre>print(df.head())</pre><p>A useful workflow is:</p><pre>Inspect<br>   ↓<br>Identify datatype problems<br>   ↓<br>Clean formatting<br>   ↓<br>Convert<br>   ↓<br>Check conversion failures<br>   ↓<br>Validate dtype<br>   ↓<br>Continue analysis</pre><h3>25. Choosing the Right Conversion Strategy</h3><p>There is no single conversion method that should always be used.</p><p>Think about the data first.</p><p>If the values are clean and compatible:</p><pre>astype()</pre><p>can be simple and direct.</p><p>If numeric values may contain invalid entries:</p><pre>pd.to_numeric(<br>    errors=&quot;coerce&quot;<br>)</pre><p>is often safer for a cleaning workflow.</p><p>If values represent dates:</p><pre>pd.to_datetime(<br>    errors=&quot;coerce&quot;<br>)</pre><p>is the appropriate tool.</p><p>The key is not memorizing syntax.</p><p>The key is understanding the data you are working with.</p><h3>26. Common Mistakes</h3><h3>Mistake 1 — Assuming numeric-looking values are numeric</h3><pre>&quot;100&quot;<br>&quot;200&quot;<br>&quot;300&quot;</pre><p>These are still strings.</p><p>Inspect:</p><pre>df.dtypes</pre><p>before assuming.</p><h3>Mistake 2 — Using astype(int) on messy data</h3><p>This can fail:</p><pre>df[&quot;Age&quot;].astype(int)</pre><p>if values contain:</p><pre>&quot;Unknown&quot;<br>&quot;N/A&quot;<br>&quot;&quot;</pre><p>Use a cleaning-aware conversion approach when necessary.</p><h3>Mistake 3 — Treating dates as ordinary strings</h3><p>If a column represents dates, convert it:</p><pre>pd.to_datetime()</pre><p>This enables proper date operations.</p><h3>Mistake 4 — Using errors=&quot;coerce&quot; without checking the result</h3><p>This:</p><pre>pd.to_numeric(<br>    df[&quot;Age&quot;],<br>    errors=&quot;coerce&quot;<br>)</pre><p>is useful, but invalid values become missing.</p><p>Always inspect:</p><pre>df[&quot;Age&quot;].isnull().sum()</pre><p>Otherwise, you may silently lose information.</p><h3>Mistake 5 — Converting without understanding the data</h3><p>A datatype conversion is not automatically a data-cleaning solution.</p><p>Before converting, ask:</p><ul><li>What does this column represent?</li><li>Which values are valid?</li><li>Which values are invalid?</li><li>What should invalid values become?</li><li>Will missing values require further treatment?</li></ul><h3>27. Data Types and Data Cleaning Are Connected</h3><p>Datatype conversion is often part of a larger cleaning process.</p><p>For example:</p><pre>Raw Dataset<br>     ↓<br>Inspect<br>     ↓<br>Detect missing values<br>     ↓<br>Standardize values<br>     ↓<br>Clean formatting<br>     ↓<br>Convert data types<br>     ↓<br>Validate<br>     ↓<br>Analyze</pre><p>This is why datatype handling should not be treated as an isolated technical operation.</p><p>It is part of preparing a dataset for reliable analysis.</p><h3>28. A Practical Reusable Template</h3><p>Here is a simple pattern you can adapt:</p><pre># Inspect<br>print(df.dtypes)<br>print(df.info())</pre><pre># Numeric conversion<br>df[&quot;Age&quot;] = pd.to_numeric(<br>    df[&quot;Age&quot;],<br>    errors=&quot;coerce&quot;<br>)</pre><pre># Datetime conversion<br>df[&quot;Date&quot;] = pd.to_datetime(<br>    df[&quot;Date&quot;],<br>    errors=&quot;coerce&quot;<br>)</pre><pre># Validate<br>print(df.dtypes)<br>print(df.isnull().sum())</pre><p>If formatting must be cleaned first:</p><pre>df[&quot;Salary&quot;] = (<br>    df[&quot;Salary&quot;]<br>    .str.replace(&quot;$&quot;, &quot;&quot;, regex=False)<br>    .str.replace(&quot;,&quot;, &quot;&quot;, regex=False)<br>)</pre><pre>df[&quot;Salary&quot;] = pd.to_numeric(<br>    df[&quot;Salary&quot;],<br>    errors=&quot;coerce&quot;<br>)</pre><h3>29. The Bigger Picture</h3><p>Data types are one of the foundations of Pandas data analysis.</p><p>A typical dataset may contain:</p><pre>Text<br>Numeric values<br>Dates<br>Boolean values<br>Categories<br>Missing values</pre><p>Pandas needs to understand what these values represent so that operations behave appropriately.</p><p>When data types are wrong, problems can appear in:</p><ul><li>calculations</li><li>filtering</li><li>sorting</li><li>grouping</li><li>date analysis</li><li>feature engineering</li><li>visualization</li><li>machine learning preparation</li></ul><p>When data types are correct, your analysis becomes more predictable and easier to validate.</p><h3>30. Quick Reference</h3><p>Inspect types:</p><pre>df.dtypes</pre><p>Inspect DataFrame structure:</p><pre>df.info()</pre><p>Convert known-compatible values:</p><pre>df[&quot;Age&quot;] = df[&quot;Age&quot;].astype(int)</pre><p>Convert numeric data:</p><pre>df[&quot;Age&quot;] = pd.to_numeric(<br>    df[&quot;Age&quot;],<br>    errors=&quot;coerce&quot;<br>)</pre><p>Convert datetime data:</p><pre>df[&quot;Date&quot;] = pd.to_datetime(<br>    df[&quot;Date&quot;],<br>    errors=&quot;coerce&quot;<br>)</pre><p>Check missing values after conversion:</p><pre>df.isnull().sum()</pre><p>Extract date components:</p><pre>df[&quot;Date&quot;].dt.year<br>df[&quot;Date&quot;].dt.month<br>df[&quot;Date&quot;].dt.day</pre><h3>31. Final Takeaways</h3><p>Data types are not just metadata attached to a DataFrame.</p><p>They influence how Pandas interprets and processes your data.</p><p>The most important concepts from this article are:</p><ul><li>Use df.dtypes and df.info() to inspect data types.</li><li>Use astype() when values are already compatible with the target type.</li><li>Use pd.to_numeric() for numeric conversion, especially when invalid values may exist.</li><li>Use pd.to_datetime() for date and time conversion.</li><li>Use errors=&quot;coerce&quot; when you want invalid values converted into missing values for later inspection.</li><li>Clean formatting such as currency symbols and commas before numeric conversion.</li><li>Always validate the result after conversion.</li><li>Never assume that values that look numeric or date-like are already stored correctly.</li><li>Correct data types make filtering, sorting, calculations, grouping and time-based analysis more reliable.</li></ul><p>A useful data-cleaning mindset is:</p><blockquote><strong><em>Inspect first. Clean deliberately. Convert carefully. Validate afterward.</em></strong></blockquote><h3>What Comes Next?</h3><p>We have now covered:</p><ul><li>missing values</li><li>filling missing values</li><li>replacing incorrect values</li><li>duplicate data</li><li>data types and datatype conversion</li></ul><p>The next step is to work with <strong>text data</strong>, where Pandas provides powerful string methods for cleaning, standardizing and extracting information from text columns.</p><p>That brings us to:</p><blockquote><strong><em>Post 13 — Pandas String Operations: </em></strong><strong><em>lower(), </em></strong><strong><em>upper(), </em></strong><strong><em>contains(), </em></strong><strong><em>split() and </em></strong><strong><em>replace()</em></strong></blockquote><h3>Series Navigation</h3><p><strong>Previous:</strong><br>Post 11 — Pandas Duplicate Data: duplicated() and drop_duplicates() Explained</p><p><strong>Next:</strong><br>Post 13 — Pandas String Operations: lower(), upper(), contains(), split() and replace()</p><p><strong>Complete Series:</strong><br>Data Analysis with Pandas — Beginner to Advanced</p><h3>Author</h3><p><strong>Engr. Md. Monjur Bakth Mazumder</strong></p><p><strong>GitHub:</strong> <a href="https://github.com/monjurmbm404/Python-Data-Science-Tools">Python-Data-Science-Tools</a></p><h3>Final Thought</h3><p>Good data analysis does not begin with a chart or a machine learning model.</p><p>It begins with understanding the data.</p><p>And understanding the data includes knowing <strong>what type each value actually is</strong>.</p><p>Once your DataFrame has appropriate data types, many downstream operations become simpler, safer and more meaningful.</p><p><strong>Inspect → Clean → Convert → Validate → Analyze</strong></p><p>That is the foundation of reliable Pandas data analysis.</p><h3>Connect &amp; Follow</h3><h4>Professional Profiles</h4><ul><li>Portfolio: <a href="https://monjurmbm404.github.io/?source=user_about----------------------d31e4eba6166------------------------">https://monjurmbm404.github.io</a></li><li>GitHub: <a href="https://github.com/monjurmbm404?source=user_about----------------------d31e4eba6166------------------------">https://github.com/monjurmbm404</a></li><li>LinkedIn: <a href="https://www.linkedin.com/in/monjurmbm404?source=user_about----------------------d31e4eba6166------------------------">https://www.linkedin.com/in/monjurmbm404</a></li><li>Medium: <a href="https://medium.com/@monjurmbm404?source=user_about----------------------d31e4eba6166------------------------">https://medium.com/@monjurmbm404</a></li></ul><h4>Books &amp; Publications</h4><ul><li>Amazon: <a href="https://www.amazon.com/author/monjurmbm404?source=user_about----------------------d31e4eba6166------------------------">https://www.amazon.com/author/monjurmbm404</a></li></ul><h4>Competitive Programming</h4><ul><li>Codeforces: <a href="https://codeforces.com/profile/monjurmbm404?source=user_about----------------------d31e4eba6166------------------------">https://codeforces.com/profile/monjurmbm404</a></li><li>CodeChef: <a href="https://www.codechef.com/users/monjurmbm404?source=user_about----------------------d31e4eba6166------------------------">https://www.codechef.com/users/monjurmbm404</a></li><li>LeetCode: <a href="https://leetcode.com/u/monjurmbm404/?source=user_about----------------------d31e4eba6166------------------------">https://leetcode.com/u/monjurmbm404/</a></li><li>VJudge: <a href="https://vjudge.net/user/monjurmbm404?source=user_about----------------------d31e4eba6166------------------------">https://vjudge.net/user/monjurmbm404</a></li></ul><h4>Social Media</h4><ul><li>YouTube: <a href="https://youtube.com/@monjurmbm404?source=user_about----------------------d31e4eba6166------------------------">https://youtube.com/@monjurmbm404</a></li><li>X: <a href="https://x.com/monjurmbm404?source=user_about----------------------d31e4eba6166------------------------">https://x.com/monjurmbm404</a></li><li>Facebook: <a href="https://www.facebook.com/monjurmbm404?source=user_about----------------------d31e4eba6166------------------------">https://www.facebook.com/monjurmbm404</a></li></ul><h4>Connect with Me</h4><ul><li>Email<strong>:</strong> <a href="/service/mailto:monjurmbm404@gmail.com">monjurmbm404@gmail.com</a></li></ul><p>— <strong>Engr. Md Monjur Bakth Mazumder<br></strong>Software Engineer | Technical Author | Competitive Programmer | AI/ML Enthusiast</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=af79da6ed1d8" width="1" height="1" alt="">]]></content:encoded>
        </item>
    </channel>
</rss>