{
  "id": 665101,
  "title": "Unicode Fractions vs Decimals: LB Score Unchanged?",
  "url": "/competitions/deep-past-initiative-machine-translation/discussion/665101",
  "author_name": "",
  "post_date": "2025-12-30T06:10:06.391320700Z",
  "votes": 10,
  "comment_count": 8,
  "views": 0,
  "content": "<p>Hi everyone,\nFollowing the host's comment regarding the hidden test dataset using Unicode fractions (e.g., <code>½</code>, <code>⅓</code>, <code>2 ½</code>) instead of decimals/floats, I implemented a post-processing step to align my model's output with the expected format.</p>\n<h3>The Context</h3>\n<p>In the discussion <a href=\"https://www.kaggle.com/competitions/deep-past-initiative-machine-translation/discussion/664795\" target=\"_blank\">How To Handle These Examples</a>, the host mentioned:</p>\n<pre><code>\"The fractions are retained in the hidden test dataset... you will find 2 ½ minas... and NOT 2.5 ma-na.\"\n</code></pre>\n<h3>My Approach</h3>\n<p>I wrote a script to dynamically convert decimal patterns in my predictions to their closest Unicode fraction equivalents (using <code>limit_denominator</code> to catch <code>0.333...</code> as <code>⅓</code>).\nVisually, the script seems to work perfectly on the sample items:</p>\n<ul>\n<li><strong>Example 1:</strong> <code>42.33333 ma-na</code> -&gt; <code>42 ⅓ ma-na</code></li>\n<li><strong>Example 2:</strong> <code>1.25 GÍN</code> -&gt; <code>1 ¼ GÍN</code></li>\n</ul>\n<h3>The Issue</h3>\n<p>Despite this correction clearly matching the host's description of the ground truth format, <strong>my LB score remained exactly the same</strong> (to the 4th decimal) compared to the submission with raw floats.</p>\n<h3>My Code</h3>\n<p>Here is the function I used. Is there an edge case I am missing, or is the spacing logic (<code>Integer[space]Fraction</code> vs <code>IntegerFraction</code>) different from what I assumed?</p>\n<pre><code>import re\nfrom fractions import Fraction\ndef process_fractions_dynamic(row, target_col=\"prediction\"):\n   text_to_process = row.get(target_col, \"\")\n   if not isinstance(text_to_process, str):\n       return str(text_to_process)\n   # Standard unicode map\n   fraction_lookup = {\n       (1, 2): \"½\", (1, 3): \"⅓\", (2, 3): \"⅔\",\n       (1, 4): \"¼\", (3, 4): \"¾\",\n       (1, 5): \"⅕\", (2, 5): \"⅖\", (3, 5): \"⅗\", (4, 5): \"⅘\",\n       (1, 6): \"⅙\", (5, 6): \"⅚\",\n       (1, 8): \"⅛\", (3, 8): \"⅜\", (5, 8): \"⅝\", (7, 8): \"⅞\"\n   }\n   def replacer(match):\n       full_str = match.group(0)\n       try:\n           val = float(full_str)\n           integer_part = int(val)\n           decimal_part = val - integer_part\n           # Return integer if decimal is negligible\n           if decimal_part &lt; 0.0001: \n               return str(integer_part)\n           # Limit denominator handles 0.3333 -&gt; 1/3\n           frac = Fraction(decimal_part).limit_denominator(12) \n\n           unicode_frac = fraction_lookup.get((frac.numerator, frac.denominator))\n           if unicode_frac:\n               if integer_part == 0:\n                   return unicode_frac\n               else:\n                   # Current logic: Space between Integer and Fraction\n                   return f\"{integer_part} {unicode_frac}\"\n\n           return full_str \n       except ValueError:\n           return full_str\n   processed_text = re.sub(r'\\b\\d+\\.\\d+\\b', replacer, text_to_process)\n   return processed_text\n</code></pre>\n<h2>Ouput</h2>\n<pre><code>[4] \n2 ne2-pi2-szu 15 ma-na.TA u2 isz-ti2-in ne2-pi2-szu-um 10 ma-na ni-is-ha-su2 DIRI sza-du-a-su2 sza-bu-u2 SZU.NÍGIN 42.33333 ma-na KÙ.BABBAR s,a-ru-pa2-am ku-nu-ki-a a-na a-lu-wa u3 e-ni-sza-ri-im a2p-qi2-id-ma a-na a-lim{ki} a ma-la2 ti2-ir-ti2-szu a-s,e2-er sza-lim-a-szu3r u2-sze2-bi-il5-szu-nu a-ha-ma 13.33333 ma-na URUDU SIG5 a-na ga-am-ri-szu-nu u3 5 GÍN KÙ.BABBAR a-na u2-ku-ul-ti2-szu-nu a-di2-in IGI ili5-ba-ni DUMU ba-szi2-lam IGI a-hu-qar DUMU zu-ur-zu-ur IGI tu3-ra-am-i3-li2 DUMU e-di2-na-a-szu3r a-ha-ma 10.33333 ma-na 3.5 GÍN KÙ.BABBAR s,a-ru-pa2-am &lt;gap&gt; tum ni &lt;gap&gt; 0.5 GÍN s,i2-ba-tim sza i-na s,e2-ri-a il5-qe2-u2-ni a-na hu-bu-li-szu a-na ka3-ri-im wa-ah-szu-sza-na a2sz-qu2l s,i2-ba-at KÙ.GI sza szi2-ip-ka3-at a-szur-be2-el-a-wa-tim i-szi2-tu3\nTranslation : 2 packages of 15 minas each plus a single package of 10 minas, its import duty added, its transport tariff paid - in all 42 ⅓ minas of refined silver under my seal, I entrusted to Aluwa and Enišārum, and I sent them to the City to Šalim-Aššur in accordance with his orders. Furthermore, I paid 13 ⅓ minas of good copper for their expenses and 5 shekels of silver for their food. Witnessed by Ilī-bāni son of Baši-ilum, by Ahu-qar son of Zurzur, by Tūram-ilī son of Eddin-Aššur. Furthermore, 10 ⅓ minas 3 ½ shekels of refined silver   &lt;big_gap&gt; ,   &lt;big_gap&gt; ½, shekel interest that they took on my account, I paid for his debt to the Wahšušana colony. The interest on the gold that remained of Aššur-bēl-awātim's investment.\nPrecdiction : 2 packages of 15 minas each and a single package of 10 minas - its import duty added, its transport tariff paid - in all 42 ⅓ minas of refined silver under my seal I entrusted to Aluwa and Enišārum, and I sent them to the City in accordance with his instructions to Šalim-Aššur. Further, I gave 13 ⅓ minas of good copper for their expenses and 5 shekels of silver for their food. Witnessed by Ilī-bāni son of Bašilum, by Ahu-waqar son of Zurzur, by Tūr.\n==============================\n[6] \n1.25 GÍN KÙ.BABBAR i-na li-bi4 lu-lu\nTranslation : 1 1 / 4 shekel of silver is owed by Lulu.\nPrecdiction : 1 ¼ shekel of silver owed by Lulu.\n</code></pre>\n<h2>Queries</h2>\n<ul>\n<li>Metric Sensitivity: Is the evaluation metric sensitive enough that correcting 0.33 (4 chars) to ⅓ (1 char) results in a score change too small to see on the public LB?</li>\n<li>Formatting: Does the ground truth use a space between the integer and the fraction (e.g., 2 ½) or are they concatenated (2½)?</li>\n<li>Normalization: Is there a hidden normalization step in the metric that might be converting ½ back to numbers or ignoring these differences?</li>\n</ul>",
  "messages": [
    {
      "id": "3383435",
      "postDate": "12/30/2025 06:10:06",
      "content": "<p>Hi everyone,\nFollowing the host's comment regarding the hidden test dataset using Unicode fractions (e.g., <code>½</code>, <code>⅓</code>, <code>2 ½</code>) instead of decimals/floats, I implemented a post-processing step to align my model's output with the expected format.</p>\n<h3>The Context</h3>\n<p>In the discussion <a href=\"https://www.kaggle.com/competitions/deep-past-initiative-machine-translation/discussion/664795\" target=\"_blank\">How To Handle These Examples</a>, the host mentioned:</p>\n<pre><code>\"The fractions are retained in the hidden test dataset... you will find 2 ½ minas... and NOT 2.5 ma-na.\"\n</code></pre>\n<h3>My Approach</h3>\n<p>I wrote a script to dynamically convert decimal patterns in my predictions to their closest Unicode fraction equivalents (using <code>limit_denominator</code> to catch <code>0.333...</code> as <code>⅓</code>).\nVisually, the script seems to work perfectly on the sample items:</p>\n<ul>\n<li><strong>Example 1:</strong> <code>42.33333 ma-na</code> -&gt; <code>42 ⅓ ma-na</code></li>\n<li><strong>Example 2:</strong> <code>1.25 GÍN</code> -&gt; <code>1 ¼ GÍN</code></li>\n</ul>\n<h3>The Issue</h3>\n<p>Despite this correction clearly matching the host's description of the ground truth format, <strong>my LB score remained exactly the same</strong> (to the 4th decimal) compared to the submission with raw floats.</p>\n<h3>My Code</h3>\n<p>Here is the function I used. Is there an edge case I am missing, or is the spacing logic (<code>Integer[space]Fraction</code> vs <code>IntegerFraction</code>) different from what I assumed?</p>\n<pre><code>import re\nfrom fractions import Fraction\ndef process_fractions_dynamic(row, target_col=\"prediction\"):\n   text_to_process = row.get(target_col, \"\")\n   if not isinstance(text_to_process, str):\n       return str(text_to_process)\n   # Standard unicode map\n   fraction_lookup = {\n       (1, 2): \"½\", (1, 3): \"⅓\", (2, 3): \"⅔\",\n       (1, 4): \"¼\", (3, 4): \"¾\",\n       (1, 5): \"⅕\", (2, 5): \"⅖\", (3, 5): \"⅗\", (4, 5): \"⅘\",\n       (1, 6): \"⅙\", (5, 6): \"⅚\",\n       (1, 8): \"⅛\", (3, 8): \"⅜\", (5, 8): \"⅝\", (7, 8): \"⅞\"\n   }\n   def replacer(match):\n       full_str = match.group(0)\n       try:\n           val = float(full_str)\n           integer_part = int(val)\n           decimal_part = val - integer_part\n           # Return integer if decimal is negligible\n           if decimal_part &lt; 0.0001: \n               return str(integer_part)\n           # Limit denominator handles 0.3333 -&gt; 1/3\n           frac = Fraction(decimal_part).limit_denominator(12) \n\n           unicode_frac = fraction_lookup.get((frac.numerator, frac.denominator))\n           if unicode_frac:\n               if integer_part == 0:\n                   return unicode_frac\n               else:\n                   # Current logic: Space between Integer and Fraction\n                   return f\"{integer_part} {unicode_frac}\"\n\n           return full_str \n       except ValueError:\n           return full_str\n   processed_text = re.sub(r'\\b\\d+\\.\\d+\\b', replacer, text_to_process)\n   return processed_text\n</code></pre>\n<h2>Ouput</h2>\n<pre><code>[4] \n2 ne2-pi2-szu 15 ma-na.TA u2 isz-ti2-in ne2-pi2-szu-um 10 ma-na ni-is-ha-su2 DIRI sza-du-a-su2 sza-bu-u2 SZU.NÍGIN 42.33333 ma-na KÙ.BABBAR s,a-ru-pa2-am ku-nu-ki-a a-na a-lu-wa u3 e-ni-sza-ri-im a2p-qi2-id-ma a-na a-lim{ki} a ma-la2 ti2-ir-ti2-szu a-s,e2-er sza-lim-a-szu3r u2-sze2-bi-il5-szu-nu a-ha-ma 13.33333 ma-na URUDU SIG5 a-na ga-am-ri-szu-nu u3 5 GÍN KÙ.BABBAR a-na u2-ku-ul-ti2-szu-nu a-di2-in IGI ili5-ba-ni DUMU ba-szi2-lam IGI a-hu-qar DUMU zu-ur-zu-ur IGI tu3-ra-am-i3-li2 DUMU e-di2-na-a-szu3r a-ha-ma 10.33333 ma-na 3.5 GÍN KÙ.BABBAR s,a-ru-pa2-am &lt;gap&gt; tum ni &lt;gap&gt; 0.5 GÍN s,i2-ba-tim sza i-na s,e2-ri-a il5-qe2-u2-ni a-na hu-bu-li-szu a-na ka3-ri-im wa-ah-szu-sza-na a2sz-qu2l s,i2-ba-at KÙ.GI sza szi2-ip-ka3-at a-szur-be2-el-a-wa-tim i-szi2-tu3\nTranslation : 2 packages of 15 minas each plus a single package of 10 minas, its import duty added, its transport tariff paid - in all 42 ⅓ minas of refined silver under my seal, I entrusted to Aluwa and Enišārum, and I sent them to the City to Šalim-Aššur in accordance with his orders. Furthermore, I paid 13 ⅓ minas of good copper for their expenses and 5 shekels of silver for their food. Witnessed by Ilī-bāni son of Baši-ilum, by Ahu-qar son of Zurzur, by Tūram-ilī son of Eddin-Aššur. Furthermore, 10 ⅓ minas 3 ½ shekels of refined silver   &lt;big_gap&gt; ,   &lt;big_gap&gt; ½, shekel interest that they took on my account, I paid for his debt to the Wahšušana colony. The interest on the gold that remained of Aššur-bēl-awātim's investment.\nPrecdiction : 2 packages of 15 minas each and a single package of 10 minas - its import duty added, its transport tariff paid - in all 42 ⅓ minas of refined silver under my seal I entrusted to Aluwa and Enišārum, and I sent them to the City in accordance with his instructions to Šalim-Aššur. Further, I gave 13 ⅓ minas of good copper for their expenses and 5 shekels of silver for their food. Witnessed by Ilī-bāni son of Bašilum, by Ahu-waqar son of Zurzur, by Tūr.\n==============================\n[6] \n1.25 GÍN KÙ.BABBAR i-na li-bi4 lu-lu\nTranslation : 1 1 / 4 shekel of silver is owed by Lulu.\nPrecdiction : 1 ¼ shekel of silver owed by Lulu.\n</code></pre>\n<h2>Queries</h2>\n<ul>\n<li>Metric Sensitivity: Is the evaluation metric sensitive enough that correcting 0.33 (4 chars) to ⅓ (1 char) results in a score change too small to see on the public LB?</li>\n<li>Formatting: Does the ground truth use a space between the integer and the fraction (e.g., 2 ½) or are they concatenated (2½)?</li>\n<li>Normalization: Is there a hidden normalization step in the metric that might be converting ½ back to numbers or ignoring these differences?</li>\n</ul>",
      "rawMarkdown": "Hi everyone,\n\nFollowing the host's comment regarding the hidden test dataset using Unicode fractions (e.g., `½`, `⅓`, `2 ½`) instead of decimals/floats, I implemented a post-processing step to align my model's output with the expected format.\n\n### The Context\nIn the discussion [How To Handle These Examples](https://www.kaggle.com/competitions/deep-past-initiative-machine-translation/discussion/664795), the host mentioned:\n```\n\"The fractions are retained in the hidden test dataset... you will find 2 ½ minas... and NOT 2.5 ma-na.\"\n```\n### My Approach\nI wrote a script to dynamically convert decimal patterns in my predictions to their closest Unicode fraction equivalents (using `limit_denominator` to catch `0.333...` as `⅓`).\n\nVisually, the script seems to work perfectly on the sample items:\n\n* **Example 1:** `42.33333 ma-na` -> `42 ⅓ ma-na`\n* **Example 2:** `1.25 GÍN` -> `1 ¼ GÍN`\n\n### The Issue\nDespite this correction clearly matching the host's description of the ground truth format, **my LB score remained exactly the same** (to the 4th decimal) compared to the submission with raw floats.\n\n### My Code\nHere is the function I used. Is there an edge case I am missing, or is the spacing logic (`Integer[space]Fraction` vs `IntegerFraction`) different from what I assumed?\n\n```python\nimport re\nfrom fractions import Fraction\n\ndef process_fractions_dynamic(row, target_col=\"prediction\"):\n    text_to_process = row.get(target_col, \"\")\n    if not isinstance(text_to_process, str):\n        return str(text_to_process)\n\n    # Standard unicode map\n    fraction_lookup = {\n        (1, 2): \"½\", (1, 3): \"⅓\", (2, 3): \"⅔\",\n        (1, 4): \"¼\", (3, 4): \"¾\",\n        (1, 5): \"⅕\", (2, 5): \"⅖\", (3, 5): \"⅗\", (4, 5): \"⅘\",\n        (1, 6): \"⅙\", (5, 6): \"⅚\",\n        (1, 8): \"⅛\", (3, 8): \"⅜\", (5, 8): \"⅝\", (7, 8): \"⅞\"\n    }\n\n    def replacer(match):\n        full_str = match.group(0)\n        try:\n            val = float(full_str)\n            integer_part = int(val)\n            decimal_part = val - integer_part\n\n            # Return integer if decimal is negligible\n            if decimal_part < 0.0001: \n                return str(integer_part)\n\n            # Limit denominator handles 0.3333 -> 1/3\n            frac = Fraction(decimal_part).limit_denominator(12) \n            \n            unicode_frac = fraction_lookup.get((frac.numerator, frac.denominator))\n\n            if unicode_frac:\n                if integer_part == 0:\n                    return unicode_frac\n                else:\n                    # Current logic: Space between Integer and Fraction\n                    return f\"{integer_part} {unicode_frac}\"\n            \n            return full_str \n\n        except ValueError:\n            return full_str\n\n    processed_text = re.sub(r'\\b\\d+\\.\\d+\\b', replacer, text_to_process)\n    return processed_text\n```\n## Ouput \n```\n[4] \n 2 ne2-pi2-szu 15 ma-na.TA u2 isz-ti2-in ne2-pi2-szu-um 10 ma-na ni-is-ha-su2 DIRI sza-du-a-su2 sza-bu-u2 SZU.NÍGIN 42.33333 ma-na KÙ.BABBAR s,a-ru-pa2-am ku-nu-ki-a a-na a-lu-wa u3 e-ni-sza-ri-im a2p-qi2-id-ma a-na a-lim{ki} a ma-la2 ti2-ir-ti2-szu a-s,e2-er sza-lim-a-szu3r u2-sze2-bi-il5-szu-nu a-ha-ma 13.33333 ma-na URUDU SIG5 a-na ga-am-ri-szu-nu u3 5 GÍN KÙ.BABBAR a-na u2-ku-ul-ti2-szu-nu a-di2-in IGI ili5-ba-ni DUMU ba-szi2-lam IGI a-hu-qar DUMU zu-ur-zu-ur IGI tu3-ra-am-i3-li2 DUMU e-di2-na-a-szu3r a-ha-ma 10.33333 ma-na 3.5 GÍN KÙ.BABBAR s,a-ru-pa2-am <gap> tum ni <gap> 0.5 GÍN s,i2-ba-tim sza i-na s,e2-ri-a il5-qe2-u2-ni a-na hu-bu-li-szu a-na ka3-ri-im wa-ah-szu-sza-na a2sz-qu2l s,i2-ba-at KÙ.GI sza szi2-ip-ka3-at a-szur-be2-el-a-wa-tim i-szi2-tu3\nTranslation : 2 packages of 15 minas each plus a single package of 10 minas, its import duty added, its transport tariff paid - in all 42 ⅓ minas of refined silver under my seal, I entrusted to Aluwa and Enišārum, and I sent them to the City to Šalim-Aššur in accordance with his orders. Furthermore, I paid 13 ⅓ minas of good copper for their expenses and 5 shekels of silver for their food. Witnessed by Ilī-bāni son of Baši-ilum, by Ahu-qar son of Zurzur, by Tūram-ilī son of Eddin-Aššur. Furthermore, 10 ⅓ minas 3 ½ shekels of refined silver   <big_gap> ,   <big_gap> ½, shekel interest that they took on my account, I paid for his debt to the Wahšušana colony. The interest on the gold that remained of Aššur-bēl-awātim's investment.\nPrecdiction : 2 packages of 15 minas each and a single package of 10 minas - its import duty added, its transport tariff paid - in all 42 ⅓ minas of refined silver under my seal I entrusted to Aluwa and Enišārum, and I sent them to the City in accordance with his instructions to Šalim-Aššur. Further, I gave 13 ⅓ minas of good copper for their expenses and 5 shekels of silver for their food. Witnessed by Ilī-bāni son of Bašilum, by Ahu-waqar son of Zurzur, by Tūr.\n==============================\n[6] \n 1.25 GÍN KÙ.BABBAR i-na li-bi4 lu-lu\nTranslation : 1 1 / 4 shekel of silver is owed by Lulu.\nPrecdiction : 1 ¼ shekel of silver owed by Lulu.\n```\n## Queries\n\n* Metric Sensitivity: Is the evaluation metric sensitive enough that correcting 0.33 (4 chars) to ⅓ (1 char) results in a score change too small to see on the public LB?\n\n* Formatting: Does the ground truth use a space between the integer and the fraction (e.g., 2 ½) or are they concatenated (2½)?\n\n* Normalization: Is there a hidden normalization step in the metric that might be converting ½ back to numbers or ignoring these differences?",
      "votes": null
    },
    {
      "id": "3383499",
      "postDate": "12/30/2025 09:41:12",
      "content": "<p>With and without space has the same LB </p>",
      "rawMarkdown": "With and without space has the same LB",
      "votes": null
    },
    {
      "id": "3383527",
      "postDate": "12/30/2025 10:42:18",
      "content": "<p>If the test set is similar to the <code>train.csv</code> set, then it will matter for ca. 38% of the transliterations / ca. 33% of translations:\n<img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F2259646%2F3559fe6ef88ba7f4bc76fb61a6a56bcc%2FFraction%20density%20in%20transliterations-translations.png?generation=1767090967162089&amp;alt=media\" alt=\"\"></p>\n<p>For those that it will, it won't give a large impact, since in most cases fractions account for less than 5% of words:\n<img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F2259646%2Ffcd4ecbbc6bdfc739cc339e34cd481ff%2FFraction%20to%20word%20ratio%20in%20transliterations-translations.png?generation=1767091109587433&amp;alt=media\" alt=\"\"></p>\n<p>The code for the above fractions:</p>\n<pre><code>number_of_fractions = train.loc[:,'transliteration'].apply(lambda row: len(re.findall(r'\\b([0-9]+\\.33[3]*|[0-9]+\\.5|\\.25|[0-9]+\\.75)\\b', row)))\nnumber_of_words = train.loc[:,'transliteration'].apply(lambda row: len(re.findall(r'\\b([\\w\\-\\.]+)\\b', row)))\nfraction_to_words_ratio = number_of_fractions/number_of_words\n\nnumber_of_fractions_t = train.loc[:,'translation'].apply(lambda row: len(re.findall(r'\\b([0-9]+\\.33[3]*|[0-9]+\\.5|\\.25|[0-9]+\\.75)\\b', row)))\nnumber_of_words_t = train.loc[:,'translation'].apply(lambda row: len(re.findall(r'\\b([\\w\\-\\.]+)\\b', row)))\nfraction_to_words_ratio_t = number_of_fractions_t/number_of_words_t\n</code></pre>\n<p>I haven't consulted the metrics so I can't give the estimates of how they would change but this would be my guess as to why the observed changes are not very impactful.</p>",
      "rawMarkdown": "If the test set is similar to the `train.csv` set, then it will matter for ca. 38% of the transliterations / ca. 33% of translations:\n![](https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F2259646%2F3559fe6ef88ba7f4bc76fb61a6a56bcc%2FFraction%20density%20in%20transliterations-translations.png?generation=1767090967162089&alt=media)\n\nFor those that it will, it won't give a large impact, since in most cases fractions account for less than 5% of words:\n![](https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F2259646%2Ffcd4ecbbc6bdfc739cc339e34cd481ff%2FFraction%20to%20word%20ratio%20in%20transliterations-translations.png?generation=1767091109587433&alt=media)\n\nThe code for the above fractions:\n```python\nnumber_of_fractions = train.loc[:,'transliteration'].apply(lambda row: len(re.findall(r'\\b([0-9]+\\.33[3]*|[0-9]+\\.5|\\.25|[0-9]+\\.75)\\b', row)))\nnumber_of_words = train.loc[:,'transliteration'].apply(lambda row: len(re.findall(r'\\b([\\w\\-\\.]+)\\b', row)))\nfraction_to_words_ratio = number_of_fractions/number_of_words\n\nnumber_of_fractions_t = train.loc[:,'translation'].apply(lambda row: len(re.findall(r'\\b([0-9]+\\.33[3]*|[0-9]+\\.5|\\.25|[0-9]+\\.75)\\b', row)))\nnumber_of_words_t = train.loc[:,'translation'].apply(lambda row: len(re.findall(r'\\b([\\w\\-\\.]+)\\b', row)))\nfraction_to_words_ratio_t = number_of_fractions_t/number_of_words_t\n```\n\nI haven't consulted the metrics so I can't give the estimates of how they would change but this would be my guess as to why the observed changes are not very impactful.",
      "votes": null
    },
    {
      "id": "3383555",
      "postDate": "12/30/2025 11:18:45",
      "content": "<p>It's similar for me the LB remains the same with or without floats to fractions conversion. However, if I sort public LB by score then I can see it's a bit better for the submission with fractions conversion. <a href=\"https://www.kaggle.com/hrithik22122\" target=\"_blank\">@hrithik22122</a> Is it the same for you? If 2 submissions are strictly equal then the first one (oldest) should be the first. Otherwise it's sorted by best score (even if it displays only a single digit as far as I know)</p>\n<p>Second, impact for floats to fractions conversion on geometric_mean is quite low on a single fold OOF for me:</p>\n<ul>\n<li>Floats + limited fractions in ground truth: 45.53 (we've some fractions already in ground truth)</li>\n<li>Fractions only in ground truth: 44.22</li>\n</ul>\n<p>Let's wait for the official voice of organizer. \nThere are for sure other reasons for such a big gap between CV / LB we need to discover.</p>",
      "rawMarkdown": "It's similar for me the LB remains the same with or without floats to fractions conversion. However, if I sort public LB by score then I can see it's a bit better for the submission with fractions conversion. @hrithik22122 Is it the same for you? If 2 submissions are strictly equal then the first one (oldest) should be the first. Otherwise it's sorted by best score (even if it displays only a single digit as far as I know)\n\nSecond, impact for floats to fractions conversion on geometric_mean is quite low on a single fold OOF for me:\n- Floats + limited fractions in ground truth: 45.53 (we've some fractions already in ground truth)\n- Fractions only in ground truth: 44.22\n\nLet's wait for the official voice of organizer. \nThere are for sure other reasons for such a big gap between CV / LB we need to discover.",
      "votes": null
    },
    {
      "id": "3383557",
      "postDate": "12/30/2025 11:24:50",
      "content": "<p>lol,yes, decimal places aren’t shown, but the submission clearly reflects the actual values of the three same scores.</p>",
      "rawMarkdown": "lol,yes, decimal places aren’t shown, but the submission clearly reflects the actual values of the three same scores.",
      "votes": null
    },
    {
      "id": "3383570",
      "postDate": "12/30/2025 11:42:53",
      "content": "<p>Nah , when I sort it , my first submission(one without fraction post processing) comes up first</p>\n<p>I was expecting an LB increase of around 0.2–0.6 because the penalty for raw decimals should be massive:</p>\n<ul>\n<li><p>BLEU: A mismatch like 13.3333 vs 13 ⅓ means zero overlap for that token. By fixing it, we should recover the unigram match, plus the associated bi-grams and tri-grams (\"paid 13.333 silver\" vs \"paid 13 ⅓ silver\").</p></li>\n<li><p>chRF++: The character edit distance between 13.3333 (7 chars) and 13 ⅓ (3-4 chars) is very large. Normalizing this should have significantly reduced the edit distance penalty.</p></li>\n</ul>",
      "rawMarkdown": "Nah , when I sort it , my first submission(one without fraction post processing) comes up first\n\nI was expecting an LB increase of around 0.2–0.6 because the penalty for raw decimals should be massive:\n\n* BLEU: A mismatch like 13.3333 vs 13 ⅓ means zero overlap for that token. By fixing it, we should recover the unigram match, plus the associated bi-grams and tri-grams (\"paid 13.333 silver\" vs \"paid 13 ⅓ silver\").\n\n* chRF++: The character edit distance between 13.3333 (7 chars) and 13 ⅓ (3-4 chars) is very large. Normalizing this should have significantly reduced the edit distance penalty.",
      "votes": null
    },
    {
      "id": "3383671",
      "postDate": "12/30/2025 16:06:08",
      "content": "<p>The ground truth uses a space between the integer and the fraction (e.g., 2 ½).\nLet me put it this way, there are no instances of '/' in either the translation or transliteration, this goes for both the public and private evaluation data.</p>",
      "rawMarkdown": "The ground truth uses a space between the integer and the fraction (e.g., 2 ½).\nLet me put it this way, there are no instances of '/' in either the translation or transliteration, this goes for both the public and private evaluation data.",
      "votes": null
    },
    {
      "id": "3383682",
      "postDate": "12/30/2025 16:37:59",
      "content": "<p>Oh, this is really a highly potential point, I can try it again tomorrow.Thanks</p>",
      "rawMarkdown": "Oh, this is really a highly potential point, I can try it again tomorrow.Thanks",
      "votes": null
    },
    {
      "id": "3383685",
      "postDate": "12/30/2025 16:43:19",
      "content": "<p>Thanks , this is Huge</p>",
      "rawMarkdown": "Thanks , this is Huge",
      "votes": null
    }
  ],
  "comments": [
    {
      "id": 3383499,
      "author_name": "hrithik22122",
      "author_url": "",
      "post_date": "12/30/2025 09:41:12",
      "content": "<p>With and without space has the same LB </p>",
      "votes": null,
      "replies": []
    },
    {
      "id": 3383527,
      "author_name": "angantyr",
      "author_url": "",
      "post_date": "12/30/2025 10:42:18",
      "content": "<p>If the test set is similar to the <code>train.csv</code> set, then it will matter for ca. 38% of the transliterations / ca. 33% of translations:\n<img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F2259646%2F3559fe6ef88ba7f4bc76fb61a6a56bcc%2FFraction%20density%20in%20transliterations-translations.png?generation=1767090967162089&amp;alt=media\" alt=\"\"></p>\n<p>For those that it will, it won't give a large impact, since in most cases fractions account for less than 5% of words:\n<img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F2259646%2Ffcd4ecbbc6bdfc739cc339e34cd481ff%2FFraction%20to%20word%20ratio%20in%20transliterations-translations.png?generation=1767091109587433&amp;alt=media\" alt=\"\"></p>\n<p>The code for the above fractions:</p>\n<pre><code>number_of_fractions = train.loc[:,'transliteration'].apply(lambda row: len(re.findall(r'\\b([0-9]+\\.33[3]*|[0-9]+\\.5|\\.25|[0-9]+\\.75)\\b', row)))\nnumber_of_words = train.loc[:,'transliteration'].apply(lambda row: len(re.findall(r'\\b([\\w\\-\\.]+)\\b', row)))\nfraction_to_words_ratio = number_of_fractions/number_of_words\n\nnumber_of_fractions_t = train.loc[:,'translation'].apply(lambda row: len(re.findall(r'\\b([0-9]+\\.33[3]*|[0-9]+\\.5|\\.25|[0-9]+\\.75)\\b', row)))\nnumber_of_words_t = train.loc[:,'translation'].apply(lambda row: len(re.findall(r'\\b([\\w\\-\\.]+)\\b', row)))\nfraction_to_words_ratio_t = number_of_fractions_t/number_of_words_t\n</code></pre>\n<p>I haven't consulted the metrics so I can't give the estimates of how they would change but this would be my guess as to why the observed changes are not very impactful.</p>",
      "votes": null,
      "replies": []
    },
    {
      "id": 3383555,
      "author_name": "mpware",
      "author_url": "",
      "post_date": "12/30/2025 11:18:45",
      "content": "<p>It's similar for me the LB remains the same with or without floats to fractions conversion. However, if I sort public LB by score then I can see it's a bit better for the submission with fractions conversion. <a href=\"https://www.kaggle.com/hrithik22122\" target=\"_blank\">@hrithik22122</a> Is it the same for you? If 2 submissions are strictly equal then the first one (oldest) should be the first. Otherwise it's sorted by best score (even if it displays only a single digit as far as I know)</p>\n<p>Second, impact for floats to fractions conversion on geometric_mean is quite low on a single fold OOF for me:</p>\n<ul>\n<li>Floats + limited fractions in ground truth: 45.53 (we've some fractions already in ground truth)</li>\n<li>Fractions only in ground truth: 44.22</li>\n</ul>\n<p>Let's wait for the official voice of organizer. \nThere are for sure other reasons for such a big gap between CV / LB we need to discover.</p>",
      "votes": null,
      "replies": [
        {
          "id": 3383557,
          "author_name": "qifeihhh666",
          "author_url": "",
          "post_date": "12/30/2025 11:24:50",
          "content": "<p>lol,yes, decimal places aren’t shown, but the submission clearly reflects the actual values of the three same scores.</p>",
          "votes": null,
          "replies": []
        },
        {
          "id": 3383570,
          "author_name": "hrithik22122",
          "author_url": "",
          "post_date": "12/30/2025 11:42:53",
          "content": "<p>Nah , when I sort it , my first submission(one without fraction post processing) comes up first</p>\n<p>I was expecting an LB increase of around 0.2–0.6 because the penalty for raw decimals should be massive:</p>\n<ul>\n<li><p>BLEU: A mismatch like 13.3333 vs 13 ⅓ means zero overlap for that token. By fixing it, we should recover the unigram match, plus the associated bi-grams and tri-grams (\"paid 13.333 silver\" vs \"paid 13 ⅓ silver\").</p></li>\n<li><p>chRF++: The character edit distance between 13.3333 (7 chars) and 13 ⅓ (3-4 chars) is very large. Normalizing this should have significantly reduced the edit distance penalty.</p></li>\n</ul>",
          "votes": null,
          "replies": []
        }
      ]
    },
    {
      "id": 3383671,
      "author_name": "deeppast",
      "author_url": "",
      "post_date": "12/30/2025 16:06:08",
      "content": "<p>The ground truth uses a space between the integer and the fraction (e.g., 2 ½).\nLet me put it this way, there are no instances of '/' in either the translation or transliteration, this goes for both the public and private evaluation data.</p>",
      "votes": null,
      "replies": [
        {
          "id": 3383682,
          "author_name": "qifeihhh666",
          "author_url": "",
          "post_date": "12/30/2025 16:37:59",
          "content": "<p>Oh, this is really a highly potential point, I can try it again tomorrow.Thanks</p>",
          "votes": null,
          "replies": []
        },
        {
          "id": 3383685,
          "author_name": "hrithik22122",
          "author_url": "",
          "post_date": "12/30/2025 16:43:19",
          "content": "<p>Thanks , this is Huge</p>",
          "votes": null,
          "replies": []
        }
      ]
    }
  ],
  "raw_markdown_by_id": {
    "3383435": "Hi everyone,\n\nFollowing the host's comment regarding the hidden test dataset using Unicode fractions (e.g., `½`, `⅓`, `2 ½`) instead of decimals/floats, I implemented a post-processing step to align my model's output with the expected format.\n\n### The Context\nIn the discussion [How To Handle These Examples](https://www.kaggle.com/competitions/deep-past-initiative-machine-translation/discussion/664795), the host mentioned:\n```\n\"The fractions are retained in the hidden test dataset... you will find 2 ½ minas... and NOT 2.5 ma-na.\"\n```\n### My Approach\nI wrote a script to dynamically convert decimal patterns in my predictions to their closest Unicode fraction equivalents (using `limit_denominator` to catch `0.333...` as `⅓`).\n\nVisually, the script seems to work perfectly on the sample items:\n\n* **Example 1:** `42.33333 ma-na` -> `42 ⅓ ma-na`\n* **Example 2:** `1.25 GÍN` -> `1 ¼ GÍN`\n\n### The Issue\nDespite this correction clearly matching the host's description of the ground truth format, **my LB score remained exactly the same** (to the 4th decimal) compared to the submission with raw floats.\n\n### My Code\nHere is the function I used. Is there an edge case I am missing, or is the spacing logic (`Integer[space]Fraction` vs `IntegerFraction`) different from what I assumed?\n\n```python\nimport re\nfrom fractions import Fraction\n\ndef process_fractions_dynamic(row, target_col=\"prediction\"):\n    text_to_process = row.get(target_col, \"\")\n    if not isinstance(text_to_process, str):\n        return str(text_to_process)\n\n    # Standard unicode map\n    fraction_lookup = {\n        (1, 2): \"½\", (1, 3): \"⅓\", (2, 3): \"⅔\",\n        (1, 4): \"¼\", (3, 4): \"¾\",\n        (1, 5): \"⅕\", (2, 5): \"⅖\", (3, 5): \"⅗\", (4, 5): \"⅘\",\n        (1, 6): \"⅙\", (5, 6): \"⅚\",\n        (1, 8): \"⅛\", (3, 8): \"⅜\", (5, 8): \"⅝\", (7, 8): \"⅞\"\n    }\n\n    def replacer(match):\n        full_str = match.group(0)\n        try:\n            val = float(full_str)\n            integer_part = int(val)\n            decimal_part = val - integer_part\n\n            # Return integer if decimal is negligible\n            if decimal_part < 0.0001: \n                return str(integer_part)\n\n            # Limit denominator handles 0.3333 -> 1/3\n            frac = Fraction(decimal_part).limit_denominator(12) \n            \n            unicode_frac = fraction_lookup.get((frac.numerator, frac.denominator))\n\n            if unicode_frac:\n                if integer_part == 0:\n                    return unicode_frac\n                else:\n                    # Current logic: Space between Integer and Fraction\n                    return f\"{integer_part} {unicode_frac}\"\n            \n            return full_str \n\n        except ValueError:\n            return full_str\n\n    processed_text = re.sub(r'\\b\\d+\\.\\d+\\b', replacer, text_to_process)\n    return processed_text\n```\n## Ouput \n```\n[4] \n 2 ne2-pi2-szu 15 ma-na.TA u2 isz-ti2-in ne2-pi2-szu-um 10 ma-na ni-is-ha-su2 DIRI sza-du-a-su2 sza-bu-u2 SZU.NÍGIN 42.33333 ma-na KÙ.BABBAR s,a-ru-pa2-am ku-nu-ki-a a-na a-lu-wa u3 e-ni-sza-ri-im a2p-qi2-id-ma a-na a-lim{ki} a ma-la2 ti2-ir-ti2-szu a-s,e2-er sza-lim-a-szu3r u2-sze2-bi-il5-szu-nu a-ha-ma 13.33333 ma-na URUDU SIG5 a-na ga-am-ri-szu-nu u3 5 GÍN KÙ.BABBAR a-na u2-ku-ul-ti2-szu-nu a-di2-in IGI ili5-ba-ni DUMU ba-szi2-lam IGI a-hu-qar DUMU zu-ur-zu-ur IGI tu3-ra-am-i3-li2 DUMU e-di2-na-a-szu3r a-ha-ma 10.33333 ma-na 3.5 GÍN KÙ.BABBAR s,a-ru-pa2-am <gap> tum ni <gap> 0.5 GÍN s,i2-ba-tim sza i-na s,e2-ri-a il5-qe2-u2-ni a-na hu-bu-li-szu a-na ka3-ri-im wa-ah-szu-sza-na a2sz-qu2l s,i2-ba-at KÙ.GI sza szi2-ip-ka3-at a-szur-be2-el-a-wa-tim i-szi2-tu3\nTranslation : 2 packages of 15 minas each plus a single package of 10 minas, its import duty added, its transport tariff paid - in all 42 ⅓ minas of refined silver under my seal, I entrusted to Aluwa and Enišārum, and I sent them to the City to Šalim-Aššur in accordance with his orders. Furthermore, I paid 13 ⅓ minas of good copper for their expenses and 5 shekels of silver for their food. Witnessed by Ilī-bāni son of Baši-ilum, by Ahu-qar son of Zurzur, by Tūram-ilī son of Eddin-Aššur. Furthermore, 10 ⅓ minas 3 ½ shekels of refined silver   <big_gap> ,   <big_gap> ½, shekel interest that they took on my account, I paid for his debt to the Wahšušana colony. The interest on the gold that remained of Aššur-bēl-awātim's investment.\nPrecdiction : 2 packages of 15 minas each and a single package of 10 minas - its import duty added, its transport tariff paid - in all 42 ⅓ minas of refined silver under my seal I entrusted to Aluwa and Enišārum, and I sent them to the City in accordance with his instructions to Šalim-Aššur. Further, I gave 13 ⅓ minas of good copper for their expenses and 5 shekels of silver for their food. Witnessed by Ilī-bāni son of Bašilum, by Ahu-waqar son of Zurzur, by Tūr.\n==============================\n[6] \n 1.25 GÍN KÙ.BABBAR i-na li-bi4 lu-lu\nTranslation : 1 1 / 4 shekel of silver is owed by Lulu.\nPrecdiction : 1 ¼ shekel of silver owed by Lulu.\n```\n## Queries\n\n* Metric Sensitivity: Is the evaluation metric sensitive enough that correcting 0.33 (4 chars) to ⅓ (1 char) results in a score change too small to see on the public LB?\n\n* Formatting: Does the ground truth use a space between the integer and the fraction (e.g., 2 ½) or are they concatenated (2½)?\n\n* Normalization: Is there a hidden normalization step in the metric that might be converting ½ back to numbers or ignoring these differences?",
    "3383499": "With and without space has the same LB",
    "3383527": "If the test set is similar to the `train.csv` set, then it will matter for ca. 38% of the transliterations / ca. 33% of translations:\n![](https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F2259646%2F3559fe6ef88ba7f4bc76fb61a6a56bcc%2FFraction%20density%20in%20transliterations-translations.png?generation=1767090967162089&alt=media)\n\nFor those that it will, it won't give a large impact, since in most cases fractions account for less than 5% of words:\n![](https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F2259646%2Ffcd4ecbbc6bdfc739cc339e34cd481ff%2FFraction%20to%20word%20ratio%20in%20transliterations-translations.png?generation=1767091109587433&alt=media)\n\nThe code for the above fractions:\n```python\nnumber_of_fractions = train.loc[:,'transliteration'].apply(lambda row: len(re.findall(r'\\b([0-9]+\\.33[3]*|[0-9]+\\.5|\\.25|[0-9]+\\.75)\\b', row)))\nnumber_of_words = train.loc[:,'transliteration'].apply(lambda row: len(re.findall(r'\\b([\\w\\-\\.]+)\\b', row)))\nfraction_to_words_ratio = number_of_fractions/number_of_words\n\nnumber_of_fractions_t = train.loc[:,'translation'].apply(lambda row: len(re.findall(r'\\b([0-9]+\\.33[3]*|[0-9]+\\.5|\\.25|[0-9]+\\.75)\\b', row)))\nnumber_of_words_t = train.loc[:,'translation'].apply(lambda row: len(re.findall(r'\\b([\\w\\-\\.]+)\\b', row)))\nfraction_to_words_ratio_t = number_of_fractions_t/number_of_words_t\n```\n\nI haven't consulted the metrics so I can't give the estimates of how they would change but this would be my guess as to why the observed changes are not very impactful.",
    "3383555": "It's similar for me the LB remains the same with or without floats to fractions conversion. However, if I sort public LB by score then I can see it's a bit better for the submission with fractions conversion. @hrithik22122 Is it the same for you? If 2 submissions are strictly equal then the first one (oldest) should be the first. Otherwise it's sorted by best score (even if it displays only a single digit as far as I know)\n\nSecond, impact for floats to fractions conversion on geometric_mean is quite low on a single fold OOF for me:\n- Floats + limited fractions in ground truth: 45.53 (we've some fractions already in ground truth)\n- Fractions only in ground truth: 44.22\n\nLet's wait for the official voice of organizer. \nThere are for sure other reasons for such a big gap between CV / LB we need to discover.",
    "3383557": "lol,yes, decimal places aren’t shown, but the submission clearly reflects the actual values of the three same scores.",
    "3383570": "Nah , when I sort it , my first submission(one without fraction post processing) comes up first\n\nI was expecting an LB increase of around 0.2–0.6 because the penalty for raw decimals should be massive:\n\n* BLEU: A mismatch like 13.3333 vs 13 ⅓ means zero overlap for that token. By fixing it, we should recover the unigram match, plus the associated bi-grams and tri-grams (\"paid 13.333 silver\" vs \"paid 13 ⅓ silver\").\n\n* chRF++: The character edit distance between 13.3333 (7 chars) and 13 ⅓ (3-4 chars) is very large. Normalizing this should have significantly reduced the edit distance penalty.",
    "3383671": "The ground truth uses a space between the integer and the fraction (e.g., 2 ½).\nLet me put it this way, there are no instances of '/' in either the translation or transliteration, this goes for both the public and private evaluation data.",
    "3383682": "Oh, this is really a highly potential point, I can try it again tomorrow.Thanks",
    "3383685": "Thanks , this is Huge"
  },
  "source": "meta"
}