From 7ea5603ea2d40504200e1ecdbceee520e647021b Mon Sep 17 00:00:00 2001 From: Ligo-code Date: Sat, 23 May 2026 21:21:00 -0400 Subject: [PATCH 1/3] complete warmup section for week7 --- assignments_07/outputs/my_plot.png | Bin 0 -> 27031 bytes assignments_07/project_07.py | 0 assignments_07/resources/bike_commute.csv | 161 +++++ assignments_07/warmup_07.py | 764 ++++++++++++++++++++++ requirements.txt | 5 +- 5 files changed, 929 insertions(+), 1 deletion(-) create mode 100644 assignments_07/outputs/my_plot.png create mode 100644 assignments_07/project_07.py create mode 100644 assignments_07/resources/bike_commute.csv create mode 100644 assignments_07/warmup_07.py diff --git a/assignments_07/outputs/my_plot.png b/assignments_07/outputs/my_plot.png new file mode 100644 index 0000000000000000000000000000000000000000..86dc1d73c6153c032ff33c0f811b5575ca0e0252 GIT binary patch literal 27031 zcmdqJbyQW~+BUptq!E!2*a(UsB^{E2fFh+*5*t*cySqe?6zT4cjdbn}BGTOr(%oI( z+@5oubDsBo#y8$~eE)rp!Ox$2ZPuD=t~sy!zOU=L*H>jlSwcK&JO~6r_(D$lH3Wk0 z1%aT=;bMbN`1+?7!C%65&o%7cSbnl|)VDE!DCpZ+nOWMI8Na7@FtD*TwzS~k;CjL# z$VP8uXJ=(A%*ko~U*F)cv@zst(z5RXXSro1r)dj;5bL9U&~n9dj3E$Xj2F_9Z=Dji zrf<33S-KM6wXD7+>qWxhEdh~SdVNQlL^4;d$SajOfSw*#Ln8HM-V~ii-`yg-cPuYk zG~P;>sY+wMCY21u9SOYkU`iJE0oPX&+yhVdt)nqn_*S;cDTn8>scr4`gW-~ z;$pk5^ke&X?oOpd!L(3$7nj=D7uME4Cg4Vwe|YOwl3f)?inJ)g%@3;Ybrz1|uvtCQdopL+bvqw7$A0X0u)^CI!$cUCI-(+C zbX`~&(C9;h#yNIzF-u?skRl4me)YZ9!6UTUj8m}?`xMXX8kblFKRH8sYSm(Gp#e>L zeAnY~!-x0pOCqk0wenO8qMJ<(dlE^@m>)m>ZZe$zT=?D{rugi3KYWTz@Xs&Vu4nrO zH8x9uZ}Z=__t_QUwc;9lg(f!TYy3!$m3$bHtQB2=CJk-8FZD&PJD%?$7ne%*tGIMU zCna}?j*#bdz1{YdCbfWt*@d6&-k;}jPaTWP&BkA2g)7AKhCfPid(D0_8YX_7yB`-B znaL4bq*WE2lkC!=pVZ)b@)GHBHH3Hz*VS=3*=C=0I|ykOcDZSRBhFtkDJ2&UNGb2F zW`w^(o%Z>DPyL?*2KE^<)3FoQnqX}H$U+?+X~=_$1E%?x1mr@$0=)n8c~pQv)E_o+{C#GlMi%TO~ma39c zA=G?}#dX`?Z!TBF#iAY^+pZ7gnIs7Ew{ad%B9xfp)!}v9!WEv(_(MO_)Cj~*=P;sD zP&f19;%F464tKw~I(h%u7l%0CO%TUZ8Lr#F-8tjDLTk^lezx78;ol4&4ih_GL%ORMSt&U&96GO4nj#)4xlE5B+%JZlgL|+KGUwXGmbm_HadH`EKsW7jD4i}^GI@59rH$z&ZXvJLSW?D<@1C|VrLu1 zG4W`bknOTuj$+bKO~3|bF-3xa>#UMq~W+T5M~vS57EfFZY1g}f@tXZdXi z*+}mayV&H}^Cz9v7oCorTpunNCU&T&Xr-KOii?Z8&B~^Z5s-@aXz`~XzG}(gekW%E zXJizs`gtO@5B6c}&lf)U!W_>|u_ln7p5#U$_XHG+YnG{p!!~+7ugfmMiu2%eoWsYW z;J9>o_O6Z7#|LZ=jU+R#Mx2c~sjor2BP}iDk68AHvg9fJc^@gnXCh*)`kOtFd%y2J zZj`Y7Fqo~N8qo!|7DEsPry7*st$#wR!X&D;k%4wo03o3qeag`2um3xkYW-KMA0b5u z@@mWS4>2Y?uCler^{&5o?mouV4f5t{hudaRZf&v(ez_Os`L2CTViaG;)A|^ z+T((o>l5U9(Ak%J7Rs$86!zN68zV&p@sCT~wNHi)~{ZWFT@Tn^ewbXnt(HuViw2upj0 zWv>%2Xr2CY;YTZ-?HGUPcwd^7DoNNj!D(r$A(albm4%*`muarJRp>lV;0fasiCw>g zvu=Uex0B749@l4vnh3X=+E1HPHE)C+Hdl|T7h>%74?WOV$uOoNpja0lp|=FpC&CM$l^CXV7h38fKAp9zE7G0VGQ`}#p}oqt!{ zb8eZ%%@22U`?NE7Efhb4f7A`qeJR{MSdx$|*uu6B*5C z0o*NTe$cB^4@wcIlE)aA%!jhfh~{L^YiIlJ=JRR~hHf0Fn=)-@Y^vt3sT$io^NgdN z*;)OwUth_F_jeE%J(X-iLYnu4Y%;gb0?Ap2gl9c2iX8SAUu@NG6c4_B{knW}(`-`M z^X6i>1N$Ex|6 zv?5M@m7CyF>+ViS+ZnLxHg>Ozb?Qv4t*s|c_ZG}MIbeuFK8q=hIslz!ou#FvLl+U> zzn9n7i%jmv@fhYW(F)u3ap=^3)Dp8>RgQw=Q*ew$>x)Jrc~)#Z0Q?Qt0$6g;*jY*O z909>HD}?sxi2hmLo1F5`pFeYVCUwX0$Y#}j>W=4zuNiRbHA3vQMVk=A1?oys5kxB( zp%*?__`H_N#m5&LJ>VS0QJgScQLDscxBVC9&0h%LfymbELPbSoGKpH5YVY2y+aOb` ztE;>C>D&)h*u}(V<^`wiM|IX?1)H72_M2-K>qEfej}odXX;|(~Fh!mZ!#murmA@eR zdM6c=goQRw(w_$n?SoL$mZrVcJN{5UI@cZ*hbFl*y-N#q8){KN8oji*SS>1WbTAXM zu$1ri=FOYoi@||`OEFIX^EqPvf;;wFm1ZhyHNs-asoJ{;0B#e}I`vK}rhTd2{7$=b zUd6EDV~Q0>@z>*T>h&jOy<)6o9u+3TZjZGbTeHA?On}*?(Uq!sKf9SE?x|Ymv9f{Jfdq?&Xr-WT1B0IUcxAKJeq*S%SL7hL z+2v%$6@D(((bZMX-t2P9@dNf{$C&K~q;G$+%6X-b%@($AEJn3$Lz;z72IM*OmO#cg z`rdmf-Ydy7?awGA^|X|6&7f4;*D&$&^oF##zjkQ`lr^Hlz3 zDi5r5&6ew{CvK-!s`z?H8f59NX%BO*zGOF{$M|Fn`+m@tGpp96jVxJe0c^)5E;agA zE5+6Jlw=V>{N1gDER_VGc;fm`cZN7Jut|`S{Zl@$sN5EbqR;Mc?sz19m^?k&nu4`n zYfPThJS)QOAVLnQ1O+Y^OQa1t%E){XUg?|4gHrJBrDmhAaP1pfUY_oGm}6An9bW|~ z+xi}K53Z2I)0l`>f{2V$M=&dzisSW@(qyPoh%GBTdzOMGXd;^cW)&vyjF+{-4{&JL zRSv#TI}K5;TTM%b=+J*763Y=D+D<+0zP8=&J?ixERN3%8`X-*~zjCZ>``LEXC>LuW z!N$ItZk5lH`OVONswaK1@o{StZDH61?-1Tv3YmzN#RsTWv1@H{T47(j7q!mCgt|6z z6HbiKHl0cut^S>{tn8ots^KmlW$v28cEZc9hyjto`*w!&R-{QAEUM2N_p;T zbo=)T;k4W4(b`~ZngbLumLf>AH*??|~4ZL%lvDVy_)KVNKj z4lxpo9)1&6>5B~Dq;#zo)T=po-agxNasG87Z;e=z0a3pvY4;^aki%nj((4m-lI@c` zPm(_3X6Kp$F}3S@sM}i35^E&;Dom!wN^%#od*$@!F6QtJ%x%x_{Jk`knKy-@MAG(~ z;?Bw!d=Ye-##y7^)#vL4Epn^szS+du0VJ5|#FX)}R(rB@eje&$oQZX(lVrT6X`aN0 zsYFjoK~H@g8|~etd7=SX=%WiS`q*ApgxBuF)wP2_ruX%|Ld+v|WOMe6_pKz}h@7hk zdG>5|j>a6w_TKA}G}O4;M!|XhNJt=NAvSfK>)Qs!xnp__RQ$CSt<4PVf>YBotO`Dr z73AT^i8xI^*Y}04UoT>Gd*VL()SFyHx82dC^F{=5&nih0CdQk=;Pumx@~ngy;$AaS z?PH6v@f0erK8tZ}qbhbv*HnlBZ#a(sXkZ|8NOfFwHtYfIvkP9xV{#d*s0;iH%xd$IPPgveSq zo$Y+fXz?A#m@hmR_<@gH*l(ZL3F6G?-E6QcS3}Z}1L8MlO2h4x6 zCOV|S^L&S2n_}*tm-*p&=rB-yNNgiati9B{w%ec{RxseKu z_MJ(2a-Nei1X#Bq?&1g2p-~emfB1|jUv2$t(f&LykZybTL+l#YE(>tI14M_+gu+Vf)Gb`dARMbkw|6OKb~`s4k+KYS>$GoJpyi&F$gw+0=re=J%|o>x#zW1)+t>`_S(@|ierNf1_S_ml~ox)fP|vBenWO5{1d zm|J&k9OhGp>zD2DFBXaHjWs&ZyE zC82~XgdU{x6bxdy$8VsicOmZB+6wd`{aULOOZb?M)d6~#w`GKD&Q-l(v_|}mhjhmZ z`nf6+2N)b8Gx5)xZ^1ZLi9Zo%JV-Ky90|=Qa%!LQgPwt6C9vT~eB4 zVf&6W&bu?dy}~9dhbq;lq)R5q%Q)=cs%Jh0w<_F)Jo`kxY(tHVOE}*gYDv#FD(@Yg zFPV5+_vpPX7PnHYd3>T*-EmLmLwepsa@|5GG?r2{lv+f+bvovNDn-_AP$D(>E`Fk8 zk2r~Y!$`F-ykGW?cN~$z?rE@K^U7_;zCF#+wBMvUzLzXYD+km?#69i{Q##P8EsWxe z*r^#hlQ2@b^K;Qxze}_U=}pA?YUsLos-L8eL#{YC3sxH>BPcjrD3yI+T&vQ7*WS6? zvX30yyfegc6z$x;>eYx*L~}kFqLrk{W|hQ&xk=LZH6N0kn{A+Sf_3fSqbJgI-Ko`9 z^P@EdLI{RiZ^TlyNdok}FnqWo{jcD166|+5gkzVnmV%gg+!0)Edl|thOh%t1Q-dK? z20o=DoXoiUeXjyJAGp)8@#4&XDf9@zBa`(Ev%S@G)*~e|>QI6Gs{o!*5L4 zJ0&++ffHz#?_@nW-B=h-xu{Lzt@T?|$$6pk453Uim;||bBP-NW=Da2peImX+t!)$? z1WBEwa;aHnMoGmkm|ob)65QxCmah$NYqg*xj|Krrc!oD4FN+=d|1u$2O|T*FQSCY$B%Z zdj7?h)FAMkOkz-5Oo7BCE(-O8BpZDW(BIV2i{n!7Q5_ldNMG0z@<|%W{H_VKZFgRO z?5W=)Idna5>)Xp7iq@k^^Wnngz-~%K((3@fdp`Chc7?&Ie#j0U!Bj~KMlUU<+vyMQ zc-hR16U)IfuywO%$#F40Tj^$GlcpGmrrINx|0K4a(1<-XLT`0U5+B@371>+3y+U8i zc#7te_cqa#L{tZwIA1SW#89kts@PmXkwWqIQ>yX!?3x#|T>SIZLFG$OL7B7(Wy|O3 z)Kwl&&R(lsfVzu|p!l`aC093grTg)w=F#~E z99f**`{LEBx2;6R;(NcyIKn1>hujJFio4LE0{@QSiW>_qc0FZpTx98aMAW)AkAA$2UEc*eyF1O;~L zcEtD|M%3lWxCwdAxbBGBU#cVnCU>DQGJ?{@JPcO_hz^{Pt6I=%1-%ZiB%1 zPp!j;ny6H%<7s7ok(PjclAS=(*8_W+Cxjh`;6h&r@8vIi=B>1x-QGV~-aenp-{flO z2OQUM1@2PNn!T0CR?c_y%!9EHVNvZdbu1QBTz{T!@dx?C=#q>StJB6E5dG;?@!$I$yPTg8$3wR)WuHsm zA?Q3p3x05DD&=pYq^)54OnSuo+Mklm2hp+$_4jaEh54EKiiPu^mrOYBI(|WvZ!aGP z$*fwgnt#JO&lFZ-l^$uaWpW~bG~=GQmm}{tu$qNGD&;4bXrjMGP$H@PS@DRH-s>WC zoox3#{nwnQ@p{69Fs7=oyOc@m=DecVs&mPG(jtbzO=4d9pU)BnT8K;ldA0P+tU)C z3Aq((#6MJvShg?tA^j}CKta0S3ynV9Z;3pnZE?u%MQ7U&_Kp6>FRr-{U0wvhDf{c= z2s`5g(r+brePttPnyu_ABkJ6Bd%Zpru+(0tF(Q3O0jNBh)hxI9JhS4LOK5t3h-{dt z{M!G-&t}RH$PWh1hUb1Q(%xeLX)9%7D0>f)`>4`x|3?%Nigy1O_{8?_CoC_9hrPn~ zt*wbV_h>qWsAIv%mRlg-+I~KnsM6}J1sA31{EGUJd=K)3on2uYabaORlpFr5rKKG} zl^I;8*6zipuGsqkjUoUsl4mayOnLv2Vq!^AnL+2>BQfnLe*tuq~oC)?stM)Q%zY5iJ3V*~mG5v_3BYJY}7U#cV#oScH9 zGg;L2BY;+uq=S{-&PbLwnVvV-rKY1C6+P@#Qw)!klI@(JBeUhk5mQbJA#_!u4x1V% zvT@7Vt$syJMf#K9vM&EwY2nv4VFo(L@y4iO)tkO6P%HWlX67Z}=M9#-;_`ooqeQt)23?HRFu*G1ig`=<6Gg@Inbv9>p6xFU zR9V7|r>cLnmIHWTYHx4Phs^^b{%tJ|5zRL~(~)ZT-j*PWVJUTGWs+YrDBbV5Oc$n? z(F85`#{i>vCixf+`G8mZLf$OOIy?y2Wk06UJ?Y)ly?r{ECNMFN_>+N~Gx z)CSj=XOf#(DE)9TDky7iXLr6iUn#Y20!hxQCinh*I`xA;a6p9R-th47Oc(opde1Al z5tbnvcHTe>_EWlC0SRjp3{VGVGxeHSPQ|I8NYLoBzvcmvXm;|2l9KvO22)>BM-*!T z4(b|gK~98vfJMeb975PwS>>-nR1mn=+YKkWd`tM)W|7g+9e_j|%E-+8bw7y{dHK5G zBwUt`r7Q$6vhoT1<{yD-vn~|xoZ@kAJl^11_0iC zVq$xJtoZ~Rx{MN5bz2aMHcVm<)R+Ma6$bwO(k96%4E{j^ z7wEO)Kl+FD3TcPo(`ombM6Pq zf3z{F%`{jXi=Ln9uXd;3W|NY>V>*WUN_r8?2WcK>NE4z-a?TQQs4hD<<3d;Rf*Y!x^bc7fR1wlzjQx zo^LM0O*^sKMx=Fr*`5k=TV?a_AuO2-2UE>aeX8USLSO;@;e8@4t*e(G6ZJv301di& z;Qt9WatRKg0?Q&JraQr9KpO#SnE&S@Ie?JttgQBF$60ou7Jjd;7Syb=NZP6Zo3R}v zahW=G4zG*e@m2HyrE(F_(nQ9*rX#N!Mz8lS4|-jvwt?ij2xNhXU%#aD0250*Pi>j+N0QNDK8TP8(zyZ}yL?cLlb#cO3whd_I z#l`lB%2ohUA{LH7s@&geg?0j=f>W!?LRE4pRnnJWT7V(l>S^Q~<^U*QRR9XoPH}Dt2Eiw+Z(gg8Pf@B z4c4kzkGgl2?_2O{mg7wq6D$$o;72Ieoq++bs{YUIxWsr4V=AFagKBjy3w8geaU`VA zRS8p(B=u=us{mh3)Y-onv^)iJQ{B6qx@<9=Fo%TS+tYQb2R1;5GwYlvH_kxs02|w% z_eNSe&1=q5YgyCZz)6h7o)bSYARUg~2VJf5|1S)kh*nA#(X^UMQB$_=QA zVS>vX@vC+NVIV4g2cmt{EryLzI9%6uv+1WKm4cuPvGi*PogAxMXvqFY^k-@~3BMFu)xbA&FT&L=a+`$Pr%O+z8gpeEs|M2dJ1 z2Fmv%^8rGc)i>421Ow25hsBTfH(vf9dV>@(KHNv${?!+*s?==bN@Tb2bnw-+%!SzR z*Sg(5W4KqW@1t@{BQ)ohhulJZIs@D`+KAA}u6aeq*v}J%7x#)TDCM2`@L?T^t$qPr zp$d(6y*TSYESN@wjxuF!lk@CYmT!MxTN-9_W3i>9VBmg=?ZxEJ#5QCXX%ataze_#R zw6)Szi|nrs`w^*Lwwu-EtR2t>9%($yx!@Vz2_65%wanmu4u@3_DiI+MBE~`ODuLzk zRYdnf#10L+k$rW}QP30ga$? zgTvau!#FeZ{_fh$#nEQF#L9aL`2pg|AKr+)gs3gt~^Ek^&6t;iK4i$5& z7C9Pv^E&VK!>Y-x@vDpoa@r*FB$DPsa2SPhoSXhsh&d$zWmwi(B>aaQ&DyHZ{ z7JXf%X&S$IA~5kE;|~`i|21|}#c~0;OHTd*ct1V@py4*^qxT;T3<@#;n~vM{*sP-G z>|n*9Ba&qRcofDfOk>`y0A@LoRlT?kfHViYR%PT4@<#V}C~d)j`@A28h{e4Pc8!w8 zV-c~LlWUtFxG8$O^0f4A2g=(G9@RG*Dw}C}gu3 zz-vD7cDw#S@u)8=GqVlYp+2q+Wa(l^V7dX?;E9nE*bf2krMiyLr0vbMwG)T(RXZ1i z?bnC);z#mT3oULwc>z&IUFfr8@o=NSF=Dm5O`QyD`BQkV?su6?RPS?1*2Pr8j z=}Q}72e~!Y2g+aGdYgb6QGL^Nd604w34BF-qtH3~t^@x$V%zgHap?IKP<+P*24qj) z)AH^VYuCIEp_kzS)Cz(XMK-BQd=>M0lcN|#C+Z>xWOPPlJk;3k;z77lpe8$Zf6W?U z|3q*QQ`LK)0&~Ky*bp|uu2jSmbU6`V0NX4H6>H@)A?0e6f-VI$?geoozM`;EUaaE8CncE2CU zf9d8&j3JTE{N>sp`Qyr6zr_Zb9IC*2>A<*SKjw+u19CBtlBR3?1E_Z;nP*Jx;?vsX zGh>Zm0qKO|uzBFZWDd-FpsJpf(%ISqV!bWf3GY5{?0*b7-Ke=&{S+8518peJM*aoj zX>R}PKyY%rZ=w5#zY)~8LOYMa;$G2yvco6bt-Sc~byqp3wA#5FxfL~Ve~Ng6l~q2)l0v*_FXcD@%F!eFHXF2 zx4luls{QPbe5FCf0&B;4Yps05uhW3#kO1a8POR1RR!r76!5WA+l?KUCuNBJJS)mj; z+iA5nuT}!G$O|R*Z!4S;N9VYEYmD`7P`GWAT#n;-yRJ-;hKrD@tlrsGq~FAloA+jt z7(yS%LHAnG2z3>cVJOnW`W|D{;aO6@CC|3`DBfKM%hPM=rm~>n0RwkXDz-PuO9;|e z79PW%NKpRg832+Z92B1SqQtpx9p3C?3S5RtziYyKz}X*pqvt#sqcNs*cy{oyH~c`q z6Q5LN=mc%5$}$CTd$J&pe(?0E7QjhXfK%ZBU)sVMWj_M%=I`am_wQ_Id#`ez2-4sF z_uQDn-s_7$V>?<{=*~ zuxPMooFJ#b&?*O*5@0`hcXTC{(zw6W*$M1>SpWusi}p6x(_{$%H$QT`(DddM8c|&8 zox6AQ;~PPQd~Q*<#cw=bnw3xJ27Ij{qOVcNbmvT^k@6~Nc?J-{dCM?NUx6trUur5; zF(=JL0O9KHygHfXoUXNhL#zX`?|NVc;eP)MH)9`_8%g)~;gDQiDJF~L#d~d~!RVu- zqN4-Hf1o0Q$|j2*w6XC5!e9|>8$XL5O8YcB-XFEA+8!u0#vWsNL+Ma)0FV4S2GisU zIEof~p6>7p|IO|0+02hBjEdjQmJS29ntEE@zud0^+uz|L~A zng5C9y!oT`0kGlbtHU_}tVR#!C|zjLpl)m#j+O}gx93RUfHWZqxH6wZS9$yP0Wdi! zN5~QbYJR@r&!6|>Wrks0$)Z|Cw%3==ae%@;+?j;|W37P}aTD~M@%>wFpWiAOGN|CU z2x?vXx@k~lb$~d*iE@OhMjTmsUT9Ug2b=Jh{S(Z8F)b1fAB$$)YvXT@OGZv*WpQ#s zAHB|hF{D&$7HvE6>cS+e^g(H@7moL5bQ8#Eu}*;rmFTojF^YWyRTGMRyF5YOM47@H z)Ywr*$6?_V_v5(QM_lKO=_7rbfOUKD@L?Wg4;AG)QCvQ%5d8>3l?{+55{k);b*;a? zQdEd352l&qo(dzcT6}3`3mwuJL*&@;^5*$2mL{4WNbv0IUn@*n)b^1=Lu>~9o znPj(Y7 zN_pJz@Jj>GN`UPJs>vAft2L#3DoV-ma^u0bWRKC|&Iijcm{Vuz&M^TWI&}9(im-it z*IQo7SReX3@EV^bfDxrw;kD;S8$dHg=)4)mfITh~i8XKWkxLo;G@;HK^^lZ`1XBF2;^j4PU7@53)keS6P2lT^ z1kT(xfmt_38L7Wz@z7jEmVC_12W;Ba%BoZwVSqG~W0OdsQ!2F$dT(<$l8Hg1wmeXAhS9>haz)cS^1H>w|Gd}yf@^8K3H`-SrO_U&a zx|{J|mGw|L`*E$qRzAAW;l`jlFr@+)89B&BgF1Pi+OIzVlxQRnug0Tk1+64dM#=$O zoC`ltVw2*({+?ezG`*C>wEf$bjrhn2scb&0ReNp0GqrYBHk&+4X>sG*RY1*JkEcADPn z6=1g24-H`6khf98_r&{bzzz?aytIG5UZ@ zY)*u%^DDEOU;VAvbPB{2$gTc~NMA&C828^VUVqLvyL|C9(iv9(c8s+Fx)pYhnx?on z&a^msnxJ#eH|j{d$GyL77y?!A6GONj5%>p}a6A6tdi+F8&{G+nv@iar7WZ zyKi%Y=5np%a+aggG<%=;VKkY_<{x^Jpl2yeN>);*R#~Rfy1`Q^%26TW#{S&I^UIwG zC#}}eBkd@BK!caAw4HHxcD=n$1J^Irg9Oxh-JytSkh=yZ`*pr)pj$sy*RfZf6UzK1 zSG-U=8$m=n(;W4`&)6SxTG*(qDhKVo(fw080$l4){}@X+7DgkeeC74^_2ZUs@%G}2 zT~D@OgYK6`TemI@9okP> z>-MJD)D+o^^364qvpMonkG)!gzGQpEfRF7JJKL*zg8;^7MZn3DnIV`7p2Yw7m!j63 z^uLeP2U(Z>AljKz2JI;vtQNg;xGhO;7&;LX>Isx~HF%kh78ju-FE8EfV~8*4OUldN z4&#MF#dUSvk5svymQBf%&`$K{w68k%puZRmsHdzEPkVgNbAZTz?q_$5dz0`iO~;Wi zu^#=B4~hj}=@s$%^!P^ky^snU%aisKM-7ZdK<;V{Ux(0%zKdqlOvh7J2Q-B7NMW4a z)`V)I*CQZ4p|T>#>Wy?CqikqoXAEaSJQCIO5zlLUJ3d1Swek7~aY}$`f?NM5CaRZc zPY7%shV)e$OjPYZUKhjj=Pv=ZtlLQTNHKD4FsBnRjhUe2d|ed(VP*ArYw}*WIO=x- z59vvCYHSQxiTgQ~Q|Pc6eY!)F`+*-!uX^hA(|h=`ExTbj$u zd)1ysPi*sRTwdn^!8JcRc?uLc}({wi7!ObpXhXpEw&jiAhzVprXm9Ikls|WCEX{MZP`FAp*=2RAW zeszx@RM#lPz>P(Cc7@|Aw#CtMw%9FY7DZ#PnjLui(R+Lg?4*7`lPf=Xs)uC}JM5oT zztXOdMTiB=?RZ*MeKTlwmv5Gv7bgDMz|K&b?R5d66SkAvu34cQM|vWQc2ZvM-fR>6 z6!ZFM`D;I^l>MMEaw5s18vfX#r~;HmsROy16+U+^ZG`5^8%P9u(ZRNZS* zVvHGIS!H)ZCklOgj=;0Ksb6Xuwey`O_B?=YTJJp!F?#BsVXYN57t&3JX``B*>&1yUL%OGgbJ$oK^SUPN@<(jkhSdn76$4 z73Dx=lA5nr@mhd~%sK}t0X4%u$ZPYEAopefWX^X~5%I1mGV%`NfgBgd!@Sz{GC#3b z$Kd|msis$1LMHaeF%&1YW?ZzB?%2(^&+|dimYZ~HWp@0lw#E)tsxc#E%9EP=tHIMO z_QGfc$@lm+R&Xlu0s4%~MiHO^Q`$N?k!Bz1+A*KFs6e|?n?dft9|D3g@ndsKXC8)~ zre(%hk!lw>()je}pu?st+J&ul34$jWoxgU|y<17}pG%ZwPNlIJWcZ3Eqs8X;Zf)Wd z&}h~>{n(so&;r#A3xJ9!z^YB?!pc5{uxVAkY6v~FzL^I#%!F=lPmh|fineBhOT`pu zf#ZO=p3eI(q58r!MnI`@wprHe*7H=B^o*Extlys{*13Bsi_~mlX2w!_#69xi0S0Sdg*FG!!k{IPywU(gfT0z_ozOwtZ%IbS_VQN3OiD;#mHI*u4ziR2FHS~A zsiUiVJfDK`(skYTo~I0~jlxr0stqNz^BVOv>Qnj1Eq3ed8bWu4kTD>z zw{4zeNe!wfMNpXhYMUH&-5f8PtRdEg(mGOmc*MKt2K*JKj}Uu#+*pQkG-~ zqX-|z(#AMFTS@5zno1_HI1zP%R;-7hS0!2w)ybo6+s%g}0iCpWK$-Yfp=VV*fN4|w za-KM!>Gr|BbhZ8{(8HDQ?@rE*I-3D{V|qG+f4n#?wn?fn-))bxB?@Oyej5N8P!%}b%X}Eg0x9{m~Y z@wZd9^%uf|IxmvOOd11o8Z~X9SCLtmXHJN zI_M{ToJ)|3-H&K@0#f4o4M6Y?Ez5?7fU1lEm2A{noq*%EcAS7^viaHU^|1gS{Zi=D zZL1nu%uu6BEP{Pdtf$1|f>v;)Zmxh6(W}G3X!tx(PIE0};7TGJmm(V1nJb;O#Up$6 zapA!)#qXRQMl$J6hTAJD3{B;#k+TeEcDbBibhdu&9F=0}+s8fHV`3;hl1UWSB;%G@6q-``x|Q~q>3{> z(@t_FRiMoSh69!-{U?rN7>%gzLOQ@C3?~NuEJI%40O6!)JZ=q>`iEPNS*HkV7M&u_ zR50BMqYkj<++VAc?~So{Z?|MVW{kbUlNCl}-zr9bdU0f;Rz#15)OOJPEfbxFbUrEX zR*qyOjZ^aeia<8c;8>yTgJ21fL(-XcFV7At{mFgAc_eMAe9$bL?&!93UMg&US0?&B8pm*a4&%mt-*nOl_vA#7o&TL{-YAuOOr7X|J`QHwg2z$40zRs z(Krh8)L9>eL)W|izM_EH0?Tj*SZWyw|Z3Xcu%4bBj^Axbw2p?MiJc{`yZm@v`tkvo&$2a;Rdxof{9OJY8t35 zRH_b}6m7b1(Zqoz)MijY;05qGfhOquRsdrUfYU7; z^x?k%JOBe=g}wnUj5^^1Hvxj}pOsa0Onw|z-R$;^_}s8L$4*hu18b3 zB{E}G@~GwnX;YdootjXO%$8whCu`r9tnjS#k4*aCNF*aQ#N%jKeW1bB@dtNVqQokF zsMs^L1Oc|OD~A&gwwq;Ti$;*!tUGBe3Erzn3Fm-IR6*C=$Xa~RBiuY?A3zG<=6%<; zA6DfC4Wv{+$kle;s(8=$SDOJP3kHfyP8PI&maUY+0jxPupsDpGAao4&{ynj$fUz72B5J7o);xFDvEy=kPD_<%0K%+4MK7Uf&e ziNy}c&o{l@q5oB%Q96;85ILU5g6?CK1rQ`8+3~(NM{rLnTa-i@&}K!B?#^g&att`s zcHrkgg+S$#8`>v&UcXv##8f!`4#XM~3(5f{k-g6ZRNT_*BSofW#i-wAK9Bc0%AN(9 zNtm9xashD0Af*cpFG2Fah0G z0H(G4goXtD_v#n!O+sY(SNmCAB1H@ADf5C%TEvOrhaa46qy{H!ObZ?$XTwCc+k|tN zGA-T~}<3y@5vBq6ZOl-v5ZIlHK-NC?0!F)Y#^r zAfpBiwX0JG|DAW{-ISG;iDvtH!6kjM#mvcItMbuo;iy*>cm$OIJ53&Fjm`IV1G(J; z>cxNfg5OL{v=1Z3cubIPEIdjZM(NI7hNE|eq=Vetp9p+xU9_?YCvdY*NaRF()cSHk z0QW4c<9%^B(|Y$pU_MFIz~aS{K8H9UX zzO~d1s&zIG$7KC3hjy+{vlZ$U0v-4=_dj^O1TQjao#+I`dEO@&t;MZD@FIc=SU)<& z#XsD9Lfa=)rrKNb%*UNE+y~{MRc;KqrNr04U3EP|rx+qTH|y$R4bY1@j?;sONSQ>( zG(ktHBj?={wZ6YUoa$i_eOG6q^?35@g0FCxA;-WYR>C3S$2vuc-_TE z0633NFD_rU3S-mX1Uv7{_Z>+z}AZM*==#yaoR% z6?2=&!#{i7;pXAdK@^{mi-1Ob;B`D13L~iq0uGSMR-nHfri1UB8_EY|D8LvQ%Yf2RpK0<0RSFjcQwQ!CciG#M#e z+r^FgYoc*HySb-Y1S;9EtbWh`b@I1?C^_UstX?uTX{6_VgWe7Qufd>$!HE28Fdx5b zHH{a5P+?9@2_pxZpO*shgmOdx5||CrlT$#YE82R<=5_7oRD3cl#j}I5&D1&Ws4@C+ z;mo7n2LrN-0btXcbU;}M3=%^R_XQ^TGrRD!A?o8vU zT;Dyu3{esZ4R)qQQpQY`c}fG444Fx>EkkCR7Atn43CUQ9B(umYbEOh0Q<9-rgs?Kk zvN+c*wf8>ff6hMt^X9zhg^#TD4EJ;2*ZsS`zu!X$M-)x-uFwFVxa6(c?eo$e8}x5cZ0GZ>D7@DwPkqO0;p)G}_C=J$xV%@G7oGt|#gIGX`neJ=5< z$9k_oyB2A7Y?Oa1GAfQCvIP^Vw9WwA2-JC&%DR z)}&fa*qr;7(B(!qiO)ygWJyg)ekvct&AeOm~6%)?XxRoP&1mWGxeD$e^ z>{=gEULQTgvkF2snk#MRc{`sj9adAn1k~?=xAK=ct^$p>{qrIq38Jy*@E;{R`e)^< z#M*HiqPzrWSeSfQdc!W`L!}D>TDI!C?8KaISL;tx1|Vd^?WD?u%d)vvAgn5;5_cXY z|2(8ajF^36AVe{L!GC$fbPI~z3)9Y1QYvc+ZF&7AQw(i0*FwrPq-Ly0M{SRKGdZl0zj+~m!^jslKv*VBsK5trdwDNdgMMn|J?!rKJJ7 zfYI|$$zpoE?{@JpbljM9&DY}_k)DaM74MP4)~uv`dUApPmC*ZH|Kmz*WcKEqlA2G? zr}sz##GQWYuV{T~aed;JZJ&j%@HbnP;M4;s#~V5-$`bqF-cdQjpRze7skw5gOJ+|v zmlgQGybdgPdXQM%`p^L4oL5sa&WR(^GVq;CaUPzB{l$FnOPzb8K27d|Ex~rS#gr`v zW5nWFXp|)kW*F_2Pb5xW2nB|u$9z|!?FXI1RL*Uo%S%yLDm@b~7d~0mmW+@O`79r5 zYuMB8a&GaKF_8If%KdwFT=Mnqw|(@R^{-t&KPTw7acc!!Kh|onlyZnUHNIp1;76nC z^M-KY(j(3G_6?4I+3D<&&~-4AlO%KFMZOZw+w@?>1bq}Y-#ho{>fr|^;t`a>4?a4= zmJ&*xm|M6s;EFi=e{x04=k?P{rTjpQT>tAb(=KDdrlx)=nwNh&HFh{P_Upcg$8C@R z@Z;2jU9W$9Uzy1cKKtPA0P)35QVPyKOwW9_{<;xY+nyB1{$T|8iLk{JBd@7G5wO|Z zE_Ud81;~a|J_6A}gGskgx;T0FYj5Ql$f>dO<7^skNfbh9WQZgOT%?_hyc@tix>WXt za0eLm1(2OWY`~ls-Kt1_J@RA+8(W6c(8!1;WQtMxE;4X>_#f@x3+v@S3>4{bYS_e9 zGh6BEMtXdCqoNVcB@#@x#cdHRFTFM|B6uadzP^{pPll3P!2(LL^7#)AL@>KZKm}U- z6`A?UCgi4`=)o*}*3SYO4Z-tQv0E$yGyhhSc zzx2ely3V}14~WD?@RK^Vnikpd!nx%zax0oa?u;<|{<8ifYiQ%f{Xn$?-}3x69y2Fk ztW(DgQu`5TvF=3yVw+e60FJrvevM5z-_4pv8{C@>GuJRq9*Tg23-%ws47_>k*|l&s+}I zUtMLdsF7#+I7o^QXC=f}N$lf`fx1_FKat@cJt>THiMQis{x zJ$~0e`mb}?0`8ee`nHv+65G5^*V4R)9)@LR@D{An{VxMLhSNU^+|TX+!!&zb*!QO($!_GRI@(lBqF#^{}MA&F3i@;JgbTs~(0rm6@7XLT=VzhF`Aq#klPJ z_)gs)u=pqE4-Wq@E=g&#A%YWkPHBY>D*O?W^{$!~J z1A*!bZGd{qVX@9eG{@Mi!KYwAkDq;87y=XSuR{25J8 z``3?u2)gB|;xqH(Nf;*IVmieD0Aaw2W5t+*Il*wQOu7`yX%1koZ;- zXxQM!!=OcVlu8oANT&l^Q?&KQMEoe23>y>)FsT36k(&$D0u1-p5vf|%o#Rt$P!wgA zTy5fm9kObQ3Vo3UCD>(^ztu;j?4^?@Tq=s=^QV~kIUjE2y`08)f_!W8N8^q>B7#W) zkXBXEHA&|Hfj4e}oN&{a)-*|7{VA}lLx^;tVjvEFUh`@B{Z=(LNm)rh2bXm(3k$4$ z44bc?ocOu2DED(8TmLd+^SkNgqsAvK*xA^Qsj3F8&+*3AL+o^d_rgu?&2)}V{X{>M zR?X3obn4emIsX-CeRv6lo)&WaFf;s9sloa$O{%-s+F&1D8#!;q#bsFN=;(+=JzuwA z>5U`dkdB#4_@Pene3`jc;Gy4JcH^Lvp`UAF08%y|gmPV(;s!}Xwd(4aMBA+ZzHx)W|OLP~U3(?Ou z)LxpO$PPM=eGD_pnkS1@&@{|o;ZCTaz$9%DNA>y=+w>$RF;N8iS+UUEL0XYhlk1L~ zX=(hZ6GZ%%09@?x&6U?V8c|z)Dqum*R2HnMv88>!nkW-4dAcc#f1lyHwy@?oZos>{ zju(M}#oExY*BLvVLts@oT1_Wz1beF(aI<9!n3;pgC=Hr0`!ODS05S(vkx1hD_%0WI zd!b*At!XQ*(=XzhUGni*04qzpB1WAD!2@fdqi*fPpQJBNz3uuor{IECs_eQI6}HXI z$r)5ao>^akk3Q(|I0K#ji)~a^heV)7+`%@6*fMolO?3;-b$69Johmv+tR^i1sjs(g zZF||&1U^XJTJPpwzgbUz{2C-S$KH;hdg=4rp{o1VhV-31hLS|8N9e$ev6!#znNj*V zM!GUiAI_V4!Loz=2mm;A5dFmCXfGvE{NCKX_O^Dij|Q5IF`y;N%7>w=%G_%~vqch~Q1iBRs3_TcD^;!eJf1)>a2^sOVRFHahbC;wB#N-G- zf}CNnDr8MPpgv4ysRem~<@u7`AV5@RNj}57jB5g_zQR@&4Adzabd?;JwAjYQ$BKV4 zBh9a~$lB^d(c90$=V&lbNQ5IQtLsdT(WmLTa`%xB@4kc3UIcw{dl)~Rp9cqv{U*zg z%K1#EHF%M@(jHU-jv)hF_z(PJK%NlkOpUsOsU)zF!zB1m59R-VEb)GISsHK?dj04& z#Mp*)r1|vP3%g(o<-xOe-sDg7l4Fx^(TQ6%j8zt}-_Ve923rQQ$x4XO2KGp_$_h8k z9MAOQi`k1!wS&bVRI6vr*QLE?$4&Aq5w!N!*r{3w@?dcr|x zQ6%a&I)#H6`w9PkJ4-xsf}dY;UZbrgT&4Jp6_zytW1kr8SAi+QLOSy;DjK>B8!#B% z25dDup`M=0TNzqI{P0jesv3FsKy&&pp8Id#zA;Uq9EZXnaQOJ%ijLj`p3c+F)fFK2 zBTmz?zq*&`2MHiz<>loP_U(fCqxk@`f?O>GFh?{nr6*hqMsR#cxqi*_S(=c<^PQ&s zdv}97Mf!IDn9gWF1-txw-29`n4g3&LIj4Y#e#BuTkF7&zjw?B7tudc>?_O;yxIJMD z{3L@3L-7R7zWdSHU_jYFj8}k#7HpzWVN1VA19zkwX+Q-B&7GzN1iTElCOg|DtyRk+ zS$7C?D?YH+c!7gEZ)q~us6cfaE9(PDGYD|r-25scK7Q1TQ;KzDWTb}+=CPmVpzR%0 zJQ~y3H$Bk$R(mX@K?A|o#jgo0v;;nl>k*JrvQ(8jJL@s0WKZ3T465xp#%-TeP~q(W zn$LC+)CM$}YsE=4f#8i-zwsIWQymMIo1f~>rji*x4L4$F*vwh>;C zuyUR>P-kDU3tjKW*V^8AouE>2mr8%25~wUg4*=P-D{Gs-O>yYH3U|`(Zkm0i*Fq1| z5t|&tiZhp`(7_-H3}Zb|zd0*MxmD3^tD5=-gQfsGPsLadb35{7p9YU}{sQOCVM~>b zRtFyPk`GKDGU8NXetW5rS2k|(kX=H0kkae@r}OSg`!y8MiEliFd(!JBYUpCo@TGQe zE2sDFRGM%#5Aku=V~^~blUowbib0_ec^N&&FlND;vpuAzr#VsX3`Iwt$*{3i&11kd zlu4dx__+!XTsJ5nIS^a=KbY`~2#C0bjfqo~51{!5ij-Nsp79-`W)Y2Cep+bg_|A<< zc$2IS#-7>`x|6ynmNbzKk>lrNA z7!Lc=lYl7U{QP06k*7QB1=fSn&fGg_O{VZrpjk5n!{9c!AbL8D|GUy@o5ZBa`Bmfh zWv=&40{Za_nX5k95(jPwXTk}607p+Ikx0Y=yxraxxQg#_fP~{f>AM^~rOx4YT+L%B z!xf>|P9Mv3Q66m3KE3n|qWtzx2SBjR1=qc(<=}yh$-5+N?NF>Xc?m{K5#1IJJD3Q( z4k^M*EF(z6#TqbguoYvP*O9;{x@&*HFM^UPpyAkY0 zZQvfVG-n&9;E0ee?k4CyP_U6(itn=k30l_E1HsI%U_JL{tak)szT4o9gINjbaA#kF zWkd|QfSmGwx3PH%&D7nBd&@35KyJ6U6GJ3aL2F$Suy$tcitFNcKZmhnfdJDp6tehZ zG9=&Hz%;Rz%e2`M;_K6p1s^(gmX=i-&s9lPzqfK3m${sc@A4L|r=fa^NEzg|7~{UC zJP0~=G{`vdjXc-Ee31641_lg@@{Sij8>9uMd!*ycRRmD#ieGT$Yo|Pht{)U+Q6TZW z_hS4`v9e&tCQ>nk!(xR(@uB!{g5C)aaHIMZil9Fz2>bQYwRG{0AQ{xBhDShA(!HX= ztws9>WT2oN6X`dE`a#GM$)Y2bk0j}}>e76N!IixdP^PH+T?Ettyy7Xjlf#W1%GYZ1 z%Rq=a0V{+4gdR#ce-LYN3vO73ec@pLsv&c8kECQ&a)@~S3yUJv_m$u(acus!G}q{(L2YqIbr6)JX&9I&k>A?% z`4m06(w{8*cZgZ&Q}Jqi0_Vre`47O{wIA7ur+s+{Dt1HdZy8}7+!<6V+)@;0u4^PA zVd2|g)5#+IksLy*+h}Fx@D%_foh+H{u!`nUZzVhEsi<7y*z(#T>Kmw1DNEL=frU4d z>7%aA3bHX&0VKEZ0R?E~9~l|u$3EJ2=+OEDI!2m$^mYm z2@JL(*s0{gVpuZ3>~bIxn$ZaybUOytYtr*DRdrw@8nLsJHLC%+kRmoV;{xwi$Q*%y z0(m+X5<~$wzzS}lR|#eq4L^7wc3U*+^FstiC-5ctopr_Iph8Bz$jWpAi<<nB@p_ zK>nd+)=`sVXi8<^o~Qab3;&pE|J~PPq=t?`(^`Jz`yfmAJPa^YdrePI^Cu`kZk&Y6 zK<%;LgG05_RMRAt)!FitA45Oj%ZdU@ z4opCibGpF#8KKV<0oIVQpJ`u_iIBS;1<#izn&pRBnv(n`52MikcId@I0LXsSkkTRNj|4*c zvKy@AC%}|OgjW*W0y+0MdZR=+z?lZ!vk;}^=H~WMhU&hIJNPT6hoW>&B+7c~ZV|u`Q)<3!oDc`X${l0+QZ_80gpf@XXQ30nn zKgxd|f)tZjXuZBvV^-itzL#-6(m3Q?T<4TClwSM{P1h)i(MdpF!L*HKmM0O=?rDPo z43Uxz_he}=Z|t0(>=iW&UNI!!I5|i}mc26%tq`E2Sn>N4? zakY7qCrTf{@_-6ov;YYkJyMpr@bQZ~SLln+QrJ{+M0pkiP=L!mlV2jz)ZE;WWtf|# z9WQZ2p>Yom=3AyI&11*jpeJ~x<<+axqlCXPN#Hq@>;#@d_#%54Np)Ze2;?b#|l$A`f`Bq<@$F5>~g zETxni0_IY6Nr&B^k4(I;f%kSSjRg+%2E>=ner>}@Ws;-lv4t{B)V!$~m#kj-6baG! z98zn0SOph_dm=#zYKk-WFHRio zvi1<|tb6gkpE7yT-eiZfcJq645Iy z02|7REZb>*4QB*98h$1|6>Q?BAM+iYBUGP1-un|gz{LB-^2^wzN str: + """Convert a Celsius temperature to Fahrenheit and return it as a formatted string.""" + fahrenheit = (celsius * 9 / 5) + 32 + return f"{celsius}°C is {fahrenheit}°F" + + +celsius_to_fahrenheit_schema = { + "type": "function", + "function": { + "name": "celsius_to_fahrenheit", + "description": "Convert a Celsius temperature to Fahrenheit.", + "parameters": { + "type": "object", + "properties": { + "celsius": { + "type": "number", + "description": "Temperature in Celsius", + } + }, + "required": ["celsius"], + }, + }, +} + + +print(celsius_to_fahrenheit(0)) +print(celsius_to_fahrenheit(100)) +print(celsius_to_fahrenheit(-40)) + +""" +Output: +0°C is 32.0°F +100°C is 212.0°F +-40°C is -40.0°F +""" + + +# ================================================== +# Q2 +# ================================================== + +""" +Prediction: + +Will calling run_agent("Convert 100 degrees Celsius to Fahrenheit") trigger a tool call? +No. + +Why? +Because this version of the agent only has one available tool: get_current_time. +The query asks for a temperature conversion, not the current time. + +How many API calls will be made? +One API call. + +If the model does not request a tool, the first response is already the final answer. +""" + + +def get_current_time() -> str: + """Return the current local time as a formatted string.""" + return datetime.now().strftime("%Y-%m-%d %H:%M:%S") + + +time_tool_schema = { + "type": "function", + "function": { + "name": "get_current_time", + "description": "Returns the current local time as a string.", + "parameters": { + "type": "object", + "properties": {}, + "required": [], + }, + }, +} + + +tools = [time_tool_schema] +print("Tools list defined with one tool: get_current_time") + + +def run_agent(user_prompt: str) -> str: + """Run a minimal ReAct-style agent for a single user prompt.""" + + SYSTEM_PROMPT = """ + You are a simple assistant that can tell the current time and convert Celsius to Fahrenheit. + Use tools when they are helpful. + """ + + messages = [ + {"role": "system", "content": SYSTEM_PROMPT}, + {"role": "user", "content": user_prompt}, + ] + + first_response = client.chat.completions.create( + model="gpt-4.1-mini", + messages=messages, + tools=tools, + tool_choice="auto", + ) + + print("First response received from model...") + print(first_response) + + first_message = first_response.choices[0].message + + messages.append( + { + "role": "assistant", + "content": first_message.content, + "tool_calls": first_message.tool_calls, + } + ) + + if first_message.tool_calls: + print("Agentic mode engaged...") + + for tool_call in first_message.tool_calls: + function_name = tool_call.function.name + + if function_name == "get_current_time": + tool_result = get_current_time() + + elif function_name == "celsius_to_fahrenheit": + args = json.loads(tool_call.function.arguments) + tool_result = celsius_to_fahrenheit(args["celsius"]) + + else: + tool_result = f"Error: unknown tool {function_name}." + + print("Tool called:", function_name) + print("Tool result:", tool_result) + + messages.append( + { + "role": "tool", + "tool_call_id": tool_call.id, + "name": function_name, + "content": tool_result, + } + ) + + second_response = client.chat.completions.create( + model="gpt-4.1-mini", + messages=messages, + ) + + print("Second response received from model...") + print(second_response) + + final_message = second_response.choices[0].message + return final_message.content or "" + + print("No tools needed....") + return first_message.content or "" + + +answer_with_agent = run_agent("Convert 100 degrees Celsius to Fahrenheit") +print(answer_with_agent) + +# Was my prediction correct? +# Yes. The model answered directly without calling the tool because +# get_current_time is unrelated to temperature conversion. + + +# ================================================== +# Q3 +# ================================================== + +tools = [ + time_tool_schema, + celsius_to_fahrenheit_schema, +] + +print("Tools list updated with two tools: get_current_time and celsius_to_fahrenheit") + +response_a = run_agent("What is 37 degrees Celsius in Fahrenheit?") +print("Response A:", response_a) + +# A tool was called because the user explicitly requested a temperature conversion, +# and the celsius_to_fahrenheit tool was available to perform that calculation. + +response_b = run_agent("What is the boiling point of water in plain English?") +print("Response B:", response_b) + +# No tool was called because this was a conceptual knowledge question. +# The model already knows the boiling point of water from its training data. + +# ================================================== +# Lesson 03: Multi-Tool Agent +# Q4 +# ================================================== + +from pathlib import Path + +import matplotlib.pyplot as plt +import pandas as pd +from scipy.stats import pearsonr + + +RESOURCES_DIR = Path(__file__).parent / "resources" + + +class CsvManager: + def __init__(self, resources_dir: Path): + self.resources_dir = resources_dir + self.df = None + self.csv_name = None + + def _normalize_csv_name(self, filename: str) -> str: + if not filename.lower().endswith(".csv"): + return filename + ".csv" + return filename + + def _available_csv_files(self) -> list[str]: + if not self.resources_dir.exists(): + return [] + return sorted( + [ + p.name + for p in self.resources_dir.iterdir() + if p.is_file() and p.suffix.lower() == ".csv" + ] + ) + + def _ensure_loaded(self): + if self.df is None: + files = self._available_csv_files() + example = files[0] if files else "your_file.csv" + return { + "error": ( + "No CSV is loaded yet. First load one from resources/. " + f"For example: load_csv '{example}'." + ) + } + return None + + def list_csv_files(self): + """List available CSV files in resources/.""" + files = self._available_csv_files() + if not files: + return { + "message": ( + "No CSV files found in resources/. " + "Create a resources/ folder and put one or more .csv files inside it." + ), + "files": [], + } + return {"files": files} + + def load_csv(self, filename: str): + """Load a CSV file from resources/ and make it the active dataset.""" + filename = self._normalize_csv_name(filename) + path = self.resources_dir / filename + + if not path.exists(): + return { + "error": f"Could not find '{filename}' in resources/.", + "available_files": self._available_csv_files(), + } + + self.df = pd.read_csv(path) + self.csv_name = filename + + return { + "message": f"Loaded {filename} with shape {self.df.shape}.", + "columns": self.df.columns.tolist(), + } + + def get_columns(self): + """Return column names for the currently loaded CSV.""" + error = self._ensure_loaded() + if error: + return error + return self.df.columns.tolist() + + def summarize_columns(self, columns: list[str] | None = None): + """Return basic summary stats for one or more columns.""" + error = self._ensure_loaded() + if error: + return error + + if columns is None: + data = self.df + else: + missing = [c for c in columns if c not in self.df.columns] + if missing: + return {"error": f"These columns are not in the data: {missing}"} + data = self.df[columns] + + summary = data.describe(include="all").transpose().round(3) + return summary.to_dict() + + def describe_column(self, column: str): + """Simple summary for a single column using pandas.describe().""" + error = self._ensure_loaded() + if error: + return error + + if column not in self.df.columns: + return {"error": f"'{column}' is not a column. Options: {self.df.columns.tolist()}"} + + s = self.df[column] + summary = s.describe().to_dict() + + cleaned = {} + for key, value in summary.items(): + if isinstance(value, (int, float)): + cleaned[key] = round(value, 3) + else: + cleaned[key] = value + + return cleaned + + def plot_data(self, y: str, x: str | None = None, plot_type: str = "line"): + """Plot from the active CSV.""" + error = self._ensure_loaded() + if error: + return error + + if plot_type not in ["scatter", "line"]: + return "Error: I can only do 'scatter' or 'line'." + + if y not in self.df.columns: + return f"Error: column '{y}' is not in {self.df.columns.tolist()}" + + if x == y: + x = None + + if plot_type == "scatter" and x is None: + return "Error: scatter plots need both x and y columns." + + title_csv = self.csv_name or "current CSV" + + if x is None: + ax = self.df[y].plot(kind="line") + ax.set_title(f"{title_csv} | Line plot: {y} vs row index") + plt.show() + + return f"Plotted {y} vs row index as a line plot." + + if x not in self.df.columns: + return f"Error: column '{x}' is not in {self.df.columns.tolist()}" + + ax = self.df.plot(x=x, y=y, kind=plot_type) + ax.set_title(f"{title_csv} | {plot_type.title()} plot: {y} vs {x}") + plt.savefig("assignments_07/outputs/my_plot.png") + + + return f"Plotted {y} vs {x} as a {plot_type}." + + def compute_correlation(self, col1: str, col2: str): + """ + Compute the Pearson correlation between two columns in the loaded DataFrame. + Returns the correlation coefficient and p-value. + """ + error = self._ensure_loaded() + if error: + return error + + missing = [col for col in [col1, col2] if col not in self.df.columns] + if missing: + return {"error": f"These columns are not in the data: {missing}"} + + pearson_r, p_value = pearsonr(self.df[col1], self.df[col2]) + + return { + "col1": col1, + "col2": col2, + "pearson_r": round(float(pearson_r), 4), + "p_value": round(float(p_value), 4), + } + + +print("Class defined") + +csv_backend = CsvManager(RESOURCES_DIR) + +node_tools = { + "list_csv_files": csv_backend.list_csv_files, + "load_csv": csv_backend.load_csv, + "get_columns": csv_backend.get_columns, + "summarize_columns": csv_backend.summarize_columns, + "describe_column": csv_backend.describe_column, + "plot_data": csv_backend.plot_data, + "compute_correlation": csv_backend.compute_correlation, +} + +tools_schema = [ + { + "type": "function", + "function": { + "name": "list_csv_files", + "description": "List available CSV files in the resources/ folder.", + }, + }, + { + "type": "function", + "function": { + "name": "load_csv", + "description": "Load a CSV file from the resources/ folder and make it the active dataset.", + "parameters": { + "type": "object", + "properties": { + "filename": { + "type": "string", + "description": "CSV filename in resources/, e.g. 'bike_commute.csv'.", + } + }, + "required": ["filename"], + }, + }, + }, + { + "type": "function", + "function": { + "name": "get_columns", + "description": "Get the column names of the currently loaded CSV.", + }, + }, + { + "type": "function", + "function": { + "name": "summarize_columns", + "description": "Show basic summary statistics for columns.", + "parameters": { + "type": "object", + "properties": { + "columns": { + "type": "array", + "items": {"type": "string"}, + "description": "Optional list of column names. If omitted, summarize all columns.", + } + }, + }, + }, + }, + { + "type": "function", + "function": { + "name": "describe_column", + "description": "Show basic summary statistics for a single column.", + "parameters": { + "type": "object", + "properties": { + "column": { + "type": "string", + "description": "Column name to describe.", + } + }, + "required": ["column"], + }, + }, + }, + { + "type": "function", + "function": { + "name": "plot_data", + "description": "Plot data from the active CSV. If only y is provided, plot y vs row index.", + "parameters": { + "type": "object", + "properties": { + "y": {"type": "string", "description": "Column name for y-axis."}, + "x": {"type": "string", "description": "Optional column name for x-axis."}, + "plot_type": { + "type": "string", + "enum": ["scatter", "line"], + "description": "Type of plot to create.", + }, + }, + "required": ["y"], + }, + }, + }, + { + "type": "function", + "function": { + "name": "compute_correlation", + "description": "Compute the Pearson correlation coefficient and p-value between two columns.", + "parameters": { + "type": "object", + "properties": { + "col1": { + "type": "string", + "description": "First column name.", + }, + "col2": { + "type": "string", + "description": "Second column name.", + }, + }, + "required": ["col1", "col2"], + }, + }, + }, +] + +# ================================================== +# Q5 +# ================================================== + +def run_agent_cycle(messages, user_text, max_tool_rounds=5): + """ + Run through one ReAct agent loop using tool calling. + """ + messages.append({"role": "user", "content": user_text}) + + def observe_tool_result(tool_call_id, result): + content = json.dumps(result, default=str) if not isinstance(result, str) else result + + return { + "role": "tool", + "tool_call_id": tool_call_id, + "content": content, + } + + for loop_idx in range(max_tool_rounds): + response = client.chat.completions.create( + model="gpt-4.1-mini", + messages=messages, + tools=tools_schema, + ) + + msg = response.choices[0].message + + assistant_entry = { + "role": "assistant", + "content": msg.content, + } + + if msg.tool_calls: + assistant_entry["tool_calls"] = [tc.model_dump() for tc in msg.tool_calls] + + messages.append(assistant_entry) + + if not msg.tool_calls: + return msg.content + + for tool_call in msg.tool_calls: + name = tool_call.function.name + tool_args = json.loads(tool_call.function.arguments or "{}") + + print(f"ACT: {name}({tool_args})") + + fn = node_tools.get(name) + + if fn is None: + result = {"error": f"Tool '{name}' not found."} + else: + try: + result = fn(**tool_args) if tool_args else fn() + except Exception as e: + result = {"error": f"Tool '{name}' failed: {type(e).__name__}: {e}"} + + messages.append(observe_tool_result(tool_call.id, result)) + + return "I hit the tool-round limit. Try a simpler request." + + +SYSTEM_PROMPT = ( + "You are a small data assistant for CSV files stored in resources/. " + "Use the available tools to do any data work (do not guess). " + "If no CSV is loaded yet, load one first (or list available CSV files). " + "Keep answers short and student-friendly." +) + + +messages = [{"role": "system", "content": SYSTEM_PROMPT}] + +result = run_agent_cycle( + messages, + "Load bike_commute.csv and compute the correlation between avg_traffic_density and avg_speed_kmh.", +) + +print(result) + +''' +Output: + +ACT: list_csv_files({}) +ACT: load_csv({'filename': 'bike_commute.csv'}) +ACT: compute_correlation({'col1': 'avg_traffic_density', 'col2': 'avg_speed_kmh'}) +The correlation between average traffic density and average speed (km/h) is approximately -0.53. +This indicates a moderate negative correlation, meaning that as traffic density increases, average speed tends to decrease. +The p-value is 0.0, showing this correlation is statistically significant. +''' + +# ================================================== +# Q6 +# ================================================== + +# In the ReAct loop: +# system = instructions that define the agent's behavior and rules. +# user = the user's request or question. +# assistant = the model's reasoning step, either a final answer or a request to call tools. +# tool = the observation step, where Python returns the result of the requested tool call. + +print(json.dumps(messages, indent=2, default=str)) + +# ================================================== +# Lesson 04: smolagents +# Q7 +# ================================================== + +from smolagents import tool + + +@tool +def compute_correlation_tool(col1: str, col2: str) -> dict: + """ + Compute the Pearson correlation between two columns in the currently loaded CSV file. + + Args: + col1: The name of the first numeric column. + col2: The name of the second numeric column. + """ + return csv_backend.compute_correlation(col1, col2) + + +print(compute_correlation_tool.description) + +# Comparison: +# In Q4 we manually wrote a full JSON schema with function name, +# description, parameters, and required fields. +# smolagents generates this automatically from the function signature +# and docstring. + +# To generate a good description, the developer must provide: +# 1. clear function name +# 2. meaningful parameter names +# 3. helpful docstring describing what the tool does + +# ================================================== +# Q8 +# ================================================== + +from smolagents import ToolCallingAgent, CodeAgent, OpenAIServerModel + + +model = OpenAIServerModel( + model_id="gpt-4.1-mini", + api_key=os.getenv("OPENAI_API_KEY"), +) + + +@tool +def load_csv_tool(filename: str) -> dict: + """ + Load a CSV file from the resources directory. + + Args: + filename: The CSV filename to load. + """ + return csv_backend.load_csv(filename) + + +@tool +def plot_data_tool(y: str, x: str = None, plot_type: str = "line") -> str: + """ + Plot data from the currently loaded CSV file. + + Args: + y: Column name for y-axis. + x: Optional column name for x-axis. + plot_type: Plot type, either scatter or line. + """ + return csv_backend.plot_data(y, x, plot_type) + + +TOOLS = [ + load_csv_tool, + plot_data_tool, + compute_correlation_tool, +] + + +tool_agent = ToolCallingAgent( + tools=TOOLS, + model=model, +) + +code_agent = CodeAgent( + tools=TOOLS, + model=model, +) + +prompt = "Load bike_commute.csv. Plot avg_heart_rate vs duration_min as a scatter plot with green dots." + +response_tool = tool_agent.run(prompt) +print("ToolCallingAgent response:", response_tool) + +response_code = code_agent.run( + prompt, + additional_args={"csv_manager": csv_backend}, +) +print("CodeAgent response:", response_code) + +""" +Comparison: + +ToolCallingAgent used the predefined tools: +- load_csv_tool +- plot_data_tool + +It created a scatter plot, but the dots were the default matplotlib color, not green. +This happened because plot_data_tool does not have a color parameter, so the agent could not pass "green" into the tool. + +CodeAgent attempted to generate and execute Python code dynamically. +However, it failed because matplotlib tried to create a GUI figure from a worker thread on macOS. +This shows both the strength and the risk of CodeAgent: +it is more flexible because it can write code, but it can also run into execution/environment errors. + +A ToolCallingAgent is better for controlled, predictable workflows. +A CodeAgent is more useful when the task requires custom logic that was not built into the predefined tools. +""" + +# ================================================== +# Q9 +# ================================================== + +""" +A ToolCallingAgent would be a better choice for a task like loading a CSV file, +summarizing columns, or computing a predefined statistic such as correlation. + +This is a good fit for a tool-based approach because the task has clear, +limited actions and safe predefined tools. The agent only needs to choose +which tool to call and what arguments to pass. + +One meaningful risk of using a CodeAgent is that it can generate and execute +unexpected or unsafe code. For example, it may try to import unauthorized +libraries, access attributes that do not exist, modify files, or fail because +of environment-specific issues. A ToolCallingAgent does not have this same +level of risk because it can only call the tools that the developer explicitly +provided. +""" \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 4c71436..ba83406 100644 --- a/requirements.txt +++ b/requirements.txt @@ -13,4 +13,7 @@ llama-index-core==0.14.10 llama-index-embeddings-openai==0.6.0 llama-index-llms-openai==0.7.8 llama-index-readers-file==0.6.0 -pypdf==6.11.0 \ No newline at end of file +pypdf==6.11.0 +hf-xet-==1.5.0 +huggingface-hub==1.16.1 +smolagents==1.25.0 \ No newline at end of file From 4b1f9d7c719650f30d829797461479dbc0b23bb6 Mon Sep 17 00:00:00 2001 From: Ligo-code Date: Mon, 25 May 2026 19:20:29 -0400 Subject: [PATCH 2/3] complete project_07.py --- .../outputs/happiness_by_region.png | Bin 0 -> 85664 bytes assignments_07/project_07.py | 300 ++++++++++++++++++ 2 files changed, 300 insertions(+) create mode 100644 assignments_07/outputs/happiness_by_region.png diff --git a/assignments_07/outputs/happiness_by_region.png b/assignments_07/outputs/happiness_by_region.png new file mode 100644 index 0000000000000000000000000000000000000000..d2feaadf2157911d6a3dbacda9fb4408ba15321b GIT binary patch literal 85664 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 dRPK|f=U1@V*Xt+VH=hAN5AJFzmnoV*`wxotm{ dict: + """ + Load the World Happiness dataset into memory. + + Returns: + A dictionary containing the shape, column names, data path, and output plot path. + """ + global df + + if not DATA_PATH.exists(): + return {"error": f"Data file not found at {DATA_PATH}"} + + df = pd.read_csv(DATA_PATH) + + return { + "shape": df.shape, + "columns": df.columns.tolist(), + "data_path": str(DATA_PATH), + "plot_path": str(PLOT_PATH), + } + + +# ================================================== +# Task 1 — Tool 2: summarize_column +# ================================================== + +@tool +def summarize_column(column: str) -> dict: + """ + Return descriptive statistics for a single column in the loaded dataset. + + Args: + column: The name of the column to summarize. + + Returns: + A dictionary with descriptive statistics for the selected column. + """ + global df + + if df is None: + return {"error": "No data is loaded. Please call load_happiness_data first."} + + if column not in df.columns: + return { + "error": f"Column '{column}' not found. Available columns: {df.columns.tolist()}" + } + + return df[column].describe().to_dict() + + +# ================================================== +# Task 1 — Tool 3: compute_correlation +# ================================================== + +@tool +def compute_correlation(col1: str, col2: str) -> dict: + """ + Compute the Pearson correlation coefficient and p-value between two numeric columns. + + Args: + col1: The name of the first numeric column. + col2: The name of the second numeric column. + + Returns: + A dictionary containing the correlation coefficient and p-value. + """ + global df + + if df is None: + return {"error": "No data is loaded. Please call load_happiness_data first."} + + if col1 not in df.columns or col2 not in df.columns: + return {"error": "One or both columns not found in dataset."} + + try: + pearson_r, p_value = pearsonr(df[col1], df[col2]) + + return { + "col1": col1, + "col2": col2, + "pearson_r": round(float(pearson_r), 4), + "p_value": round(float(p_value), 4), + } + + except Exception as e: + return {"error": str(e)} + + +# ================================================== +# Task 1 — Tool 4: get_top_n_countries +# ================================================== + +@tool +def get_top_n_countries(column: str, year: int, n: int = 5) -> dict: + """ + Return the top N countries ranked by a given column for a specific year. + + Args: + column: The numeric column used for ranking. + year: The year to filter the dataset by. + n: Number of top countries to return. + + Returns: + A dictionary containing the ranked countries and values. + """ + global df + + if df is None: + return {"error": "No data is loaded. Please call load_happiness_data first."} + + if column not in df.columns: + return {"error": f"Column '{column}' not found."} + + filtered_df = df[df["year"] == year] + + if filtered_df.empty: + return {"error": f"No data found for year {year}."} + + top_rows = filtered_df.sort_values(by=column, ascending=False).head(n) + + result = [] + + for _, row in top_rows.iterrows(): + result.append( + { + "country": row["country"], + column: row[column], + } + ) + + return {"results": result} + + +# ================================================== +# Task 2 — Build the Agent +# ================================================== + +model = OpenAIServerModel( + api_key=api_key, + model_id="gpt-4o-mini", +) + +SYSTEM_PROMPT = """ +You are a data analyst assistant for the World Happiness dataset. + +Use the available tools for: +- loading data +- summarizing columns +- computing correlations +- ranking countries + +Important rules — follow them exactly: +1. NEVER mock or generate fake data. Always use the tools to load real data. +2. When creating any plot, ALWAYS start with these exact lines FIRST: + import matplotlib + matplotlib.use('Agg') + import matplotlib.pyplot as plt +3. For custom plots, call load_happiness_data() first, read the CSV from the + returned data_path using pandas, then save the plot to the exact absolute + path returned as plot_path. Never use a relative path like 'outputs/...'. + +Be concise and student-friendly in your responses. +""" + +agent = CodeAgent( + tools=[ + load_happiness_data, + summarize_column, + compute_correlation, + get_top_n_countries, + ], + model=model, + instructions=SYSTEM_PROMPT, + additional_authorized_imports=[ + "pandas", + "matplotlib", + "matplotlib.pyplot", + "scipy.stats", + ], + max_steps=8, +) + + +# ================================================== +# Task 3 — Run Guided Queries +# ================================================== + +if __name__ == "__main__": + queries = [ + "Load the happiness data and tell me its shape and column names.", + "Summarize the happiness_score column.", + "What is the correlation between gdp_per_capita and happiness_score? Is it statistically significant?", + "Show me the top 5 happiest countries in 2020.", + ( + "Plot happiness_score over the years as a line chart, with one line per region. " + "Use the data_path and plot_path from load_happiness_data. " + "Read the real CSV with pandas, group by year and regional_indicator, " + "and save the plot exactly to plot_path." + ), + ] + + for query in queries: + print(f"\n--- Query: {query} ---") + response = agent.run(query, reset=False) + print(response) + + print("\nExpected plot location:") + print(PLOT_PATH) + + # ================================================== + # Task 4 — Your Own Questions + # ================================================== + + my_query_1 = ( + "Which country improved its happiness_score the most between 2015 and 2023?" + ) + print(f"\n--- My Query 1: {my_query_1} ---") + response_1 = agent.run(my_query_1, reset=False) + print(response_1) + + # Comment: This triggered code generation because the agent needed to compare + # happiness_score changes between multiple years across countries. + + my_query_2 = ( + "Which region has the highest average happiness_score across all years?" + ) + print(f"\n--- My Query 2: {my_query_2} ---") + response_2 = agent.run(my_query_2, reset=False) + print(response_2) + + # Comment: This triggered code generation because the agent needed to group + # the dataset by region and calculate average happiness scores. + + # ================================================== +# Task 5 — Reflection +# ================================================== + +# --- Reflection --- +# +# 1. In Query 3, the agent communicated statistical significance by returning +# both the Pearson correlation coefficient and the p-value. It used the +# p-value correctly because a p-value of 0.0 is well below the standard +# significance threshold of 0.05, so the correlation was considered +# statistically significant. +# +# 2. One response that surprised me was Query 5, where the agent successfully +# generated custom pandas and matplotlib code to create a line chart grouped +# by region. This was more capable than I expected because none of the +# predefined tools directly supported multi-line plotting, so the agent had +# to reason through the steps and write the analysis code itself. +# +# 3. One additional tool that would make this agent more useful would be a +# custom plotting tool that accepts parameters such as x column, y column, +# grouping column, chart type, and output filename. This would allow the +# agent to create visualizations more reliably without needing to generate +# raw Python code, and it would help answer questions like: +# "Create a bar chart of average happiness_score by region for 2020." From 319b1e14b969ca933e97f2662feef2e4305751f5 Mon Sep 17 00:00:00 2001 From: Ligo-code Date: Mon, 25 May 2026 19:28:35 -0400 Subject: [PATCH 3/3] add README.md for the project_07. --- assignments_07/README.md | 141 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 141 insertions(+) create mode 100644 assignments_07/README.md diff --git a/assignments_07/README.md b/assignments_07/README.md new file mode 100644 index 0000000..965996e --- /dev/null +++ b/assignments_07/README.md @@ -0,0 +1,141 @@ +# World Happiness Agent + +A mini-project built with `smolagents` and OpenAI's `CodeAgent` to explore the World Happiness dataset through natural language queries. + +## Project Overview + +This project demonstrates how an AI agent can combine predefined Python tools with dynamic code generation to analyze data conversationally. + +The agent can: + +- load the World Happiness dataset +- summarize dataset columns +- compute Pearson correlations with statistical significance +- rank countries by selected metrics +- generate custom visualizations when predefined tools are not sufficient + +## Technologies Used + +- Python +- Pandas +- Matplotlib +- SciPy +- smolagents +- OpenAI API +- dotenv + +## Project Structure + +```bash +assignments_07/ +│ +├── project_07.py +├── outputs/ +│ └── happiness_by_region.png +``` + +## Implemented Tools + +### load_happiness_data() + +Loads the merged World Happiness dataset into a shared global DataFrame. + +Returns: + +- dataset shape +- column names +- data path +- plot output path + +--- + +### summarize_column(column) + +Returns descriptive statistics for a selected column. + +Example: + +```python +summarize_column("happiness_score") +``` + +--- + +### compute_correlation(col1, col2) + +Computes: + +- Pearson correlation coefficient +- p-value + +Example: + +```python +compute_correlation("gdp_per_capita", "happiness_score") +``` + +--- + +### get_top_n_countries(column, year, n) + +Ranks countries by a selected metric for a given year. + +Example: + +```python +get_top_n_countries("happiness_score", 2020, 5) +``` + +## Example Queries + +Guided queries: + +- Load the happiness data and tell me its shape and column names +- Summarize the happiness_score column +- Compute correlation between GDP per capita and happiness score +- Show top 5 happiest countries in 2020 +- Generate a regional happiness trend chart + +Custom queries: + +- Which country improved its happiness score the most between 2015 and 2023? +- Which region has the highest average happiness score across all years? + +## Key Learning Outcomes + +This project helped demonstrate the difference between: + +**Tool-based agent behavior** +- safe +- predictable +- limited to predefined actions + +and + +**CodeAgent behavior** +- flexible +- capable of custom analysis +- able to generate Python code dynamically +- more prone to execution/runtime issues + +## How to Run + +Install dependencies: + +```bash +pip install -r requirements.txt +``` + +Run: + +```bash +python assignments_07/project_07.py +``` + +## Output + +Generated visualization: + +```bash +assignments_07/outputs/happiness_by_region.png +``` \ No newline at end of file