<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Ahmed Abdeltawab</title>
    <description>The latest articles on DEV Community by Ahmed Abdeltawab (@ahmedabdeltawab).</description>
    <link>https://dev.to/ahmedabdeltawab</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4069261%2Fffc54d43-6307-4eca-b088-baa393b4f082.png</url>
      <title>DEV Community: Ahmed Abdeltawab</title>
      <link>https://dev.to/ahmedabdeltawab</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ahmedabdeltawab"/>
    <language>en</language>
    <item>
      <title>The head of your CSV is lying: how 9,291 invoice numbers almost vanished</title>
      <dc:creator>Ahmed Abdeltawab</dc:creator>
      <pubDate>Sat, 15 Aug 2026 06:19:41 +0000</pubDate>
      <link>https://dev.to/ahmedabdeltawab/the-head-of-your-csv-is-lying-how-9291-invoice-numbers-almost-vanished-3891</link>
      <guid>https://dev.to/ahmedabdeltawab/the-head-of-your-csv-is-lying-how-9291-invoice-numbers-almost-vanished-3891</guid>
      <description>&lt;p&gt;Real transaction data is never clean — and the worst part is that it &lt;em&gt;looks&lt;/em&gt; clean. This is a short story from a real dataset (UCI Online Retail: 541,909 e-commerce transactions) about the quietest way to destroy data: silent type coercion. All numbers below come verbatim from an executed notebook.&lt;/p&gt;

&lt;h2&gt;
  
  
  The head looks perfect
&lt;/h2&gt;

&lt;p&gt;Peek at the first rows of the file and &lt;code&gt;InvoiceNo&lt;/code&gt; parses as clean integers — 100% parse rate, full confidence. Any type-inference step, mine included, would call it &lt;code&gt;int64&lt;/code&gt; and move on.&lt;/p&gt;

&lt;p&gt;Measure the &lt;em&gt;whole&lt;/em&gt; file instead of the head, and the number drops to ~98%. The other 2%: invoice numbers starting with &lt;strong&gt;"C"&lt;/strong&gt; — which in this dataset marks a &lt;strong&gt;cancellation&lt;/strong&gt;. Coerce the column to numeric and every one of them becomes &lt;code&gt;NaN&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Invoice numbers destroyed by numeric coercion: 9,291

DextraLoaderWarning: load: ambiguous decision(s):
column 'InvoiceNo': ambiguous - float64 at parse_rate=0.98
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An entire class of business events — silently gone. No exception, no crash. That's what makes coercion the quietest bug in data work: the pipeline &lt;em&gt;succeeds&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why those 9,291 rows matter
&lt;/h2&gt;

&lt;p&gt;They are not noise. They are the &lt;strong&gt;returns side of the business&lt;/strong&gt;: cancelled orders worth 8.4% of everything sold. Lose them and every revenue number downstream is quietly wrong.&lt;/p&gt;

&lt;p&gt;One example of what they catch: the dataset's apparent #1 bestseller, "PAPER CRAFT, LITTLE BIRDIE" (168,470 GBP), is a phantom — a single 80,995-unit order entered at 09:15 and fully cancelled at 09:27 the same morning. Only the preserved cancellation rows expose it. The genuine bestseller is a cake stand.&lt;/p&gt;

&lt;h2&gt;
  
  
  The fix: identifiers are labels, not quantities
&lt;/h2&gt;

