Ë
    MµCj2  ã                   ój  — d Z ddlZddlZddlZddlmZ ddlZddlZ	 ej                  ej                  d¬«        ej                  e«      Zddddd	dd
œZdede	j                   fd„Zdde	j                   dede	j                   fd„Zde	j                   de	j                   fd„Zdd„Zedk(  r e«        yy)aJ  
Crop yield data preprocessing.

Cleans raw crop production data (ICRISAT VDSA or APY government format)
into a standardized long-format table: one row per district-year-crop.

Usage:
    python -m ml_pipeline.preprocessing.clean_crop_data         --input data/raw/icrisat/yield.csv         --output data/processed/crop_clean.csv
é    N)ÚPathz%%(asctime)s %(levelname)s %(message)s)ÚlevelÚformaté   é   é   é	   )ÚkharifÚrabiÚsummerz
whole yearÚautumnÚwinterÚ
input_pathÚreturnc                 óJ  ‡— | j                  «       st        d| › �«      ‚t        j                  | «      Š‰j                  D �cg c]0  }|j                  «       j                  «       j                  dd«      ‘Œ2 c}‰_        d‰j                  vrLd‰j                  vr!t        dt        ‰j                  «      › �«      ‚t        j                  d«       ‰d   ‰d<   d‰j                  v r"d	‰j                  vr‰j                  dd	i¬
«      Šh d£}|t        ‰j                  «      z
  }|r$t        d|› dt        ‰j                  «      › �«      ‚g d¢}t        ˆfd„|D «       d «      }|€%t        d|› dt        ‰j                  «      › d�«      ‚|dk7  r9‰j                  |di¬
