{"id":41680,"date":"2026-07-03T13:27:16","date_gmt":"2026-07-03T11:27:16","guid":{"rendered":"https:\/\/www.graviton.at\/letterswaplibrary\/i-engineered-102-leakage-free-ml-features-from-49000-international-football-matches-1872-2026-and-published-it-as-a-free-dataset\/"},"modified":"2026-07-03T13:27:16","modified_gmt":"2026-07-03T11:27:16","slug":"i-engineered-102-leakage-free-ml-features-from-49000-international-football-matches-1872-2026-and-published-it-as-a-free-dataset","status":"publish","type":"post","link":"https:\/\/www.graviton.at\/letterswaplibrary\/i-engineered-102-leakage-free-ml-features-from-49000-international-football-matches-1872-2026-and-published-it-as-a-free-dataset\/","title":{"rendered":"I Engineered 102 Leakage-free ML Features From 49,000+ International Football Matches (1872\u20132026) And Published It As A Free Dataset"},"content":{"rendered":"<p><!-- SC_OFF --><\/p>\n<div class=\"md\">\n<p>Been working on a football prediction project and couldn&#8217;t find a dataset that had <\/p>\n<p>the actual context needed to model match outcomes \u2014 just raw results everywhere.<\/p>\n<p>So I built one from scratch on top of the International Football Results dataset <\/p>\n<p>by Mart J\u00fcrisoo (the well known one on Kaggle with 49,000+ matches going back to 1872).<\/p>\n<p>What I added:<\/p>\n<p>**Elo ratings** \u2014 built from scratch, updated after every single match across 150 <\/p>\n<p>years. Both teams&#8217; ratings, their difference, and the expected win probability <\/p>\n<p>going into each match.<\/p>\n<p>**Rolling form** \u2014 win rate, goals scored, goals conceded, goal difference, clean <\/p>\n<p>sheet rate, both-teams-scored rate, scoring rate, and win streak. Computed at <\/p>\n<p>three lookback windows: last 5, last 10, and last 20 matches. For both teams.<\/p>\n<p>**Head-to-head history** \u2014 based on the last 10 meetings between those two specific <\/p>\n<p>teams. Some teams have persistent edges over specific opponents that their general <\/p>\n<p>form doesn&#8217;t explain.<\/p>\n<p>**Fatigue signals** \u2014 days since each team&#8217;s last match and the difference between <\/p>\n<p>the two.<\/p>\n<p>**Penalty reliance** \u2014 fraction of each team&#8217;s historical goals that came from <\/p>\n<p>penalties, pulled from the goalscorer dataset.<\/p>\n<p>**Shootout composure** \u2014 historical penalty shootout win rate for each team, from <\/p>\n<p>the shootouts dataset.<\/p>\n<p>**Tournament context** \u2014 World Cup, qualifier, friendly, neutral venue, competition <\/p>\n<p>importance weight, confederation.<\/p>\n<p>The thing I spent the most time on: every feature is computed in strict <\/p>\n<p>chronological order using only data that existed before that match was played. <\/p>\n<p>State updates happen after each row is recorded, never before. No lookahead, <\/p>\n<p>no leakage anywhere in the 102 columns.<\/p>\n<p>102 features total. 49,094 rows. result column (H\/D\/A) included as the label. <\/p>\n<p>Drop date and result, plug into any classifier.<\/p>\n<p>Dataset is fully documented with column descriptors for every feature.<\/p>\n<p>Link: <a href=\"https:\/\/www.kaggle.com\/datasets\/kriishgulati\/football-match-results-1872-2026-with-ml-features\">https:\/\/www.kaggle.com\/datasets\/kriishgulati\/football-match-results-1872-2026-with-ml-features<\/a><\/p>\n<p>Built on top of the original dataset by Mart J\u00fcrisoo \u2014 full credit and link <\/p>\n<p>in the dataset description.<\/p>\n<\/div>\n<p><!-- SC_ON -->   submitted by   <a href=\"https:\/\/www.reddit.com\/user\/Kriish_Gulati\"> \/u\/Kriish_Gulati <\/a> <br \/> <span><a href=\"https:\/\/www.kaggle.com\/datasets\/kriishgulati\/football-match-results-1872-2026-with-ml-features\">[link]<\/a><\/span>   <span><a href=\"https:\/\/www.reddit.com\/r\/datasets\/comments\/1umbo14\/i_engineered_102_leakagefree_ml_features_from\/\">[comments]<\/a><\/span><\/p><div class='watch-action'><div class='watch-position align-right'><div class='action-like'><a class='lbg-style1 like-41680 jlk' href='javascript:void(0)' data-task='like' data-post_id='41680' data-nonce='9de69db8d5' rel='nofollow'><img class='wti-pixel' src='https:\/\/www.graviton.at\/letterswaplibrary\/wp-content\/plugins\/wti-like-post\/images\/pixel.gif' title='Like' \/><span class='lc-41680 lc'>0<\/span><\/a><\/div><\/div> <div class='status-41680 status align-right'><\/div><\/div><div class='wti-clear'><\/div>","protected":false},"excerpt":{"rendered":"<p>Been working on a football prediction project and couldn&#8217;t find a dataset that had the actual context&#8230;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[85],"tags":[],"class_list":["post-41680","post","type-post","status-publish","format-standard","hentry","category-datatards","wpcat-85-id"],"_links":{"self":[{"href":"https:\/\/www.graviton.at\/letterswaplibrary\/wp-json\/wp\/v2\/posts\/41680","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.graviton.at\/letterswaplibrary\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.graviton.at\/letterswaplibrary\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.graviton.at\/letterswaplibrary\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.graviton.at\/letterswaplibrary\/wp-json\/wp\/v2\/comments?post=41680"}],"version-history":[{"count":0,"href":"https:\/\/www.graviton.at\/letterswaplibrary\/wp-json\/wp\/v2\/posts\/41680\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.graviton.at\/letterswaplibrary\/wp-json\/wp\/v2\/media?parent=41680"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.graviton.at\/letterswaplibrary\/wp-json\/wp\/v2\/categories?post=41680"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.graviton.at\/letterswaplibrary\/wp-json\/wp\/v2\/tags?post=41680"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}