&lt;p&gt;No library can know that "InvoiceNo" is an ID — that's domain knowledge. What a tool &lt;em&gt;can&lt;/em&gt; do is disclose its guess and hand you a replayable plan you can correct:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;naive&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;plan&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CSV_PATH&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;return_params&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="c1"&gt;# warns: ambiguous at 0.98
&lt;/span&gt;
&lt;span class="n"&gt;plan&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;columns&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;InvoiceNo&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;dtype&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;object&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;      &lt;span class="c1"&gt;# invoices are labels
&lt;/span&gt;&lt;span class="n"&gt;plan&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;columns&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;StockCode&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;dtype&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;object&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;      &lt;span class="c1"&gt;# product codes too
&lt;/span&gt;
&lt;span class="n"&gt;df&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CSV_PATH&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;plan&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;                   &lt;span class="c1"&gt;# deterministic replay
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Cancellation invoices preserved: 9,288
InvoiceNo values lost: 0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The correction is now documented, versionable code — not a mystery cell someone ran once.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three habits this taught me
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Never trust the head of a file.&lt;/strong&gt; The first rows parse clean at 100%; the truth lives in the full-file parse rate. Measure every cell.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Declare identifiers explicitly.&lt;/strong&gt; Invoice numbers, product codes, phone numbers, zip codes — they look numeric and they are not. One schema line prevents the whole class of bug.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Split, don't delete.&lt;/strong&gt; The final frame here is three frames: sales (530,104 rows, 97.8%), returns (9,288, 1.7%), oddities (2,517, 0.5%). Deleted rows can't answer questions later; split rows can — the returns table is where the phantom bestseller was caught.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;The full walkthrough — from messy CSV to a business decision, every step printing what it did and why — is published fully executed on Kaggle: &lt;a href="https://www.kaggle.com/code/ahmedabdeltawab40/rescuing-9-291-invoices-from-coercion-pydextra" rel="noopener noreferrer"&gt;Rescuing 9,291 invoices from coercion (pydextra)&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It runs on pydextra (&lt;code&gt;pip install pydextra&lt;/code&gt;, MIT), a small library I built around one idea: every function prints a one-line &lt;code&gt;Decision:&lt;/code&gt; explaining what it did and why. To be transparent: it's a personal educational-practical project, not a replacement for pandas — pandas is the engine underneath.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;GitHub: &lt;a href="https://github.com/ahmedabdeltawab602-collab/dextra" rel="noopener noreferrer"&gt;https://github.com/ahmedabdeltawab602-collab/dextra&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Docs: &lt;a href="https://ahmedabdeltawab602-collab.github.io/dextra/" rel="noopener noreferrer"&gt;https://ahmedabdeltawab602-collab.github.io/dextra/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Previous article — data leakage, same AUC, hidden lie: &lt;a href="https://dev.to/ahmedabdeltawab/an-adversarial-review-found-11-real-defects-in-my-python-library-best-decision-i-made-2h85"&gt;https://dev.to/ahmedabdeltawab/an-adversarial-review-found-11-real-defects-in-my-python-library-best-decision-i-made-2h85&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Data: Chen, D. (2015). Online Retail. UCI Machine Learning Repository. DOI 10.24432/C5BW33, CC BY 4.0.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>datascience</category>
      <category>pandas</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>An adversarial review found 11 real defects in my Python library. Best decision I made.</title>
      <dc:creator>Ahmed Abdeltawab</dc:creator>
      <pubDate>Tue, 11 Aug 2026 12:23:15 +0000</pubDate>
      <link>https://dev.to/ahmedabdeltawab/an-adversarial-review-found-11-real-defects-in-my-python-library-best-decision-i-made-2h85</link>
      <guid>https://dev.to/ahmedabdeltawab/an-adversarial-review-found-11-real-defects-in-my-python-library-best-decision-i-made-2h85</guid>
      <description>&lt;p&gt;I built and published a Python library — then put it in front of an adversarial review whose only job was to break it. This is what it found, and what the library looks like now.&lt;/p&gt;

&lt;h2&gt;
  
  
  What pydextra is
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;pip install pydextra&lt;/code&gt;, then &lt;code&gt;import dextra as dx&lt;/code&gt;. It's a small data-analysis library with one obsession: &lt;strong&gt;disclosure&lt;/strong&gt;. Its 63 public functions — plus 5 scikit-learn-compatible wrappers, 68 public callables in all — share one flag vocabulary and:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;print a one-line &lt;code&gt;Decision:&lt;/code&gt; explaining what they did and why,&lt;/li&gt;
&lt;li&gt;keep an audit trail on the DataFrame,&lt;/li&gt;
&lt;li&gt;and, wherever statistics are learned from data, return a replayable &lt;code&gt;params&lt;/code&gt; plan — fit on train, replay verbatim on test — so train/test leakage becomes hard to commit by accident.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One real call, verbatim from the leakage-safe pipeline notebook:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;train_fe&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;featpipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;train&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;steps&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;steps&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;return_params&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Decision: Fitted a 3-step featpipe pipeline (handle_missing -&amp;gt; encode -&amp;gt; scale);
33 new column(s) produced; combined params is a versioned, JSON-serialisable
artifact. Apply to held-out data with featpipe(df_test, params=...).
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The part I'm most proud of isn't a feature
&lt;/h2&gt;