«      Š|dk(  r t        j                  d«       ‰d   dz  ‰d<   d‰j                  vrFddg}t        ˆfd„|D «       d «      }|r‰j                  |di¬
«      Š‰S t        j                  d«       ‰S c c}w )NzInput file not found: ú Ú_ÚdistrictÚstateuW   Input has neither a 'district' nor a 'state' column â€” cannot proceed. Found columns: u°   No district column found in input â€” using 'state' as a stopgap district value. Model granularity will be state-level, not district-level, until real district data is sourced.Ú	crop_yearÚyear©Úcolumns>   Úcropr   r   z"Input is missing required columns z	. Found: )Úyield_ton_haÚyield_kg_haÚyieldÚproductivityc              3   ó@   •K  — | ]  }|‰j                   v sŒ|–— Œ y ­w©Nr   ©Ú.0ÚcÚdfs     €úVC:\Crop_Prediction\Backend\crop-ai-system\ml_pipeline\preprocessing\clean_crop_data.pyú	<genexpr>z$load_and_validate.<locals>.<genexpr>D   s   øè ø€ ÒH !¸¸R¿Z¹ZºœAÑHùó   ƒ—z/No recognizable yield column found. Looked for z, found columns: z?. Rename the source column or add the new alias to this script.r   r   z9Converting yield_kg_ha to yield_ton_ha (dividing by 1000)iè  Úarea_haÚareaÚarea_hectaresc              3   ó@   •K  — | ]  }|‰j                   v sŒ|–— Œ y ­wr    r   r!   s     €r%   r&   z$load_and_validate.<locals>.<genexpr>S   s   øè ø€ ÒJ Q¸!¸r¿z¹zº/œqÑJùr'   u;   No area column found â€” area_ha will be absent from output)ÚexistsÚFileNotFoundErrorÚpdÚread_csvr   ÚstripÚlowerÚreplaceÚ
ValueErrorÚlistÚloggerÚwarningÚrenameÚsetÚnextÚinfo)	r   r#   ÚrequiredÚmissingÚyield_aliasesÚfound_yield_colÚarea_aliasesÚfound_area_colr$   s	           @r%   Úload_and_validaterA      s-  ø€ Ø×ÑÔÜÐ"8¸¸Ð EÓFÐFä	�‰�ZÓ	 €BØ?A¿z¹zÖJ¸!�!—'‘'“)—/‘/Ó#×+Ñ+¨C°Õ5ÒJ€B„Jð ˜Ÿ™Ñ#Ø˜"Ÿ*™*Ñ$Üð+Ü+/°·
±
Ó+;Ð*<ð>óð ô 	�‰ðCô	
ð
 ˜G™ˆˆ:‰à�b—j‘jÑ  V°2·:±:Ñ%=Ø�Y‰Y ¨VÐ4ˆYÓ5ˆâ+€HØœ˜RŸZ™Z›Ñ(€GÙÜÐ=¸g¸YÀiÔPTÐUW×U_ÑU_ÓP`ÐOaÐbÓcÐcò M€MÜÓH }ÔHÈ$ÓO€OØÐÜØ=¸m¸_ð MÜ" 2§:¡:Ó.Ð/ð 00ð1ó
ð 	
ð
 ˜.Ò(Ø�Y‰Y °Ð@ˆYÓAˆØ˜mÒ+Ü�K‰KÐSÔTØ!# NÑ!3°dÑ!:ˆBˆ~Ñà˜Ÿ
™
Ñ"Ø Ð0ˆÜÓJ¨,ÔJÈDÓQˆÙØ—‘ N°IÐ#>�Ó?ˆBð €Iô �N‰NÐXÔYà€Iùòs Ks   Á5H r$   Ústd_thresholdc                 óô  — | j                  d«      d   j                  ddg«      j                  «       }g d¢|_        | j	                  |dd¬«      }|d   j                  «       |d   d	k(  z  t        j                  |d   |d
   z
  «      ||d   z  k  z  }t        | «      }||   j                  d
dg¬«      j                  d¬«      }t        |«      }t        j                  d||z
  › d||z
  |z  d›d�«       |S )u–  
    Drops rows more than std_threshold standard deviations from their
    crop's mean yield. Implemented via boolean masking rather than
    groupby().apply() returning filtered sub-frames â€” recent pandas
    versions (2.2+) can silently drop the grouping column itself when
    apply() returns a row-filtered subset of the original frame, which
    would corrupt 'crop' out of the output entirely.
    r   r   ÚmeanÚstd)r   Ú_group_meanÚ
_group_stdÚleft)ÚonÚhowrG   r   rF   r   T©ÚdropzOutlier removal: dropped z rows (z.1%ú))ÚgroupbyÚaggÚreset_indexr   ÚmergeÚisnaÚnpÚabsÚlenrL   r5   r:   )r$   rB   Úgroup_statsÚmergedÚ	keep_maskÚbeforeÚresultÚafters           r%   Úremove_outliers_per_cropr\   \   s  € ð —*‘*˜VÓ$ ^Ñ4×8Ñ8¸&À%¸ÓI×UÑUÓW€KÚ?€KÔà�X‰X�k f°&ˆXÓ9€Fð 	ˆ|Ñ×!Ñ!Ó#Ø�,Ñ 1Ñ$ñ	&ä�6‰6�&˜Ñ(¨6°-Ñ+@Ñ@ÓAÀMÐTZÐ[gÑThÑDhÑhñ	jð ô �‹W€FØ�IÑ×#Ñ#¨]¸LÐ,IÐ#ÓJ×VÑVÐ\`ÐVÓa€FÜ�‹K€EÜ
‡K�KÐ+¨F°U©NÐ+;¸7ÀFÈUÁNÐV\ÑC\Ð]`ÐBaÐabÐcÔdØ€Mó    c                 ó  — | d   j                  t        «      j                  j                  «       j                  j                  «       | d<   | d   j                  t        «      j                  j                  «       j                  j                  «       | d<   d| j                  v rÂ| d   j                  t        «      j                  j                  «       j                  j                  «       | d<   | d   j                  t        «      | d<   | d   j                  «       j                  «       }|dkD  rPt        j                  |› dt        t        j                  «       «      › �«       nt        j                  d«       d| d<   t        j                  | d	   d
¬«      | d	<   t        | «      }| | d	   dkD     } t        | «      }t        j!                  d||z
  › d�«       t#        | «      } | j%                  d	dg¬«      j'                  d¬«      } | S )Nr   r   ÚseasonÚmonthr   zO rows have an unrecognized season value and got month=NaN. Recognized seasons: uM   No 'season' column present â€” month will be absent, defaulting to 6 (kharif)r   r   Úcoerce)ÚerrorszDropped z/ rows with zero, negative, or non-numeric yield)ÚsubsetTrK   )ÚastypeÚstrr0   r1   r   ÚmapÚSEASON_TO_MONTHÚisnullÚsumr5   r6   r4   Úkeysr.   Ú