&lt;p&gt;An adversarial external evaluation found &lt;strong&gt;11 evidence-backed defects&lt;/strong&gt; in the library. Version 0.6.0 closed all 11 — each one reproduced first by a failing "red" test, then fixed until green, with ~35 permanent regression tests and zero-warning exit gates. The original independent audit had scored an earlier version 82/100; the updated referee evaluation of 0.6.0 passed every agreed gate — 10/10 on a measurable definition fixed before the work started — with the remaining limitations listed openly in the repo.&lt;/p&gt;

&lt;p&gt;Since 0.6.0 the API is frozen by policy: no new features, hotfixes only. What you evaluate today is what you run next year.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three notebooks, real mess
&lt;/h2&gt;

&lt;p&gt;All three are published fully executed on Kaggle:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://www.kaggle.com/code/ahmedabdeltawab40/rescuing-9-291-invoices-from-coercion-pydextra" rel="noopener noreferrer"&gt;Rescuing 9,291 invoice numbers from silent coercion&lt;/a&gt;&lt;/strong&gt; — how numeric coercion silently eats identifiers, and how a disclosure-first loader catches it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://www.kaggle.com/code/ahmedabdeltawab40/two-pipelines-same-auc-0-8465-one-is-lying" rel="noopener noreferrer"&gt;Two pipelines, same AUC (0.8465) — one is lying&lt;/a&gt;&lt;/strong&gt; — the wrong protocol and the right one produce identical single-run AUC (0.8465); only 10 repetitions expose the real optimistic bias (+0.0261 ± 0.0210, wrong side higher in 9 of 10 runs). The full write-up: &lt;a href="https://ahmedabdeltawab602-collab.github.io/dextra/same-auc-hidden-leak/" rel="noopener noreferrer"&gt;Same AUC, hidden leak&lt;/a&gt;, or &lt;a href="https://colab.research.google.com/github/ahmedabdeltawab602-collab/dextra/blob/main/notebooks/00-leakage-in-5-minutes.ipynb" rel="noopener noreferrer"&gt;run it in 5 minutes on Colab&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://www.kaggle.com/code/ahmedabdeltawab40/egypt-food-prices-2010-2026-arabic-eda-pydextra" rel="noopener noreferrer"&gt;Egypt food prices 2010–2026: a fully-Arabic EDA&lt;/a&gt;&lt;/strong&gt; — garlic +214.8% in one jump. Deliberately written in Arabic: technical Arabic data-science content is rare, and this is a small contribution against that gap.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Honest scope
&lt;/h2&gt;

&lt;p&gt;It's a personal educational-practical project — &lt;strong&gt;not&lt;/strong&gt; a pandas replacement, and not aimed at production-scale pipelines. pandas is the engine underneath; pydextra adds the disclosure layer.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;GitHub: &lt;a href="https://github.com/ahmedabdeltawab602-collab/dextra" rel="noopener noreferrer"&gt;https://github.com/ahmedabdeltawab602-collab/dextra&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;PyPI: &lt;a href="https://pypi.org/project/pydextra/" rel="noopener noreferrer"&gt;https://pypi.org/project/pydextra/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Docs: &lt;a href="https://ahmedabdeltawab602-collab.github.io/dextra/" rel="noopener noreferrer"&gt;https://ahmedabdeltawab602-collab.github.io/dextra/&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If the idea of functions that explain themselves appeals to you, a GitHub star helps others find it.&lt;/p&gt;

</description>
      <category>python</category>
      <category>datascience</category>
      <category>opensource</category>
      <category>testing</category>
    </item>
  </channel>
</rss>