to_numericrU   r:   r\   ÚdropnarP   )r$   ÚunmappedrY   r[   s       r%   Úcleanrn   x   s¿  € Ø˜
‘^×*Ñ*¬3Ó/×3Ñ3×9Ñ9Ó;×?Ñ?×EÑEÓG€B€z�NØ�F‘×"Ñ"¤3Ó'×+Ñ+×1Ñ1Ó3×7Ñ7×=Ñ=Ó?€B€v�Jà�2—:‘:ÑØ˜(‘|×*Ñ*¬3Ó/×3Ñ3×9Ñ9Ó;×?Ñ?×EÑEÓGˆˆ8‰Ø˜‘l×&Ñ&¤Ó7ˆˆ7‰Ø�g‘;×%Ñ%Ó'×+Ñ+Ó-ˆØ�aŠ<Ü�N‰NØ�*ð 'Ü'+¬O×,@Ñ,@Ó,BÓ'CÐ&DðFõô
 	�‰ÐfÔgØˆˆ7‰äŸ™ r¨.Ñ'9À(ÔK€B€~Ñä�‹W€FØ	ˆBˆ~Ñ Ñ"Ñ	#€BÜ�‹G€EÜ
‡K�K�(˜6 E™>Ð*Ð*YÐZÔ[ä	! "Ó	%€BØ	�‰˜>¨7Ð3ˆÓ	4×	@Ñ	@ÀdÐ	@Ó	K€BØ€Ir]   c                  óò  — t        j                  d¬«      } | j                  dd¬«       | j                  dd¬«       | j                  «       }t	        |j
                  «      }t	        |j                  «      }t        j                  d|› �«       t        |«      }t        j                  d«       t        |«      }t        |«      d	k(  r*t        j                  d
«       t        j                  d«       |j                  j!                  dd¬«       |j#                  |d¬«       t        j                  dt        |«      › d|› �«       t        j                  dt%        |d   j'                  «       «      › �«       y )NzClean raw crop yield data)Údescriptionz--inputT)r;   z--outputzLoading raw crop data from z8Cleaning: type coercion, season mapping, outlier removalr   zKZero rows remain after cleaning. Check the source file and column mappings.é   )ÚparentsÚexist_okF)ÚindexzSaved z cleaned rows to zCrops present: r   )ÚargparseÚArgumentParserÚadd_argumentÚ
parse_argsr   ÚinputÚoutputr5   r:   rA   rn   rU   ÚerrorÚsysÚexitÚparentÚmkdirÚto_csvÚsortedÚunique)ÚparserÚargsr   Úoutput_pathÚraw_dfÚclean_dfs         r%   Úmainrˆ   •   s)  € Ü×$Ñ$Ð1LÔM€FØ
×Ñ˜	¨DÐÔ1Ø
×Ñ˜
¨TÐÔ2Ø×ÑÓ€Dä�d—j‘jÓ!€JÜ�t—{‘{Ó#€Kä
‡K�KÐ-¨j¨\Ð:Ô;Ü˜zÓ*€Fä
‡K�KÐJÔKÜ�V‹}€Hä
ˆ8ƒ}˜ÒÜ�‰ÐbÔcÜ�‰�Œà×Ñ×Ñ T°DÐÔ9Ø‡O�O�K u€OÔ-Ü
‡K�K�&œ˜X›˜Ð'8¸¸ÐFÔGÜ
‡K�K�/¤&¨°&Ñ)9×)@Ñ)@Ó)BÓ"CÐ!DÐEÕFr]   Ú__main__)g      @)r   N)Ú__doc__ru   Úloggingr|   Úpathlibr   ÚnumpyrS   Úpandasr.   ÚbasicConfigÚINFOÚ	getLoggerÚ__name__r5   rg   Ú	DataFramerA   Úfloatr\   rn   rˆ   © r]   r%   ú<module>r–      sÊ   ðñ
ó Û Û 
Ý ã Û à €× Ñ ˜'Ÿ,™,Ð/VÕ WØ	ˆ×	Ñ	˜8Ó	$€à¨°aÀqÐTUÐacÑd€ð> $ð >¨2¯<©<ó >ñB §¡ð ¸eð ÈbÏlÉló ð8ˆb�l‰lð ˜rŸ|™|ó ó:Gð2 ˆzÒÙ…Fð r]   