├── .gitignore ├── static ├── blackbox.jpg ├── uax_logo.png ├── quad_xsinx.png ├── trap_xsinx.png ├── plot_colormaps.png ├── styles │ ├── FluxBold.ttf │ └── style.css ├── ejercicio_contour.png ├── figure_elasticlaw.png ├── aeropython_name_mini.png ├── polar.dat └── temperaturas.csv ├── Python España y AeroPython.pdf ├── notebooks ├── eq_state.py ├── 01-Ejercicios_basico.ipynb ├── 04-SciPy.ipynb ├── 03-Matplotlib.ipynb ├── 02-NumPy.ipynb └── PX - Sistema Cerrado.ipynb └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .ipynb_checkpoints/ 2 | -------------------------------------------------------------------------------- /static/blackbox.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AeroPython/aeropython_uax/master/static/blackbox.jpg -------------------------------------------------------------------------------- /static/uax_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AeroPython/aeropython_uax/master/static/uax_logo.png -------------------------------------------------------------------------------- /static/quad_xsinx.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AeroPython/aeropython_uax/master/static/quad_xsinx.png -------------------------------------------------------------------------------- /static/trap_xsinx.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AeroPython/aeropython_uax/master/static/trap_xsinx.png -------------------------------------------------------------------------------- /static/plot_colormaps.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AeroPython/aeropython_uax/master/static/plot_colormaps.png -------------------------------------------------------------------------------- /static/styles/FluxBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AeroPython/aeropython_uax/master/static/styles/FluxBold.ttf -------------------------------------------------------------------------------- /Python España y AeroPython.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AeroPython/aeropython_uax/master/Python España y AeroPython.pdf -------------------------------------------------------------------------------- /static/ejercicio_contour.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AeroPython/aeropython_uax/master/static/ejercicio_contour.png -------------------------------------------------------------------------------- /static/figure_elasticlaw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AeroPython/aeropython_uax/master/static/figure_elasticlaw.png -------------------------------------------------------------------------------- /static/aeropython_name_mini.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AeroPython/aeropython_uax/master/static/aeropython_name_mini.png -------------------------------------------------------------------------------- /static/polar.dat: -------------------------------------------------------------------------------- 1 | # Polar data for a certain airplane 2 | # 1st line: C_L 3 | # 2rd line: C_D 4 | 5 | -0.9100 -0.7200 -0.4800 -0.2700 -0.0600 0.1600 0.3100 0.4700 0.6000 0.8200 1.0200 1.2000 1.2400 1.1500 1.0000 0.8000 6 | 0.0538 0.0438 0.0316 0.0245 0.0228 0.0232 0.0262 0.0301 0.0348 0.0461 0.0608 0.0771 0.0814 0.0900 0.0950 0.1000 7 | -------------------------------------------------------------------------------- /notebooks/eq_state.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | 4 | 5 | """ 6 | 7 | # %% 8 | 9 | 10 | 11 | def state_eq(R, P=False, V=False, m=False, T=False): 12 | if P is False: 13 | P_=solve_P(V, m, T, R) 14 | return P_ 15 | elif V is False: 16 | V_=solve_V(P, m, T, R) 17 | return V_ 18 | elif m is False: 19 | m_ = solve_m(P, V, T, R) 20 | return m_ 21 | elif T is False: 22 | T_ = solve_T(P, V, m, R) 23 | return T_ 24 | 25 | def solve_P(V, T, m, R): 26 | return m*R*T/V 27 | 28 | def solve_V(P, m, T, R): 29 | return m*R*T/P 30 | 31 | def solve_m(P, V, T, R): 32 | return P*V/R/T 33 | 34 | def solve_T(P, V, m, R): 35 | return P*V/m/R 36 | 37 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Charla AeroPython Universidad Alfonso X el Sabio 2 | 3 | AeroPython 4 | UAX 5 | 6 | ## [Juan Luis Cano](http://es.linkedin.com/in/juanluiscanor) y [Álex Sáez](https://www.linkedin.com/in/alejandrosaezm) 7 | 8 | __Charla de introducción a Python en la Universidad Alfonso X el Sabio__ 9 | __5 de diciembre de 2016__ 10 | 11 | Charla-taller introductorio a Python científico (NumPy, matplotlib y SciPy) dirigida a los alumnos de las asignaturas de Termodinámica (2º) y Motores (3º) del grado en Ingeniería Aeroespacial. 12 | 13 | Impartido con la colaboración del Profesor [Marcos A. Rodríguez Jiménez](https://www.linkedin.com/in/marcos-antonio-rodr%C3%ADguez-jim%C3%A9nez-50615446). 14 | 15 | 16 | Licencia Creative Commons
Curso AeroPython por Juan Luis Cano Rodriguez y Alejandro Sáez Mollejo se distribuye bajo una Licencia Creative Commons Atribución 4.0 Internacional. 17 | -------------------------------------------------------------------------------- /static/styles/style.css: -------------------------------------------------------------------------------- 1 | /* This template is inspired in the one used by Lorena Barba 2 | in the numerical-mooc repository: https://github.com/numerical-mooc/numerical-mooc 3 | We thank her work and hope you also enjoy the look of the notobooks with this style */ 4 | 5 | 6 | 7 | El estilo se ha aplicado =) 8 | 9 | 146 | 162 | -------------------------------------------------------------------------------- /static/temperaturas.csv: -------------------------------------------------------------------------------- 1 | STATION,DATE,TMAX,TMIN 2 | GHCND:USW00094728,20130101,44,-33 3 | GHCND:USW00094728,20130102,6,-56 4 | GHCND:USW00094728,20130103,0,-44 5 | GHCND:USW00094728,20130104,28,-11 6 | GHCND:USW00094728,20130105,56,0 7 | GHCND:USW00094728,20130106,78,11 8 | GHCND:USW00094728,20130107,72,28 9 | GHCND:USW00094728,20130108,89,17 10 | GHCND:USW00094728,20130109,94,39 11 | GHCND:USW00094728,20130110,83,44 12 | GHCND:USW00094728,20130111,78,28 13 | GHCND:USW00094728,20130112,83,56 14 | GHCND:USW00094728,20130113,100,61 15 | GHCND:USW00094728,20130114,133,33 16 | GHCND:USW00094728,20130115,33,17 17 | GHCND:USW00094728,20130116,28,0 18 | GHCND:USW00094728,20130117,61,17 19 | GHCND:USW00094728,20130118,17,-39 20 | GHCND:USW00094728,20130119,106,-11 21 | GHCND:USW00094728,20130120,117,-11 22 | GHCND:USW00094728,20130121,0,-33 23 | GHCND:USW00094728,20130122,-28,-106 24 | GHCND:USW00094728,20130123,-67,-117 25 | GHCND:USW00094728,20130124,-56,-111 26 | GHCND:USW00094728,20130125,-44,-106 27 | GHCND:USW00094728,20130126,-28,-94 28 | GHCND:USW00094728,20130127,11,-72 29 | GHCND:USW00094728,20130128,22,-17 30 | GHCND:USW00094728,20130129,94,22 31 | GHCND:USW00094728,20130130,150,39 32 | GHCND:USW00094728,20130131,161,-11 33 | GHCND:USW00094728,20130201,-6,-44 34 | GHCND:USW00094728,20130202,-17,-72 35 | GHCND:USW00094728,20130203,-11,-44 36 | GHCND:USW00094728,20130204,-11,-50 37 | GHCND:USW00094728,20130205,0,-22 38 | GHCND:USW00094728,20130206,39,-17 39 | GHCND:USW00094728,20130207,0,-33 40 | GHCND:USW00094728,20130208,11,-28 41 | GHCND:USW00094728,20130209,0,-61 42 | GHCND:USW00094728,20130210,22,-78 43 | GHCND:USW00094728,20130211,72,11 44 | GHCND:USW00094728,20130212,67,22 45 | GHCND:USW00094728,20130213,67,6 46 | GHCND:USW00094728,20130214,78,6 47 | GHCND:USW00094728,20130215,128,28 48 | GHCND:USW00094728,20130216,50,-6 49 | GHCND:USW00094728,20130217,0,-78 50 | GHCND:USW00094728,20130218,17,-83 51 | GHCND:USW00094728,20130219,94,6 52 | GHCND:USW00094728,20130220,33,-39 53 | GHCND:USW00094728,20130221,11,-44 54 | GHCND:USW00094728,20130222,33,-39 55 | GHCND:USW00094728,20130223,56,22 56 | GHCND:USW00094728,20130224,83,11 57 | GHCND:USW00094728,20130225,72,0 58 | GHCND:USW00094728,20130226,67,17 59 | GHCND:USW00094728,20130227,83,22 60 | GHCND:USW00094728,20130228,106,39 61 | GHCND:USW00094728,20130301,72,22 62 | GHCND:USW00094728,20130302,44,-6 63 | GHCND:USW00094728,20130303,44,-17 64 | GHCND:USW00094728,20130304,56,-28 65 | GHCND:USW00094728,20130305,94,-6 66 | GHCND:USW00094728,20130306,56,33 67 | GHCND:USW00094728,20130307,33,6 68 | GHCND:USW00094728,20130308,56,-6 69 | GHCND:USW00094728,20130309,128,17 70 | GHCND:USW00094728,20130310,100,22 71 | GHCND:USW00094728,20130311,122,44 72 | GHCND:USW00094728,20130312,139,61 73 | GHCND:USW00094728,20130313,111,33 74 | GHCND:USW00094728,20130314,50,-17 75 | GHCND:USW00094728,20130315,83,-11 76 | GHCND:USW00094728,20130316,50,0 77 | GHCND:USW00094728,20130317,33,-17 78 | GHCND:USW00094728,20130318,17,-22 79 | GHCND:USW00094728,20130319,61,6 80 | GHCND:USW00094728,20130320,72,0 81 | GHCND:USW00094728,20130321,44,-11 82 | GHCND:USW00094728,20130322,50,-22 83 | GHCND:USW00094728,20130323,78,0 84 | GHCND:USW00094728,20130324,83,6 85 | GHCND:USW00094728,20130325,44,22 86 | GHCND:USW00094728,20130326,117,28 87 | GHCND:USW00094728,20130327,117,22 88 | GHCND:USW00094728,20130328,106,28 89 | GHCND:USW00094728,20130329,128,44 90 | GHCND:USW00094728,20130330,150,44 91 | GHCND:USW00094728,20130331,122,67 92 | GHCND:USW00094728,20130401,167,28 93 | GHCND:USW00094728,20130402,61,6 94 | GHCND:USW00094728,20130403,78,6 95 | GHCND:USW00094728,20130404,117,6 96 | GHCND:USW00094728,20130405,178,50 97 | GHCND:USW00094728,20130406,111,17 98 | GHCND:USW00094728,20130407,128,44 99 | GHCND:USW00094728,20130408,228,106 100 | GHCND:USW00094728,20130409,278,106 101 | GHCND:USW00094728,20130410,233,128 102 | GHCND:USW00094728,20130411,156,83 103 | GHCND:USW00094728,20130412,83,50 104 | GHCND:USW00094728,20130413,144,50 105 | GHCND:USW00094728,20130414,139,78 106 | GHCND:USW00094728,20130415,150,67 107 | GHCND:USW00094728,20130416,172,83 108 | GHCND:USW00094728,20130417,217,128 109 | GHCND:USW00094728,20130418,150,106 110 | GHCND:USW00094728,20130419,217,128 111 | GHCND:USW00094728,20130420,156,61 112 | GHCND:USW00094728,20130421,128,28 113 | GHCND:USW00094728,20130422,128,50 114 | GHCND:USW00094728,20130423,117,50 115 | GHCND:USW00094728,20130424,206,67 116 | GHCND:USW00094728,20130425,189,78 117 | GHCND:USW00094728,20130426,194,100 118 | GHCND:USW00094728,20130427,217,89 119 | GHCND:USW00094728,20130428,206,106 120 | GHCND:USW00094728,20130429,139,111 121 | GHCND:USW00094728,20130430,200,106 122 | GHCND:USW00094728,20130501,206,83 123 | GHCND:USW00094728,20130502,222,94 124 | GHCND:USW00094728,20130503,189,94 125 | GHCND:USW00094728,20130504,206,89 126 | GHCND:USW00094728,20130505,178,78 127 | GHCND:USW00094728,20130506,206,78 128 | GHCND:USW00094728,20130507,233,111 129 | GHCND:USW00094728,20130508,172,128 130 | GHCND:USW00094728,20130509,200,122 131 | GHCND:USW00094728,20130510,261,150 132 | GHCND:USW00094728,20130511,211,161 133 | GHCND:USW00094728,20130512,211,106 134 | GHCND:USW00094728,20130513,144,72 135 | GHCND:USW00094728,20130514,161,56 136 | GHCND:USW00094728,20130515,206,111 137 | GHCND:USW00094728,20130516,261,167 138 | GHCND:USW00094728,20130517,222,144 139 | GHCND:USW00094728,20130518,183,133 140 | GHCND:USW00094728,20130519,150,128 141 | GHCND:USW00094728,20130520,261,144 142 | GHCND:USW00094728,20130521,300,200 143 | GHCND:USW00094728,20130522,256,150 144 | GHCND:USW00094728,20130523,267,178 145 | GHCND:USW00094728,20130524,183,72 146 | GHCND:USW00094728,20130525,122,72 147 | GHCND:USW00094728,20130526,189,89 148 | GHCND:USW00094728,20130527,228,106 149 | GHCND:USW00094728,20130528,200,133 150 | GHCND:USW00094728,20130529,278,144 151 | GHCND:USW00094728,20130530,322,222 152 | GHCND:USW00094728,20130531,322,239 153 | GHCND:USW00094728,20130601,322,228 154 | GHCND:USW00094728,20130602,311,206 155 | GHCND:USW00094728,20130603,256,189 156 | GHCND:USW00094728,20130604,239,139 157 | GHCND:USW00094728,20130605,233,144 158 | GHCND:USW00094728,20130606,211,150 159 | GHCND:USW00094728,20130607,172,150 160 | GHCND:USW00094728,20130608,250,139 161 | GHCND:USW00094728,20130609,267,172 162 | GHCND:USW00094728,20130610,211,167 163 | GHCND:USW00094728,20130611,267,178 164 | GHCND:USW00094728,20130612,244,183 165 | GHCND:USW00094728,20130613,206,128 166 | GHCND:USW00094728,20130614,222,117 167 | GHCND:USW00094728,20130615,267,161 168 | GHCND:USW00094728,20130616,267,183 169 | GHCND:USW00094728,20130617,289,206 170 | GHCND:USW00094728,20130618,289,178 171 | GHCND:USW00094728,20130619,250,150 172 | GHCND:USW00094728,20130620,267,167 173 | GHCND:USW00094728,20130621,278,178 174 | GHCND:USW00094728,20130622,289,183 175 | GHCND:USW00094728,20130623,311,211 176 | GHCND:USW00094728,20130624,333,233 177 | GHCND:USW00094728,20130625,328,228 178 | GHCND:USW00094728,20130626,294,233 179 | GHCND:USW00094728,20130627,300,228 180 | GHCND:USW00094728,20130628,294,228 181 | GHCND:USW00094728,20130629,283,217 182 | GHCND:USW00094728,20130630,300,228 183 | GHCND:USW00094728,20130701,250,222 184 | GHCND:USW00094728,20130702,278,222 185 | GHCND:USW00094728,20130703,283,228 186 | GHCND:USW00094728,20130704,306,239 187 | GHCND:USW00094728,20130705,322,244 188 | GHCND:USW00094728,20130706,333,256 189 | GHCND:USW00094728,20130707,333,256 190 | GHCND:USW00094728,20130708,317,228 191 | GHCND:USW00094728,20130709,311,233 192 | GHCND:USW00094728,20130710,294,239 193 | GHCND:USW00094728,20130711,289,244 194 | GHCND:USW00094728,20130712,250,200 195 | GHCND:USW00094728,20130713,272,194 196 | GHCND:USW00094728,20130714,322,233 197 | GHCND:USW00094728,20130715,344,256 198 | GHCND:USW00094728,20130716,344,250 199 | GHCND:USW00094728,20130717,361,261 200 | GHCND:USW00094728,20130718,367,272 201 | GHCND:USW00094728,20130719,356,283 202 | GHCND:USW00094728,20130720,339,272 203 | GHCND:USW00094728,20130721,317,244 204 | GHCND:USW00094728,20130722,300,239 205 | GHCND:USW00094728,20130723,306,228 206 | GHCND:USW00094728,20130724,283,200 207 | GHCND:USW00094728,20130725,200,178 208 | GHCND:USW00094728,20130726,283,183 209 | GHCND:USW00094728,20130727,278,211 210 | GHCND:USW00094728,20130728,256,211 211 | GHCND:USW00094728,20130729,294,206 212 | GHCND:USW00094728,20130730,283,194 213 | GHCND:USW00094728,20130731,283,194 214 | GHCND:USW00094728,20130801,244,189 215 | GHCND:USW00094728,20130802,283,194 216 | GHCND:USW00094728,20130803,256,200 217 | GHCND:USW00094728,20130804,267,189 218 | GHCND:USW00094728,20130805,256,167 219 | GHCND:USW00094728,20130806,278,178 220 | GHCND:USW00094728,20130807,267,211 221 | GHCND:USW00094728,20130808,272,211 222 | GHCND:USW00094728,20130809,294,233 223 | GHCND:USW00094728,20130810,283,211 224 | GHCND:USW00094728,20130811,272,183 225 | GHCND:USW00094728,20130812,278,211 226 | GHCND:USW00094728,20130813,250,200 227 | GHCND:USW00094728,20130814,233,161 228 | GHCND:USW00094728,20130815,256,150 229 | GHCND:USW00094728,20130816,278,178 230 | GHCND:USW00094728,20130817,289,178 231 | GHCND:USW00094728,20130818,244,200 232 | GHCND:USW00094728,20130819,261,189 233 | GHCND:USW00094728,20130820,311,200 234 | GHCND:USW00094728,20130821,322,222 235 | GHCND:USW00094728,20130822,256,217 236 | GHCND:USW00094728,20130823,278,217 237 | GHCND:USW00094728,20130824,267,183 238 | GHCND:USW00094728,20130825,283,178 239 | GHCND:USW00094728,20130826,289,200 240 | GHCND:USW00094728,20130827,306,217 241 | GHCND:USW00094728,20130828,300,217 242 | GHCND:USW00094728,20130829,267,217 243 | GHCND:USW00094728,20130830,294,211 244 | GHCND:USW00094728,20130831,300,228 245 | GHCND:USW00094728,20130901,289,239 246 | GHCND:USW00094728,20130902,278,233 247 | GHCND:USW00094728,20130903,278,200 248 | GHCND:USW00094728,20130904,278,183 249 | GHCND:USW00094728,20130905,267,178 250 | GHCND:USW00094728,20130906,222,139 251 | GHCND:USW00094728,20130907,267,150 252 | GHCND:USW00094728,20130908,283,178 253 | GHCND:USW00094728,20130909,228,139 254 | GHCND:USW00094728,20130910,306,200 255 | GHCND:USW00094728,20130911,356,250 256 | GHCND:USW00094728,20130912,306,211 257 | GHCND:USW00094728,20130913,250,150 258 | GHCND:USW00094728,20130914,194,122 259 | GHCND:USW00094728,20130915,228,106 260 | GHCND:USW00094728,20130916,228,133 261 | GHCND:USW00094728,20130917,183,100 262 | GHCND:USW00094728,20130918,222,106 263 | GHCND:USW00094728,20130919,256,128 264 | GHCND:USW00094728,20130920,261,156 265 | GHCND:USW00094728,20130921,250,161 266 | GHCND:USW00094728,20130922,206,122 267 | GHCND:USW00094728,20130923,189,100 268 | GHCND:USW00094728,20130924,228,83 269 | GHCND:USW00094728,20130925,228,111 270 | GHCND:USW00094728,20130926,217,144 271 | GHCND:USW00094728,20130927,206,139 272 | GHCND:USW00094728,20130928,228,133 273 | GHCND:USW00094728,20130929,222,139 274 | GHCND:USW00094728,20130930,239,133 275 | GHCND:USW00094728,20131001,278,150 276 | GHCND:USW00094728,20131002,283,178 277 | GHCND:USW00094728,20131003,256,172 278 | GHCND:USW00094728,20131004,300,189 279 | GHCND:USW00094728,20131005,244,178 280 | GHCND:USW00094728,20131006,211,183 281 | GHCND:USW00094728,20131007,244,156 282 | GHCND:USW00094728,20131008,194,122 283 | GHCND:USW00094728,20131009,167,117 284 | GHCND:USW00094728,20131010,183,122 285 | GHCND:USW00094728,20131011,200,156 286 | GHCND:USW00094728,20131012,222,156 287 | GHCND:USW00094728,20131013,183,133 288 | GHCND:USW00094728,20131014,189,111 289 | GHCND:USW00094728,20131015,222,117 290 | GHCND:USW00094728,20131016,194,133 291 | GHCND:USW00094728,20131017,228,161 292 | GHCND:USW00094728,20131018,200,128 293 | GHCND:USW00094728,20131019,178,111 294 | GHCND:USW00094728,20131020,172,100 295 | GHCND:USW00094728,20131021,189,100 296 | GHCND:USW00094728,20131022,194,106 297 | GHCND:USW00094728,20131023,128,72 298 | GHCND:USW00094728,20131024,122,50 299 | GHCND:USW00094728,20131025,117,44 300 | GHCND:USW00094728,20131026,128,50 301 | GHCND:USW00094728,20131027,144,78 302 | GHCND:USW00094728,20131028,161,61 303 | GHCND:USW00094728,20131029,133,67 304 | GHCND:USW00094728,20131030,156,83 305 | GHCND:USW00094728,20131031,189,117 306 | GHCND:USW00094728,20131101,211,150 307 | GHCND:USW00094728,20131102,200,128 308 | GHCND:USW00094728,20131103,128,28 309 | GHCND:USW00094728,20131104,78,17 310 | GHCND:USW00094728,20131105,128,50 311 | GHCND:USW00094728,20131106,161,94 312 | GHCND:USW00094728,20131107,178,67 313 | GHCND:USW00094728,20131108,100,44 314 | GHCND:USW00094728,20131109,100,33 315 | GHCND:USW00094728,20131110,161,67 316 | GHCND:USW00094728,20131111,117,61 317 | GHCND:USW00094728,20131112,111,-5 318 | GHCND:USW00094728,20131113,39,-16 319 | GHCND:USW00094728,20131114,111,6 320 | GHCND:USW00094728,20131115,139,67 321 | GHCND:USW00094728,20131116,156,72 322 | GHCND:USW00094728,20131117,156,106 323 | GHCND:USW00094728,20131118,183,106 324 | GHCND:USW00094728,20131119,106,22 325 | GHCND:USW00094728,20131120,67,0 326 | GHCND:USW00094728,20131121,111,17 327 | GHCND:USW00094728,20131122,139,106 328 | GHCND:USW00094728,20131123,122,-5 329 | GHCND:USW00094728,20131124,-10,-49 330 | GHCND:USW00094728,20131125,17,-49 331 | GHCND:USW00094728,20131126,83,6 332 | GHCND:USW00094728,20131127,167,17 333 | GHCND:USW00094728,20131128,17,-10 334 | GHCND:USW00094728,20131129,39,-16 335 | GHCND:USW00094728,20131130,39,-38 336 | GHCND:USW00094728,20131201,94,22 337 | GHCND:USW00094728,20131202,94,50 338 | GHCND:USW00094728,20131203,117,33 339 | GHCND:USW00094728,20131204,111,50 340 | GHCND:USW00094728,20131205,156,89 341 | GHCND:USW00094728,20131206,167,28 342 | GHCND:USW00094728,20131207,50,0 343 | GHCND:USW00094728,20131208,6,-16 344 | GHCND:USW00094728,20131209,39,-5 345 | GHCND:USW00094728,20131210,28,-10 346 | GHCND:USW00094728,20131211,6,-27 347 | GHCND:USW00094728,20131212,-10,-49 348 | GHCND:USW00094728,20131213,17,-49 349 | GHCND:USW00094728,20131214,11,-55 350 | GHCND:USW00094728,20131215,44,-10 351 | GHCND:USW00094728,20131216,6,-38 352 | GHCND:USW00094728,20131217,0,-43 353 | GHCND:USW00094728,20131218,28,-49 354 | GHCND:USW00094728,20131219,83,-10 355 | GHCND:USW00094728,20131220,117,50 356 | GHCND:USW00094728,20131221,183,106 357 | GHCND:USW00094728,20131222,217,161 358 | GHCND:USW00094728,20131223,178,56 359 | GHCND:USW00094728,20131224,56,-32 360 | GHCND:USW00094728,20131225,-5,-71 361 | GHCND:USW00094728,20131226,22,-10 362 | GHCND:USW00094728,20131227,44,-5 363 | GHCND:USW00094728,20131228,128,22 364 | GHCND:USW00094728,20131229,89,50 365 | GHCND:USW00094728,20131230,72,-49 366 | GHCND:USW00094728,20131231,0,-60 367 | GHCND:USW00094728,20140101,6,-43 368 | -------------------------------------------------------------------------------- /notebooks/01-Ejercicios_basico.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "\"AeroPython\"" 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | "# 01-Ejercicios Básico" 15 | ] 16 | }, 17 | { 18 | "cell_type": "markdown", 19 | "metadata": {}, 20 | "source": [ 21 | "_En esta clase vamos a afianzar los conocimientos de Python que acabamos de adquirir haciendo algunos ejercicios, y así retener las peculiaridades de la sintaxis y aclarar algunos detalles a tener en cuenta cuando se trabaja en modo interactivo._" 22 | ] 23 | }, 24 | { 25 | "cell_type": "markdown", 26 | "metadata": {}, 27 | "source": [ 28 | "## Funciones" 29 | ] 30 | }, 31 | { 32 | "cell_type": "markdown", 33 | "metadata": {}, 34 | "source": [ 35 | "Lo más importante para programar, y no solo en Python, es saber organizar el código en piezas más pequeñas que hagan tareas independientes y combinarlas entre sí. Las **funciones** son el primer nivel de organización del código: reciben unas *entradas*, las *procesan* y devuelven unas *salidas*." 36 | ] 37 | }, 38 | { 39 | "cell_type": "markdown", 40 | "metadata": {}, 41 | "source": [ 42 | "![Black box](../static/blackbox.jpg)" 43 | ] 44 | }, 45 | { 46 | "cell_type": "markdown", 47 | "metadata": {}, 48 | "source": [ 49 | "### Ejercicio 1: Nuestra primera función" 50 | ] 51 | }, 52 | { 53 | "cell_type": "markdown", 54 | "metadata": {}, 55 | "source": [ 56 | "Vamos a crear ahora una función que compruebe si un número es mayor o menor que cinco. La función tiene que devolver una cadena de texto que diga: \n", 57 | "```\n", 58 | "El número es mayor/menor/igual que cinco\n", 59 | "```" 60 | ] 61 | }, 62 | { 63 | "cell_type": "code", 64 | "execution_count": null, 65 | "metadata": { 66 | "collapsed": false 67 | }, 68 | "outputs": [], 69 | "source": [] 70 | }, 71 | { 72 | "cell_type": "markdown", 73 | "metadata": {}, 74 | "source": [ 75 | "
¡No olvides los dos puntos! Si el sangrado del código no avanza automáticamente, es que te los has dejado.
" 76 | ] 77 | }, 78 | { 79 | "cell_type": "code", 80 | "execution_count": null, 81 | "metadata": { 82 | "collapsed": false 83 | }, 84 | "outputs": [], 85 | "source": [ 86 | "# Comprobamos que funciona\n" 87 | ] 88 | }, 89 | { 90 | "cell_type": "code", 91 | "execution_count": null, 92 | "metadata": { 93 | "collapsed": false 94 | }, 95 | "outputs": [], 96 | "source": [ 97 | "# Probamos a pasarle una variable\n" 98 | ] 99 | }, 100 | { 101 | "cell_type": "markdown", 102 | "metadata": {}, 103 | "source": [ 104 | "
Apuntes:\n", 105 | "\n", 106 | "\n", 109 | "\n", 110 | "
" 111 | ] 112 | }, 113 | { 114 | "cell_type": "markdown", 115 | "metadata": {}, 116 | "source": [ 117 | "### Ejercicio 2: Sumatorio" 118 | ] 119 | }, 120 | { 121 | "cell_type": "markdown", 122 | "metadata": {}, 123 | "source": [ 124 | "Vamos a escribir ahora una función que sume los `n` primeros números naturales. Observa que podemos escribir una **cadena de documentación** (_docstring_) justo debajo de la definición de la función para explicar lo que hace." 125 | ] 126 | }, 127 | { 128 | "cell_type": "code", 129 | "execution_count": null, 130 | "metadata": { 131 | "collapsed": false 132 | }, 133 | "outputs": [], 134 | "source": [] 135 | }, 136 | { 137 | "cell_type": "markdown", 138 | "metadata": {}, 139 | "source": [ 140 | "Lo que hemos hecho ha sido inicializar el valor de la suma a 0 e ir acumulando en ella los `num` primeros números naturales." 141 | ] 142 | }, 143 | { 144 | "cell_type": "code", 145 | "execution_count": null, 146 | "metadata": { 147 | "collapsed": false 148 | }, 149 | "outputs": [], 150 | "source": [ 151 | "# Comprobamos que funciona\n" 152 | ] 153 | }, 154 | { 155 | "cell_type": "code", 156 | "execution_count": null, 157 | "metadata": { 158 | "collapsed": false 159 | }, 160 | "outputs": [], 161 | "source": [ 162 | "# Pedimos que nos muestre la ayuda\n" 163 | ] 164 | }, 165 | { 166 | "cell_type": "markdown", 167 | "metadata": {}, 168 | "source": [ 169 | "
Observa lo que sucede si no inicializamos la suma:
" 170 | ] 171 | }, 172 | { 173 | "cell_type": "code", 174 | "execution_count": null, 175 | "metadata": { 176 | "collapsed": false 177 | }, 178 | "outputs": [], 179 | "source": [ 180 | "def sumatorio_mal(num):\n", 181 | " " 182 | ] 183 | }, 184 | { 185 | "cell_type": "code", 186 | "execution_count": null, 187 | "metadata": { 188 | "collapsed": false 189 | }, 190 | "outputs": [], 191 | "source": [ 192 | "sumatorio_mal(4)" 193 | ] 194 | }, 195 | { 196 | "cell_type": "markdown", 197 | "metadata": {}, 198 | "source": [ 199 | "Para comprobar el resultado correcto, nada como acudir a la función `sum` de Python, que suma los elementos que le pasemos:" 200 | ] 201 | }, 202 | { 203 | "cell_type": "code", 204 | "execution_count": null, 205 | "metadata": { 206 | "collapsed": false 207 | }, 208 | "outputs": [], 209 | "source": [] 210 | }, 211 | { 212 | "cell_type": "code", 213 | "execution_count": null, 214 | "metadata": { 215 | "collapsed": false 216 | }, 217 | "outputs": [], 218 | "source": [] 219 | }, 220 | { 221 | "cell_type": "markdown", 222 | "metadata": {}, 223 | "source": [ 224 | "### Ejercicio 3: Sumatorio con cota superior" 225 | ] 226 | }, 227 | { 228 | "cell_type": "markdown", 229 | "metadata": {}, 230 | "source": [ 231 | "Ahora nuestra función es un poco más rara: tiene que sumar números naturales consecutivos y no pasarse de un determinado límite. Además, queremos el valor de la suma." 232 | ] 233 | }, 234 | { 235 | "cell_type": "code", 236 | "execution_count": null, 237 | "metadata": { 238 | "collapsed": false 239 | }, 240 | "outputs": [], 241 | "source": [ 242 | "def suma_tope(tope):\n", 243 | " \"\"\"Suma números naturales consecutivos hasta un tope.\n", 244 | " \"\"\"\n", 245 | " " 246 | ] 247 | }, 248 | { 249 | "cell_type": "code", 250 | "execution_count": null, 251 | "metadata": { 252 | "collapsed": false 253 | }, 254 | "outputs": [], 255 | "source": [] 256 | }, 257 | { 258 | "cell_type": "code", 259 | "execution_count": null, 260 | "metadata": { 261 | "collapsed": false 262 | }, 263 | "outputs": [], 264 | "source": [ 265 | "# Comprobamos con ==\n", 266 | "suma_tope(9) == 1 + 2 + 3" 267 | ] 268 | }, 269 | { 270 | "cell_type": "markdown", 271 | "metadata": {}, 272 | "source": [ 273 | "### Ejercicio 4: método babilónico para encontrar la raíz de un número" 274 | ] 275 | }, 276 | { 277 | "cell_type": "markdown", 278 | "metadata": {}, 279 | "source": [ 280 | "Hallar $x = \\sqrt{S}$.\n", 281 | "\n", 282 | "1. $\\displaystyle \\tilde{x} \\leftarrow \\frac{S}{2}$.\n", 283 | "2. $\\displaystyle \\tilde{x} \\leftarrow \\frac{1}{2}\\left(\\tilde{x} + \\frac{S}{\\tilde{x}}\\right)$.\n", 284 | "3. Repetir (2) hasta que se alcance un límite de iteraciones o un criterio de convergencia.\n", 285 | "\n", 286 | "http://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method" 287 | ] 288 | }, 289 | { 290 | "cell_type": "code", 291 | "execution_count": null, 292 | "metadata": { 293 | "collapsed": false 294 | }, 295 | "outputs": [], 296 | "source": [] 297 | }, 298 | { 299 | "cell_type": "markdown", 300 | "metadata": {}, 301 | "source": [ 302 | "Aquí estoy usando un truco de la aritmética en punto flotante: como la convergencia se alcanza rápidamente, llega un momento en que el error es menor que la precisión de la máquina y el valor no cambia de un paso a otro." 303 | ] 304 | }, 305 | { 306 | "cell_type": "code", 307 | "execution_count": null, 308 | "metadata": { 309 | "collapsed": false 310 | }, 311 | "outputs": [], 312 | "source": [] 313 | }, 314 | { 315 | "cell_type": "markdown", 316 | "metadata": {}, 317 | "source": [ 318 | "
Se deja como ejercicio implementar otras condiciones de convergencia: error relativo por debajo de un umbral o número máximo de iteraciones.
" 319 | ] 320 | }, 321 | { 322 | "cell_type": "code", 323 | "execution_count": null, 324 | "metadata": { 325 | "collapsed": false 326 | }, 327 | "outputs": [], 328 | "source": [ 329 | "# Usamos la raiz implementada en Python\n" 330 | ] 331 | }, 332 | { 333 | "cell_type": "code", 334 | "execution_count": null, 335 | "metadata": { 336 | "collapsed": false 337 | }, 338 | "outputs": [], 339 | "source": [] 340 | }, 341 | { 342 | "cell_type": "code", 343 | "execution_count": null, 344 | "metadata": { 345 | "collapsed": false 346 | }, 347 | "outputs": [], 348 | "source": [] 349 | }, 350 | { 351 | "cell_type": "markdown", 352 | "metadata": {}, 353 | "source": [ 354 | "Ahora tienes curiosidad, ¿verdad? :) http://puntoflotante.org/" 355 | ] 356 | }, 357 | { 358 | "cell_type": "markdown", 359 | "metadata": {}, 360 | "source": [ 361 | "### Ejercicio 5: Serie de Fibonacci" 362 | ] 363 | }, 364 | { 365 | "cell_type": "markdown", 366 | "metadata": {}, 367 | "source": [ 368 | "Secuencia de Fibonacci: $F_n = F_{n - 1} + F_{n - 2}$, con $F_0 = 0$ y $F_1 = 1$.\n", 369 | "\n", 370 | "$$0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, ...$$" 371 | ] 372 | }, 373 | { 374 | "cell_type": "markdown", 375 | "metadata": {}, 376 | "source": [ 377 | "Con iteración:" 378 | ] 379 | }, 380 | { 381 | "cell_type": "code", 382 | "execution_count": null, 383 | "metadata": { 384 | "collapsed": false 385 | }, 386 | "outputs": [], 387 | "source": [ 388 | "def fib(n):\n", 389 | " a, b = 0, 1\n", 390 | " for i in range(n):\n", 391 | " a, b = b, a + b # Bendita asignación múltiple\n", 392 | " return a" 393 | ] 394 | }, 395 | { 396 | "cell_type": "code", 397 | "execution_count": null, 398 | "metadata": { 399 | "collapsed": false 400 | }, 401 | "outputs": [], 402 | "source": [ 403 | "fib(0), fib(3), fib(10)" 404 | ] 405 | }, 406 | { 407 | "cell_type": "markdown", 408 | "metadata": {}, 409 | "source": [ 410 | "---" 411 | ] 412 | }, 413 | { 414 | "cell_type": "markdown", 415 | "metadata": {}, 416 | "source": [ 417 | "_En esta clase hemos visto cómo crear funciones que encapsulen tareas de nuestro programa y las hemos aplicado para respondernos ciertas preguntas sencillas._\n", 418 | "\n", 419 | "**Referencias**\n", 420 | "\n", 421 | "* Libro \"Learn Python the Hard Way\" http://learnpythonthehardway.org/book/\n", 422 | "* Python Tutor, para visualizar código Python paso a paso http://pythontutor.com/\n", 423 | "* Libro \"How To Think Like a Computer Scientist\" http://interactivepython.org/runestone/static/thinkcspy/toc.html\n", 424 | "* Project Euler: ejercicios para aprender Python https://projecteuler.net/problems\n", 425 | "* Python Challenge (!) http://www.pythonchallenge.com/" 426 | ] 427 | }, 428 | { 429 | "cell_type": "markdown", 430 | "metadata": {}, 431 | "source": [ 432 | "Si te ha gustado esta clase:\n", 433 | "\n", 434 | "Tweet\n", 435 | "\n", 436 | "\n", 437 | "---" 438 | ] 439 | }, 440 | { 441 | "cell_type": "markdown", 442 | "metadata": {}, 443 | "source": [ 444 | "####

¡Síguenos en Twitter!" 445 | ] 446 | }, 447 | { 448 | "cell_type": "markdown", 449 | "metadata": {}, 450 | "source": [ 451 | "##### \"Licencia
Curso AeroPython por Juan Luis Cano Rodriguez y Alejandro Sáez Mollejo se distribuye bajo una Licencia Creative Commons Atribución 4.0 Internacional." 452 | ] 453 | }, 454 | { 455 | "cell_type": "markdown", 456 | "metadata": {}, 457 | "source": [ 458 | "##### " 459 | ] 460 | }, 461 | { 462 | "cell_type": "markdown", 463 | "metadata": {}, 464 | "source": [ 465 | "---\n", 466 | "_Las siguientes celdas contienen configuración del Notebook_\n", 467 | "\n", 468 | "_Para visualizar y utlizar los enlaces a Twitter el notebook debe ejecutarse como [seguro](http://ipython.org/ipython-doc/dev/notebook/security.html)_\n", 469 | "\n", 470 | " File > Trusted Notebook" 471 | ] 472 | }, 473 | { 474 | "cell_type": "code", 475 | "execution_count": 4, 476 | "metadata": { 477 | "collapsed": false 478 | }, 479 | "outputs": [ 480 | { 481 | "data": { 482 | "text/html": [ 483 | "/* This template is inspired in the one used by Lorena Barba\n", 484 | "in the numerical-mooc repository: https://github.com/numerical-mooc/numerical-mooc\n", 485 | "We thank her work and hope you also enjoy the look of the notobooks with this style */\n", 486 | "\n", 487 | "\n", 488 | "\n", 489 | "El estilo se ha aplicado =)\n", 490 | "\n", 491 | "\n", 628 | "\n" 644 | ], 645 | "text/plain": [ 646 | "" 647 | ] 648 | }, 649 | "execution_count": 4, 650 | "metadata": {}, 651 | "output_type": "execute_result" 652 | } 653 | ], 654 | "source": [ 655 | "# Esta celda da el estilo al notebook\n", 656 | "from IPython.core.display import HTML\n", 657 | "css_file = '../static/styles/style.css'\n", 658 | "HTML(open(css_file, \"r\").read())" 659 | ] 660 | } 661 | ], 662 | "metadata": { 663 | "anaconda-cloud": {}, 664 | "kernelspec": { 665 | "display_name": "Python [default]", 666 | "language": "python", 667 | "name": "python3" 668 | }, 669 | "language_info": { 670 | "codemirror_mode": { 671 | "name": "ipython", 672 | "version": 3 673 | }, 674 | "file_extension": ".py", 675 | "mimetype": "text/x-python", 676 | "name": "python", 677 | "nbconvert_exporter": "python", 678 | "pygments_lexer": "ipython3", 679 | "version": "3.5.2" 680 | } 681 | }, 682 | "nbformat": 4, 683 | "nbformat_minor": 0 684 | } 685 | -------------------------------------------------------------------------------- /notebooks/04-SciPy.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "\"AeroPython\"" 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | "# 04-SciPy " 15 | ] 16 | }, 17 | { 18 | "cell_type": "markdown", 19 | "metadata": {}, 20 | "source": [ 21 | "_`SciPy` es paquete que incluye una colección de algoritmos matemáticos y funciones construidas sobre el paquete `NumPy`. En definitiva, nos proporciona una serie de comandos de alto nivel para realizar acciones como:_\n", 22 | "\n", 23 | "* Integración numérca.\n", 24 | "* Resolver un sistema de ecuaciones por el _método de Newton_, bisección...\n", 25 | "* Resolver EDOs.\n", 26 | " \n", 27 | "_Incluso otras más especializadas como:_\n", 28 | "\n", 29 | "* [Ajuste e interpolación de datos.](http://pybonacci.wordpress.com/2013/08/15/ajuste-e-interpolacion-unidimensionales-basicos-en-python-con-scipy/)\n", 30 | "* [Teoría de control](http://pybonacci.wordpress.com/2013/10/10/teoria-de-control-en-python-con-scipy-i/).\n", 31 | "* Procesamiento de imágenes.\n", 32 | "* Estadística.\n", 33 | "* ...\n", 34 | "\n", 35 | "_Cada una de estas tareas pertenece a un subpaquete distinto de `SciPy`. __En la clase de hoy nos centraremos en las tres primeras__, pero seguro que si quieres hincarle el diente a alguna de las otras no tienes ningún problema._\n", 36 | "\n", 37 | "\n", 38 | "\n", 39 | "| Subpackage | Description |\n", 40 | "| ---------- | :--- |\n", 41 | "| constants | Physical and mathematical constants |\n", 42 | "| integrate | Integration and ordinary differential equation solvers |\n", 43 | "| interpolate |\tInterpolation and smoothing splines |\n", 44 | "| linalg |\tLinear algebra |\n", 45 | "| signal |\tSignal processing |\n", 46 | "| special | \tSpecial functions |\n", 47 | "| stats |\tStatistical distributions and functions |" 48 | ] 49 | }, 50 | { 51 | "cell_type": "markdown", 52 | "metadata": {}, 53 | "source": [ 54 | "# scipy.integrate" 55 | ] 56 | }, 57 | { 58 | "cell_type": "markdown", 59 | "metadata": {}, 60 | "source": [ 61 | "Este subpaquete de `SciPy` proporciona algunas __técnicas de integración tanto de funciones como de ecuaciones diferenciales__. En primer lugar importémoslo y ejecutemos la ayuda para ver cuáles son estas funciones:" 62 | ] 63 | }, 64 | { 65 | "cell_type": "code", 66 | "execution_count": null, 67 | "metadata": { 68 | "collapsed": true 69 | }, 70 | "outputs": [], 71 | "source": [] 72 | }, 73 | { 74 | "cell_type": "code", 75 | "execution_count": null, 76 | "metadata": { 77 | "collapsed": false 78 | }, 79 | "outputs": [], 80 | "source": [] 81 | }, 82 | { 83 | "cell_type": "code", 84 | "execution_count": null, 85 | "metadata": { 86 | "collapsed": false 87 | }, 88 | "outputs": [], 89 | "source": [ 90 | "from IPython.display import HTML\n", 91 | "HTML('')" 92 | ] 93 | }, 94 | { 95 | "cell_type": "markdown", 96 | "metadata": {}, 97 | "source": [ 98 | "### quad" 99 | ] 100 | }, 101 | { 102 | "cell_type": "markdown", 103 | "metadata": {}, 104 | "source": [ 105 | "Como se puede ver en la ayuda, si queremos realizar una _integración numérica_ de una función de una variable, debemos utilizar `quad` (aunque también podemos usar `trapz`, `simps`... La forma de acceder a ella tal y como hemos importado el paquete sería ejecutando `integrate.quad`. Sin emabrgo, sería más normal importar del siguiete modo:" 106 | ] 107 | }, 108 | { 109 | "cell_type": "code", 110 | "execution_count": null, 111 | "metadata": { 112 | "collapsed": false 113 | }, 114 | "outputs": [], 115 | "source": [] 116 | }, 117 | { 118 | "cell_type": "markdown", 119 | "metadata": {}, 120 | "source": [ 121 | "De este modo se puede usar la función quad, simplemente como `quad`. Pero todavía no sabemos cómo funciona, ¿te atreves a investigarlo tú?" 122 | ] 123 | }, 124 | { 125 | "cell_type": "markdown", 126 | "metadata": {}, 127 | "source": [ 128 | "Quizá [esta ayuda](http://docs.scipy.org/doc/scipy/reference/integrate.html#module-scipy.integrate) te resulte más atractiva.\n", 129 | "\n", 130 | "¿Qué es lo primero que necesitamos hacer para integrar una función? Pues sí, la función... definamos una:\n", 131 | "\n", 132 | "$$f(x) = x \\cdot sin(x)$$" 133 | ] 134 | }, 135 | { 136 | "cell_type": "code", 137 | "execution_count": null, 138 | "metadata": { 139 | "collapsed": false 140 | }, 141 | "outputs": [], 142 | "source": [] 143 | }, 144 | { 145 | "cell_type": "markdown", 146 | "metadata": {}, 147 | "source": [ 148 | "#### Ahora integremos con `quad`" 149 | ] 150 | }, 151 | { 152 | "cell_type": "markdown", 153 | "metadata": {}, 154 | "source": [ 155 | "__Integremos__ la función en el intervalo $[2, 9]$. Recuerda que esto te calcula la integral, _no el área_:" 156 | ] 157 | }, 158 | { 159 | "cell_type": "code", 160 | "execution_count": null, 161 | "metadata": { 162 | "collapsed": false 163 | }, 164 | "outputs": [], 165 | "source": [] 166 | }, 167 | { 168 | "cell_type": "markdown", 169 | "metadata": {}, 170 | "source": [ 171 | "#### Simpson y trapecio" 172 | ] 173 | }, 174 | { 175 | "cell_type": "markdown", 176 | "metadata": {}, 177 | "source": [ 178 | "Según figura en la documentación a estos métodos hay que pasarles las coordenadas de los puntos (no la función). Esto puede ser útil si no disponemos de una función, sino de una serie da valores, que por ejemplo, provienen de un experimento.\n", 179 | "\n", 180 | "![](../static/trap_xsinx.png)" 181 | ] 182 | }, 183 | { 184 | "cell_type": "markdown", 185 | "metadata": {}, 186 | "source": [ 187 | "## Ecuaciones no lineales" 188 | ] 189 | }, 190 | { 191 | "cell_type": "markdown", 192 | "metadata": {}, 193 | "source": [ 194 | "Visto cómo resolver sistemas de ecuaciones lineales, tal vez sea incluso más atractivo resolver ecuaciones no lineales. Para ello, importaremos el paquete `optimize` de SciPy:" 195 | ] 196 | }, 197 | { 198 | "cell_type": "code", 199 | "execution_count": null, 200 | "metadata": { 201 | "collapsed": false 202 | }, 203 | "outputs": [], 204 | "source": [] 205 | }, 206 | { 207 | "cell_type": "markdown", 208 | "metadata": {}, 209 | "source": [ 210 | "La ayuda de este paquete es bastante larga (puedes consultarla también en http://docs.scipy.org/doc/scipy/reference/tutorial/optimize.html). El paquete `optimize` incluye multitud de métodos para **optimización**, **ajuste de curvas** y **búsqueda de raíces**. Vamos a centrarnos ahora en la búsqueda de raíces de funciones escalares. Para más información puedes leer http://pybonacci.org/2012/10/25/como-resolver-ecuaciones-algebraicas-en-python-con-scipy/" 211 | ] 212 | }, 213 | { 214 | "cell_type": "markdown", 215 | "metadata": {}, 216 | "source": [ 217 | "
**Nota**: La función `root` se utiliza para hallar soluciones de *sistemas* de ecuaciones no lineales así que obviamente también funciona para ecuaciones escalares. No obstante, vamos a utilizar las funciones `brentq` y `newton` para que el método utilizado quede más claro.
" 218 | ] 219 | }, 220 | { 221 | "cell_type": "markdown", 222 | "metadata": {}, 223 | "source": [ 224 | "Hay básicamente dos tipos de algoritmos para hallar raíces de ecuaciones no lineales:\n", 225 | "\n", 226 | "* Aquellos que operan en un intervalo $[a, b]$ tal que $f(a) \\cdot f(b) < 0$. Más lentos, convergencia asegurada.\n", 227 | "* Aquellos que operan dando una condición inicial $x_0$ más o menos cerca de la solución. Más rápidos, convergencia condicionada.\n", 228 | "\n", 229 | "De los primeros vamos a usar la función `brentq` (aunque podríamos usar `bisect`) y de los segundos vamos a usar `newton` (que en realidad engloba los métodos de Newton y de la secante)." 230 | ] 231 | }, 232 | { 233 | "cell_type": "markdown", 234 | "metadata": {}, 235 | "source": [ 236 | "**Ejemplo**:\n", 237 | "\n", 238 | "$\\ln{x} = \\sin{x} \\Rightarrow F(x) \\equiv \\ln{x} - \\sin{x} = 0$" 239 | ] 240 | }, 241 | { 242 | "cell_type": "markdown", 243 | "metadata": {}, 244 | "source": [ 245 | "Lo primero que tengo que hacer es definir la ecuación, que matemáticamente será una función $F(x)$ que quiero igualar a cero." 246 | ] 247 | }, 248 | { 249 | "cell_type": "code", 250 | "execution_count": null, 251 | "metadata": { 252 | "collapsed": false 253 | }, 254 | "outputs": [], 255 | "source": [] 256 | }, 257 | { 258 | "cell_type": "markdown", 259 | "metadata": {}, 260 | "source": [ 261 | "Para hacernos una idea de las posibles soluciones siempre podemos representar gráficamente esa función:" 262 | ] 263 | }, 264 | { 265 | "cell_type": "code", 266 | "execution_count": null, 267 | "metadata": { 268 | "collapsed": false 269 | }, 270 | "outputs": [], 271 | "source": [ 272 | "x = np.linspace(0, 10, num=100)\n", 273 | "plt.plot(x, F(x), 'k', lw=2, label=\"$F(x)$\")\n", 274 | "plt.plot(x, np.log(x), label=\"$\\log{x}$\")\n", 275 | "plt.plot(x, np.sin(x), label=\"$\\sin{x}$\")\n", 276 | "plt.plot(x, np.zeros_like(x), 'k--')\n", 277 | "plt.legend(loc=4)" 278 | ] 279 | }, 280 | { 281 | "cell_type": "markdown", 282 | "metadata": {}, 283 | "source": [ 284 | "Y utilizando por ejemplo el método de Brent en el intervalo $[0, 3]$:" 285 | ] 286 | }, 287 | { 288 | "cell_type": "code", 289 | "execution_count": null, 290 | "metadata": { 291 | "collapsed": false 292 | }, 293 | "outputs": [], 294 | "source": [] 295 | }, 296 | { 297 | "cell_type": "markdown", 298 | "metadata": {}, 299 | "source": [ 300 | "### Argumentos extra" 301 | ] 302 | }, 303 | { 304 | "cell_type": "markdown", 305 | "metadata": {}, 306 | "source": [ 307 | "Nuestras funciones siempre tienen que tomar como primer argumento la incógnita, el valor que la hace cero. Si queremos incluir más, tendremos que usar el argumento `args` de la funciones de búsqueda de raíces. Este patrón se usa también en otras partes de SciPy, como ya veremos." 308 | ] 309 | }, 310 | { 311 | "cell_type": "markdown", 312 | "metadata": {}, 313 | "source": [ 314 | "Vamos a resolver ahora una ecuación que depende de un parámetro:" 315 | ] 316 | }, 317 | { 318 | "cell_type": "markdown", 319 | "metadata": {}, 320 | "source": [ 321 | "$$\\sqrt{x} + \\log{x} = C$$." 322 | ] 323 | }, 324 | { 325 | "cell_type": "code", 326 | "execution_count": null, 327 | "metadata": { 328 | "collapsed": false 329 | }, 330 | "outputs": [], 331 | "source": [ 332 | "def G(x, C):\n", 333 | " return C - np.sqrt(x) - np.log(x)" 334 | ] 335 | }, 336 | { 337 | "cell_type": "markdown", 338 | "metadata": {}, 339 | "source": [ 340 | "**Nuestra incógnita sigue siendo $x$**, así que debe ir en primer lugar. El resto de parámetros van a continuación, y sus valores se especifican a la hora de resolver la ecuación usando `args`:" 341 | ] 342 | }, 343 | { 344 | "cell_type": "code", 345 | "execution_count": null, 346 | "metadata": { 347 | "collapsed": false 348 | }, 349 | "outputs": [], 350 | "source": [ 351 | "optimize.newton(G, 2.0, args=(2,))" 352 | ] 353 | }, 354 | { 355 | "cell_type": "markdown", 356 | "metadata": {}, 357 | "source": [ 358 | "### Flujo compresible" 359 | ] 360 | }, 361 | { 362 | "cell_type": "markdown", 363 | "metadata": {}, 364 | "source": [ 365 | "Esta es la relación isentrópica entre el número de Mach $M(x)$ en un conducto de área $A(x)$:" 366 | ] 367 | }, 368 | { 369 | "cell_type": "markdown", 370 | "metadata": {}, 371 | "source": [ 372 | "$$ \\frac{A(x)}{A^*} = \\frac{1}{M(x)} \\left( \\frac{2}{1 + \\gamma} \\left( 1 + \\frac{\\gamma - 1}{2} M(x)^2 \\right) \\right)^{\\frac{\\gamma + 1}{2 (\\gamma - 1)}}$$" 373 | ] 374 | }, 375 | { 376 | "cell_type": "markdown", 377 | "metadata": {}, 378 | "source": [ 379 | "Para un conducto convergente:\n", 380 | "\n", 381 | "$$ \\frac{A(x)}{A^*} = 3 - 2 x \\quad x \\in [0, 1]$$" 382 | ] 383 | }, 384 | { 385 | "cell_type": "markdown", 386 | "metadata": {}, 387 | "source": [ 388 | "Hallar el número de Mach en la sección $x = 0.9$." 389 | ] 390 | }, 391 | { 392 | "cell_type": "code", 393 | "execution_count": null, 394 | "metadata": { 395 | "collapsed": false 396 | }, 397 | "outputs": [], 398 | "source": [ 399 | "def A(x):\n", 400 | " return 3 - 2 * x\n", 401 | "\n", 402 | "x = np.linspace(0, 1)\n", 403 | "area = A(x)\n", 404 | "r = np.sqrt(area / np.pi)\n", 405 | "plt.fill_between(x, r, -r, color=\"#ffcc00\")" 406 | ] 407 | }, 408 | { 409 | "cell_type": "markdown", 410 | "metadata": {}, 411 | "source": [ 412 | "¿Cuál es la función $F$ ahora? Hay dos opciones: definir una función $F_{0.9}(M)$ que me da el número de Mach en la sección $0.9$ o una función $F(M; x)$ con la que puedo hallar el número de Mach en cualquier sección. *Bonus points* si haces la segunda opción :)" 413 | ] 414 | }, 415 | { 416 | "cell_type": "markdown", 417 | "metadata": {}, 418 | "source": [ 419 | "Para resolver la ecuación utiliza el método de Brent (bisección). ¿En qué intervalo se encontrará la solución? ¡Si no te haces una idea es tan fácil como pintar la función $F$!" 420 | ] 421 | }, 422 | { 423 | "cell_type": "code", 424 | "execution_count": null, 425 | "metadata": { 426 | "collapsed": false 427 | }, 428 | "outputs": [], 429 | "source": [ 430 | "def F(M, x, g):\n", 431 | " return A(x) - (1 / M) * ((2 / (1 + g)) * (1 + (g - 1) / 2 * M ** 2)) ** ((g + 1) / (2 * (g - 1))) " 432 | ] 433 | }, 434 | { 435 | "cell_type": "code", 436 | "execution_count": null, 437 | "metadata": { 438 | "collapsed": false 439 | }, 440 | "outputs": [], 441 | "source": [ 442 | "optimize.brentq(F, 0.01, 1, args=(0.9, 1.4))" 443 | ] 444 | }, 445 | { 446 | "cell_type": "markdown", 447 | "metadata": {}, 448 | "source": [ 449 | "### Ecuación de Kepler" 450 | ] 451 | }, 452 | { 453 | "cell_type": "markdown", 454 | "metadata": {}, 455 | "source": [ 456 | "Representar la ecuación de Kepler\n", 457 | "\n", 458 | "$$M = E - e \\sin E$$\n", 459 | "\n", 460 | "que relaciona dos parámetros geométricos de las órbitas elípticas, la anomalía media $M$ y la anomalía excéntrica $E$.\n", 461 | "\n", 462 | "![Anomalías excéntrica y media](http://upload.wikimedia.org/wikipedia/commons/thumb/f/f8/Kepler%27s-equation-scheme.png/250px-Kepler%27s-equation-scheme.png)\n", 463 | "\n", 464 | "para los siguientes valores de excentricidad:\n", 465 | "\n", 466 | "* Tierra: $0.0167$\n", 467 | "* Plutón: $0.249$\n", 468 | "* Cometa Holmes: $0.432$\n", 469 | "* 28P/Neujmin: $0.775$\n", 470 | "* Cometa Halley: $0.967$\n", 471 | "\n", 472 | "Para reproducir esta gráfica:" 473 | ] 474 | }, 475 | { 476 | "cell_type": "code", 477 | "execution_count": null, 478 | "metadata": { 479 | "collapsed": false 480 | }, 481 | "outputs": [], 482 | "source": [ 483 | "from IPython.display import HTML\n", 484 | "HTML('')" 485 | ] 486 | }, 487 | { 488 | "cell_type": "markdown", 489 | "metadata": {}, 490 | "source": [ 491 | "Para ello utilizaremos el método de Newton (secante).\n", 492 | "\n", 493 | "1- Define la función correspondiente a la ecuación de Kepler, que no solo es una ecuación implícita sino que además depende de un parámetro. ¿Cuál es la incógnita?" 494 | ] 495 | }, 496 | { 497 | "cell_type": "code", 498 | "execution_count": null, 499 | "metadata": { 500 | "collapsed": false 501 | }, 502 | "outputs": [], 503 | "source": [ 504 | "def F(E, e, M):\n", 505 | " return M - E + e * np.sin(E)" 506 | ] 507 | }, 508 | { 509 | "cell_type": "markdown", 510 | "metadata": {}, 511 | "source": [ 512 | "2- Como primer paso, resuélvela para la excentricidad terrerestre y anomalía media $M = 0.3$. ¿Qué valor escogerías como condición inicial?" 513 | ] 514 | }, 515 | { 516 | "cell_type": "code", 517 | "execution_count": null, 518 | "metadata": { 519 | "collapsed": false 520 | }, 521 | "outputs": [], 522 | "source": [ 523 | "optimize.newton(F, 0.3, args=(0.0167, 0.3))" 524 | ] 525 | }, 526 | { 527 | "cell_type": "markdown", 528 | "metadata": {}, 529 | "source": [ 530 | "3- Como siguiente paso, crea un dominio (`linspace`) de anomalías medias entre $0$ y $2 \\pi$ y resuelve la ecuación de Kepler con excentricidad terrestre para todos esos valores. Fíjate que necesitarás un array donde almacenar las soluciones. Representa la curva resultante." 531 | ] 532 | }, 533 | { 534 | "cell_type": "code", 535 | "execution_count": null, 536 | "metadata": { 537 | "collapsed": false 538 | }, 539 | "outputs": [], 540 | "source": [ 541 | "N = 500\n", 542 | "\n", 543 | "M = np.linspace(0, 2 * np.pi, N)\n", 544 | "sol = np.zeros_like(M)\n", 545 | "\n", 546 | "for ii in range(N):\n", 547 | " sol[ii] = optimize.newton(F, sol[ii - 1], args=(0.249, M[ii]))\n", 548 | "\n", 549 | "plt.plot(M, sol)" 550 | ] 551 | }, 552 | { 553 | "cell_type": "markdown", 554 | "metadata": {}, 555 | "source": [ 556 | "4- Como último paso, solo tienes que meter parte del código que ya has escrito en un bucle que cambie el valor de la excentricidad 5 veces. Es aconsejable que tengas todo ese código en una única celda (esta de aquí abajo)." 557 | ] 558 | }, 559 | { 560 | "cell_type": "markdown", 561 | "metadata": {}, 562 | "source": [ 563 | "Vamos a introducir aquí un truco muy útil en Python:" 564 | ] 565 | }, 566 | { 567 | "cell_type": "code", 568 | "execution_count": null, 569 | "metadata": { 570 | "collapsed": false 571 | }, 572 | "outputs": [], 573 | "source": [ 574 | "M = np.linspace(0, 2 * np.pi, N)\n", 575 | "sol = np.zeros_like(M)\n", 576 | "\n", 577 | "plt.figure(figsize=(6, 6))\n", 578 | "\n", 579 | "for ee in 0.0167, 0.249, 0.432, 0.775, 0.967:\n", 580 | " # Para cada valor de excentricidad sobreescribimos el array sol\n", 581 | " for ii in range(N):\n", 582 | " sol[ii] = optimize.newton(F, sol[ii - 1], args=(ee, M[ii]))\n", 583 | " plt.plot(M, sol)\n", 584 | "\n", 585 | "plt.xlim(0, 2 * np.pi)\n", 586 | "plt.ylim(0, 2 * np.pi)\n", 587 | "plt.xlabel(\"$M$\", fontsize=15)\n", 588 | "plt.ylabel(\"$E$\", fontsize=15)\n", 589 | "plt.gca().set_aspect(1)\n", 590 | "plt.grid(True)\n", 591 | "plt.legend([\"Earth\", \"Pluto\", \"Comet Holmes\", \"28P/Neujmin\", \"Halley's Comet\"], loc=2)\n", 592 | "plt.title(\"Kepler's equation solutions\")" 593 | ] 594 | }, 595 | { 596 | "cell_type": "markdown", 597 | "metadata": {}, 598 | "source": [ 599 | "## Ecuaciones diferenciales ordinarias" 600 | ] 601 | }, 602 | { 603 | "cell_type": "markdown", 604 | "metadata": {}, 605 | "source": [ 606 | "Para integrar EDOs vamos a usar la función `odeint` del paquete `integrate`, que permite integrar sistemas del tipo:\n", 607 | "\n", 608 | "$$ \\frac{d\\mathbf{y}}{dt}=\\mathbf{f}\\left(\\mathbf{y},t\\right)$$\n", 609 | "\n", 610 | "con condiciones iniciales $\\mathbf{y}(\\mathbf{0}) = \\mathbf{y_0}$." 611 | ] 612 | }, 613 | { 614 | "cell_type": "markdown", 615 | "metadata": {}, 616 | "source": [ 617 | "
**¡Importante!**: La función del sistema recibe como primer argumento $\\mathbf{y}$ (un array) y como segundo argumento el instante $t$ (un escalar). Esta convención va exactamente al revés que en MATLAB y si se hace al revés obtendremos errores o, lo que es peor, resultados incorrectos.
" 618 | ] 619 | }, 620 | { 621 | "cell_type": "code", 622 | "execution_count": null, 623 | "metadata": { 624 | "collapsed": false 625 | }, 626 | "outputs": [], 627 | "source": [] 628 | }, 629 | { 630 | "cell_type": "markdown", 631 | "metadata": {}, 632 | "source": [ 633 | "Vamos a integrar primero una EDO elemental, cuya solución ya conocemos:\n", 634 | "\n", 635 | "$$y' + y = 0$$\n", 636 | "\n", 637 | "$$f(y, t) = \\frac{dy}{dt} = -y$$" 638 | ] 639 | }, 640 | { 641 | "cell_type": "code", 642 | "execution_count": null, 643 | "metadata": { 644 | "collapsed": false 645 | }, 646 | "outputs": [], 647 | "source": [] 648 | }, 649 | { 650 | "cell_type": "markdown", 651 | "metadata": {}, 652 | "source": [ 653 | "Condiciones iniciales:" 654 | ] 655 | }, 656 | { 657 | "cell_type": "code", 658 | "execution_count": null, 659 | "metadata": { 660 | "collapsed": false 661 | }, 662 | "outputs": [], 663 | "source": [] 664 | }, 665 | { 666 | "cell_type": "markdown", 667 | "metadata": {}, 668 | "source": [ 669 | "Vector de tiempos donde realizamos la integración:" 670 | ] 671 | }, 672 | { 673 | "cell_type": "code", 674 | "execution_count": null, 675 | "metadata": { 676 | "collapsed": false 677 | }, 678 | "outputs": [], 679 | "source": [] 680 | }, 681 | { 682 | "cell_type": "markdown", 683 | "metadata": {}, 684 | "source": [ 685 | "Integramos y representamos la solución:" 686 | ] 687 | }, 688 | { 689 | "cell_type": "code", 690 | "execution_count": null, 691 | "metadata": { 692 | "collapsed": false 693 | }, 694 | "outputs": [], 695 | "source": [] 696 | }, 697 | { 698 | "cell_type": "markdown", 699 | "metadata": {}, 700 | "source": [ 701 | "### EDOs de orden superior" 702 | ] 703 | }, 704 | { 705 | "cell_type": "markdown", 706 | "metadata": {}, 707 | "source": [ 708 | "Tendremos que acordarnos ahora de cómo reducir las ecuaciones de orden. De nuevo, vamos a probar con un ejemplo académico:" 709 | ] 710 | }, 711 | { 712 | "cell_type": "markdown", 713 | "metadata": {}, 714 | "source": [ 715 | "$$y + y'' = 0$$\n", 716 | "\n", 717 | "$$\\mathbf{y} \\leftarrow \\pmatrix{y \\\\ y'}$$\n", 718 | "\n", 719 | "$$\\mathbf{f}(\\mathbf{y}) = \\frac{d\\mathbf{y}}{dt} = \\pmatrix{y \\\\ y'}' = \\pmatrix{y' \\\\ y''} = \\pmatrix{y' \\\\ -y}$$" 720 | ] 721 | }, 722 | { 723 | "cell_type": "code", 724 | "execution_count": null, 725 | "metadata": { 726 | "collapsed": false 727 | }, 728 | "outputs": [], 729 | "source": [ 730 | "def f(y, t):\n", 731 | " return np.array([y[1], -y[0]])\n", 732 | "\n", 733 | "t = np.linspace(0, 10)\n", 734 | "y0 = np.array([1.0, 0.0])\n", 735 | "\n", 736 | "sol = odeint(f, y0, t)\n", 737 | "\n", 738 | "plt.plot(t, sol[:, 0], label='$y$')\n", 739 | "plt.plot(t, sol[:, 1], '--k', label='$\\dot{y}$')\n", 740 | "plt.legend()" 741 | ] 742 | }, 743 | { 744 | "cell_type": "markdown", 745 | "metadata": {}, 746 | "source": [ 747 | "## Para ampliar\n", 748 | "\n", 749 | "En nuestra edición anterior del curso de AeroPython puedes ver una aplicación muy interesante de lo que hemos visto hasta ahora al **salto de Felix Baumgartner**. ¡Aquí lo tienes!\n", 750 | "\n", 751 | "http://nbviewer.ipython.org/github/AeroPython/Curso_AeroPython/blob/v1.0/Notebooks/Clase6b_Finale.ipynb\n", 752 | "\n", 753 | "$$\\displaystyle m \\frac{d^2 y}{d t^2} = -m g + D$$" 754 | ] 755 | }, 756 | { 757 | "cell_type": "markdown", 758 | "metadata": {}, 759 | "source": [ 760 | "Si te ha gustado esta clase:\n", 761 | "\n", 762 | "Tweet\n", 763 | "\n", 764 | "\n", 765 | "---" 766 | ] 767 | }, 768 | { 769 | "cell_type": "markdown", 770 | "metadata": {}, 771 | "source": [ 772 | "####

¡Síguenos en Twitter!" 773 | ] 774 | }, 775 | { 776 | "cell_type": "markdown", 777 | "metadata": {}, 778 | "source": [ 779 | "##### \"Licencia
Curso AeroPython por Juan Luis Cano Rodriguez y Alejandro Sáez Mollejo se distribuye bajo una Licencia Creative Commons Atribución 4.0 Internacional." 780 | ] 781 | }, 782 | { 783 | "cell_type": "markdown", 784 | "metadata": {}, 785 | "source": [ 786 | "##### " 787 | ] 788 | }, 789 | { 790 | "cell_type": "markdown", 791 | "metadata": {}, 792 | "source": [ 793 | "---\n", 794 | "_Las siguientes celdas contienen configuración del Notebook_\n", 795 | "\n", 796 | "_Para visualizar y utlizar los enlaces a Twitter el notebook debe ejecutarse como [seguro](http://ipython.org/ipython-doc/dev/notebook/security.html)_\n", 797 | "\n", 798 | " File > Trusted Notebook" 799 | ] 800 | }, 801 | { 802 | "cell_type": "code", 803 | "execution_count": null, 804 | "metadata": { 805 | "collapsed": false 806 | }, 807 | "outputs": [], 808 | "source": [ 809 | "# Esta celda da el estilo al notebook\n", 810 | "from IPython.core.display import HTML\n", 811 | "css_file = '../static/styles/style.css'\n", 812 | "HTML(open(css_file, \"r\").read())" 813 | ] 814 | } 815 | ], 816 | "metadata": { 817 | "anaconda-cloud": {}, 818 | "kernelspec": { 819 | "display_name": "Python [default]", 820 | "language": "python", 821 | "name": "python3" 822 | }, 823 | "language_info": { 824 | "codemirror_mode": { 825 | "name": "ipython", 826 | "version": 3 827 | }, 828 | "file_extension": ".py", 829 | "mimetype": "text/x-python", 830 | "name": "python", 831 | "nbconvert_exporter": "python", 832 | "pygments_lexer": "ipython3", 833 | "version": "3.5.2" 834 | } 835 | }, 836 | "nbformat": 4, 837 | "nbformat_minor": 0 838 | } 839 | -------------------------------------------------------------------------------- /notebooks/03-Matplotlib.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "\"AeroPython\"" 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | "# 03-Matplotlib" 15 | ] 16 | }, 17 | { 18 | "cell_type": "markdown", 19 | "metadata": {}, 20 | "source": [ 21 | "_Después de estudiar la sintaxis de Python y empezar a manejar datos numéricos de manera un poco más profesional, ha llegado el momento de visualizarlos. Con la biblioteca **matplotlib** podemos crear gráficos de muy alta calidad y altamente personalizables._\n", 22 | "\n", 23 | "_matplotlib es una biblioteca muy potente que requiere tiempo de práctica para dominarla. Vamos a empezar por lo más sencillo._" 24 | ] 25 | }, 26 | { 27 | "cell_type": "markdown", 28 | "metadata": {}, 29 | "source": [ 30 | "## ¿Qué es matplotlib?" 31 | ] 32 | }, 33 | { 34 | "cell_type": "markdown", 35 | "metadata": {}, 36 | "source": [ 37 | "* Estándar *de facto* para visualización en Python\n", 38 | "* Pretende ser similar a las funciones de visualización de MATLAB\n", 39 | "* Diferentes formas de usarla: interfaz `pyplot` y orientada a objetos" 40 | ] 41 | }, 42 | { 43 | "cell_type": "markdown", 44 | "metadata": {}, 45 | "source": [ 46 | "Lo primero que vamos a hacer es activar el modo *inline* - de esta manera las figuras aparecerán automáticamente incrustadas en el notebook." 47 | ] 48 | }, 49 | { 50 | "cell_type": "code", 51 | "execution_count": null, 52 | "metadata": { 53 | "collapsed": false 54 | }, 55 | "outputs": [], 56 | "source": [ 57 | "# Modo inline para notebook\n" 58 | ] 59 | }, 60 | { 61 | "cell_type": "markdown", 62 | "metadata": {}, 63 | "source": [ 64 | "Importamos los paquetes necesarios:" 65 | ] 66 | }, 67 | { 68 | "cell_type": "code", 69 | "execution_count": null, 70 | "metadata": { 71 | "collapsed": false 72 | }, 73 | "outputs": [], 74 | "source": [] 75 | }, 76 | { 77 | "cell_type": "markdown", 78 | "metadata": {}, 79 | "source": [ 80 | "La biblioteca matplotlib es gigantesca y es difícil hacerse una idea global de todas sus posibilidades en una primera toma de contacto. Es recomendable tener a mano la documentación y la galería (http://matplotlib.org/gallery.html#pylab_examples):" 81 | ] 82 | }, 83 | { 84 | "cell_type": "code", 85 | "execution_count": null, 86 | "metadata": { 87 | "collapsed": false 88 | }, 89 | "outputs": [], 90 | "source": [ 91 | "from IPython.display import HTML\n", 92 | "HTML('')" 93 | ] 94 | }, 95 | { 96 | "cell_type": "markdown", 97 | "metadata": {}, 98 | "source": [ 99 | "Si hacemos clic en cualquiera de las imágenes, accedemos al código fuente que la ha generado (ejemplo: http://matplotlib.org/examples/pylab_examples/annotation_demo.html):" 100 | ] 101 | }, 102 | { 103 | "cell_type": "code", 104 | "execution_count": null, 105 | "metadata": { 106 | "collapsed": false 107 | }, 108 | "outputs": [], 109 | "source": [ 110 | "HTML('')" 111 | ] 112 | }, 113 | { 114 | "cell_type": "markdown", 115 | "metadata": {}, 116 | "source": [ 117 | "## Interfaz pyplot" 118 | ] 119 | }, 120 | { 121 | "cell_type": "markdown", 122 | "metadata": {}, 123 | "source": [ 124 | "La interfaz `pyplot` proporciona una serie de funciones que operan sobre un *estado global* - es decir, nosotros no especificamos sobre qué gráfica o ejes estamos actuando. Es una forma rápida y cómoda de crear gráficas pero perdemos parte del control." 125 | ] 126 | }, 127 | { 128 | "cell_type": "markdown", 129 | "metadata": {}, 130 | "source": [ 131 | "### Función `plot`" 132 | ] 133 | }, 134 | { 135 | "cell_type": "markdown", 136 | "metadata": {}, 137 | "source": [ 138 | "El paquete `pyplot` se suele importar bajo el alias `plt`, de modo que todas las funciones se acceden a través de `plt.`. La función más básica es la función `plot`:" 139 | ] 140 | }, 141 | { 142 | "cell_type": "code", 143 | "execution_count": null, 144 | "metadata": { 145 | "collapsed": false 146 | }, 147 | "outputs": [], 148 | "source": [ 149 | "# Nuestro primer plot\n" 150 | ] 151 | }, 152 | { 153 | "cell_type": "markdown", 154 | "metadata": {}, 155 | "source": [ 156 | "La función `plot` recibe una sola lista (si queremos especificar los valores *y*) o dos listas (si especificamos *x* e *y*). Naturalmente si especificamos dos listas ambas tienen que tener la misma longitud." 157 | ] 158 | }, 159 | { 160 | "cell_type": "markdown", 161 | "metadata": {}, 162 | "source": [ 163 | "La tarea más habitual a la hora de trabajar con matplotlib es representar una función. Lo que tendremos que hacer es definir un dominio y evaluarla en dicho dominio. Por ejemplo:" 164 | ] 165 | }, 166 | { 167 | "cell_type": "markdown", 168 | "metadata": {}, 169 | "source": [ 170 | "$$ f(x) = e^{-x^2} $$" 171 | ] 172 | }, 173 | { 174 | "cell_type": "code", 175 | "execution_count": null, 176 | "metadata": { 177 | "collapsed": false 178 | }, 179 | "outputs": [], 180 | "source": [] 181 | }, 182 | { 183 | "cell_type": "markdown", 184 | "metadata": {}, 185 | "source": [ 186 | "Definimos el dominio con la función `np.linspace`, que crea un vector de puntos equiespaciados:" 187 | ] 188 | }, 189 | { 190 | "cell_type": "code", 191 | "execution_count": null, 192 | "metadata": { 193 | "collapsed": false 194 | }, 195 | "outputs": [], 196 | "source": [ 197 | "x = " 198 | ] 199 | }, 200 | { 201 | "cell_type": "markdown", 202 | "metadata": {}, 203 | "source": [ 204 | "Y representamos la función:" 205 | ] 206 | }, 207 | { 208 | "cell_type": "code", 209 | "execution_count": null, 210 | "metadata": { 211 | "collapsed": false 212 | }, 213 | "outputs": [], 214 | "source": [ 215 | "# Pintamos la función\n", 216 | "\n", 217 | "\n", 218 | "# Ponemos nombre a los ejes\n", 219 | "\n", 220 | "\n", 221 | "# Ponemos una leyenda\n", 222 | "\n", 223 | "# Ponemos un título\n" 224 | ] 225 | }, 226 | { 227 | "cell_type": "markdown", 228 | "metadata": {}, 229 | "source": [ 230 | "Notamos varias cosas:\n", 231 | "\n", 232 | "* Con diversas llamadas a funciones dentro de `plt.` se actualiza el gráfico *actual*. Esa es la forma de trabajar con la interfaz pyplot.\n", 233 | "* Podemos añadir etiquetas, y escribir $\\LaTeX$ en ellas. Tan solo hay que encerrarlo entre signos de dólar $$.\n", 234 | "* Añadiendo como argumento `label` podemos definir una leyenda." 235 | ] 236 | }, 237 | { 238 | "cell_type": "markdown", 239 | "metadata": {}, 240 | "source": [ 241 | "### Personalización" 242 | ] 243 | }, 244 | { 245 | "cell_type": "markdown", 246 | "metadata": {}, 247 | "source": [ 248 | "La función `plot` acepta una serie de argumentos para personalizar el aspecto de la función. Con una letra podemos especificar el color, y con un símbolo el tipo de línea." 249 | ] 250 | }, 251 | { 252 | "cell_type": "code", 253 | "execution_count": null, 254 | "metadata": { 255 | "collapsed": false 256 | }, 257 | "outputs": [], 258 | "source": [ 259 | "# Método abreviado estilo MATLAB\n" 260 | ] 261 | }, 262 | { 263 | "cell_type": "markdown", 264 | "metadata": {}, 265 | "source": [ 266 | "Esto en realidad son códigos abreviados, que se corresponden con argumentos de la función `plot`:" 267 | ] 268 | }, 269 | { 270 | "cell_type": "code", 271 | "execution_count": null, 272 | "metadata": { 273 | "collapsed": false 274 | }, 275 | "outputs": [], 276 | "source": [ 277 | "# Argumentos\n" 278 | ] 279 | }, 280 | { 281 | "cell_type": "markdown", 282 | "metadata": {}, 283 | "source": [ 284 | "La lista de posibles argumentos y abreviaturas está disponible en la documentación de la función `plot` http://matplotlib.org/api/pyplot_api.html#matplotlib.pyplot.plot." 285 | ] 286 | }, 287 | { 288 | "cell_type": "markdown", 289 | "metadata": {}, 290 | "source": [ 291 | "### Más personalización, pero a lo loco" 292 | ] 293 | }, 294 | { 295 | "cell_type": "markdown", 296 | "metadata": {}, 297 | "source": [ 298 | "Desde matplotlib 1.4 se puede manipular fácilmente la apariencia de la gráfica usando **estilos**. Para ver qué estilos hay disponibles, escribiríamos `plt.style.available`." 299 | ] 300 | }, 301 | { 302 | "cell_type": "code", 303 | "execution_count": null, 304 | "metadata": { 305 | "collapsed": false 306 | }, 307 | "outputs": [], 308 | "source": [ 309 | "plt.style.available" 310 | ] 311 | }, 312 | { 313 | "cell_type": "markdown", 314 | "metadata": {}, 315 | "source": [ 316 | "No hay muchos pero podemos crear los nuestros. Para activar uno de ellos, usamos `plt.style.use`. ¡Aquí va el que uso yo! https://gist.github.com/Juanlu001/edb2bf7b583e7d56468a" 317 | ] 318 | }, 319 | { 320 | "cell_type": "code", 321 | "execution_count": null, 322 | "metadata": { 323 | "collapsed": false 324 | }, 325 | "outputs": [], 326 | "source": [ 327 | "#plt.style.use(\"ggplot\") # Afecta a todos los plots" 328 | ] 329 | }, 330 | { 331 | "cell_type": "markdown", 332 | "metadata": {}, 333 | "source": [ 334 | "
No he sido capaz de encontrar una manera fácil de volver a la apariencia por defecto en el notebook. A ver qué dicen los desarrolladores (https://github.com/ipython/ipython/issues/6707) ¡pero de momento si quieres volver a como estaba antes toca reiniciar el notebook!
" 335 | ] 336 | }, 337 | { 338 | "cell_type": "markdown", 339 | "metadata": {}, 340 | "source": [ 341 | "Para emplear un estilo solo a una porción del código, creamos un bloque `with plt.style.context(\"STYLE\")`:" 342 | ] 343 | }, 344 | { 345 | "cell_type": "code", 346 | "execution_count": null, 347 | "metadata": { 348 | "collapsed": false 349 | }, 350 | "outputs": [], 351 | "source": [ 352 | "with plt.style.context('ggplot'):\n", 353 | " plt.plot(x, f(x))\n", 354 | " plt.plot(x, 1 - f(x))" 355 | ] 356 | }, 357 | { 358 | "cell_type": "markdown", 359 | "metadata": {}, 360 | "source": [ 361 | "Y hay otro tipo de personalización más loca todavía:" 362 | ] 363 | }, 364 | { 365 | "cell_type": "code", 366 | "execution_count": null, 367 | "metadata": { 368 | "collapsed": false 369 | }, 370 | "outputs": [], 371 | "source": [ 372 | "with plt.xkcd():\n", 373 | " plt.plot(x, f(x))\n", 374 | " plt.plot(x, 1 - f(x))\n", 375 | " plt.xlabel(\"Eje x\")" 376 | ] 377 | }, 378 | { 379 | "cell_type": "markdown", 380 | "metadata": {}, 381 | "source": [ 382 | "¡Nunca imitar a XKCD fue tan fácil! http://xkcd.com/353/" 383 | ] 384 | }, 385 | { 386 | "cell_type": "code", 387 | "execution_count": null, 388 | "metadata": { 389 | "collapsed": true 390 | }, 391 | "outputs": [], 392 | "source": [ 393 | "import antigravity" 394 | ] 395 | }, 396 | { 397 | "cell_type": "markdown", 398 | "metadata": {}, 399 | "source": [ 400 | "### Otros tipo de gráficas" 401 | ] 402 | }, 403 | { 404 | "cell_type": "markdown", 405 | "metadata": {}, 406 | "source": [ 407 | "La función `scatter` muestra una nube de puntos, con posibilidad de variar también el tamaño y el color." 408 | ] 409 | }, 410 | { 411 | "cell_type": "code", 412 | "execution_count": null, 413 | "metadata": { 414 | "collapsed": false 415 | }, 416 | "outputs": [], 417 | "source": [ 418 | "N = 100\n", 419 | "x = \n", 420 | "y = \n", 421 | "\n", 422 | "# Pintamos\n" 423 | ] 424 | }, 425 | { 426 | "cell_type": "markdown", 427 | "metadata": {}, 428 | "source": [ 429 | "Con `s` y `c` podemos modificar el tamaño y el color respectivamente. Para el color, a cada valor numérico se le asigna un color a través de un *mapa de colores*; ese mapa se puede cambiar con el argumento `cmap`. Esa correspondencia se puede visualizar llamando a la función `colorbar`." 430 | ] 431 | }, 432 | { 433 | "cell_type": "code", 434 | "execution_count": null, 435 | "metadata": { 436 | "collapsed": false 437 | }, 438 | "outputs": [], 439 | "source": [ 440 | "# size & color\n", 441 | "\n", 442 | "\n", 443 | "# Pintamos\n", 444 | "\n", 445 | "# color bar\n" 446 | ] 447 | }, 448 | { 449 | "cell_type": "code", 450 | "execution_count": null, 451 | "metadata": { 452 | "collapsed": false 453 | }, 454 | "outputs": [], 455 | "source": [ 456 | "# En otro color\n" 457 | ] 458 | }, 459 | { 460 | "cell_type": "markdown", 461 | "metadata": {}, 462 | "source": [ 463 | "matplotlib trae por defecto muchos mapas de colores. En las SciPy Lecture Notes dan una lista de todos ellos (http://scipy-lectures.github.io/intro/matplotlib/matplotlib.html#colormaps)\n", 464 | "\n", 465 | "![matplotlib colormaps](../static/plot_colormaps.png)" 466 | ] 467 | }, 468 | { 469 | "cell_type": "markdown", 470 | "metadata": {}, 471 | "source": [ 472 | "La función `contour` se utiliza para visualizar las curvas de nivel de funciones de dos variables y está muy ligada a la función `np.meshgrid`. Veamos un ejemplo:\n", 473 | "\n", 474 | "$$f(x) = x^2 - y^2$$" 475 | ] 476 | }, 477 | { 478 | "cell_type": "code", 479 | "execution_count": null, 480 | "metadata": { 481 | "collapsed": false 482 | }, 483 | "outputs": [], 484 | "source": [] 485 | }, 486 | { 487 | "cell_type": "code", 488 | "execution_count": null, 489 | "metadata": { 490 | "collapsed": false 491 | }, 492 | "outputs": [], 493 | "source": [ 494 | "x = \n", 495 | "y = \n", 496 | "xx, yy = \n", 497 | "zz = \n", 498 | "\n" 499 | ] 500 | }, 501 | { 502 | "cell_type": "markdown", 503 | "metadata": {}, 504 | "source": [ 505 | "La función `contourf` es casi idéntica pero rellena el espacio entre niveles. Podemos especificar manualmente estos niveles usando el cuarto argumento:" 506 | ] 507 | }, 508 | { 509 | "cell_type": "code", 510 | "execution_count": null, 511 | "metadata": { 512 | "collapsed": false 513 | }, 514 | "outputs": [], 515 | "source": [] 516 | }, 517 | { 518 | "cell_type": "markdown", 519 | "metadata": {}, 520 | "source": [ 521 | "Para guardar las gráficas en archivos aparte podemos usar la función `plt.savefig`. matplotlib usará el tipo de archivo adecuado según la extensión que especifiquemos. Veremos esto con más detalle cuando hablemos de la interfaz orientada a objetos." 522 | ] 523 | }, 524 | { 525 | "cell_type": "markdown", 526 | "metadata": {}, 527 | "source": [ 528 | "### Varias figuras" 529 | ] 530 | }, 531 | { 532 | "cell_type": "markdown", 533 | "metadata": {}, 534 | "source": [ 535 | "Podemos crear figuras con varios sistemas de ejes, pasando a `subplot` el número de filas y de columnas." 536 | ] 537 | }, 538 | { 539 | "cell_type": "code", 540 | "execution_count": null, 541 | "metadata": { 542 | "collapsed": false 543 | }, 544 | "outputs": [], 545 | "source": [ 546 | "x = np.linspace(-1, 7, 1000)\n", 547 | "\n", 548 | "\n", 549 | "# ???\n", 550 | "plt.plot(x, np.sin(x))\n", 551 | "plt.title(\"Función seno\")\n", 552 | "\n", 553 | "# ???\n", 554 | "plt.plot(x, np.cos(x))\n", 555 | "plt.title(\"Función coseno\")" 556 | ] 557 | }, 558 | { 559 | "cell_type": "markdown", 560 | "metadata": {}, 561 | "source": [ 562 | "
¿Cómo se ajusta el espacio entre gráficas para que no se solapen los textos? Buscamos en Google \"plt.subplot adjust\" en el primer resultado tenemos la respuesta http://stackoverflow.com/a/9827848
" 563 | ] 564 | }, 565 | { 566 | "cell_type": "markdown", 567 | "metadata": {}, 568 | "source": [ 569 | "Como hemos guardado la figura en una variable, puedo recuperarla más adelate y seguir editándola." 570 | ] 571 | }, 572 | { 573 | "cell_type": "markdown", 574 | "metadata": {}, 575 | "source": [ 576 | "
Si queremos manipular la figura una vez hemos abandonado la celda donde la hemos definido, tendríamos que utilizar la interfaz orientada a objetos de matplotlib. Es un poco lioso porque algunas funciones cambian de nombre, así que en este curso no la vamos a ver. Si te interesa puedes ver los notebooks de la primera edición, donde sí la introdujimos.\n", 577 | "\n", 578 | "https://github.com/AeroPython/Curso_AeroPython/releases/tag/v1.0
" 579 | ] 580 | }, 581 | { 582 | "cell_type": "markdown", 583 | "metadata": {}, 584 | "source": [ 585 | "**Ejercicio**\n", 586 | "\n", 587 | "Crear una función que represente gráficamente esta expresión:\n", 588 | "\n", 589 | "$$\\sin(2 \\pi f_1 t) + \\sin(2 \\pi f_2 t)$$\n", 590 | "\n", 591 | "Siendo $f_1$ y $f_2$ argumentos de entrada (por defecto $10$ y $100$) y $t \\in [0, 0.5]$. Además, debe mostrar:\n", 592 | "\n", 593 | "* leyenda,\n", 594 | "* título \"Dos frecuencias\",\n", 595 | "* eje x \"Tiempo ($t$)\"\n", 596 | "\n", 597 | "y usar algún estilo de los disponibles." 598 | ] 599 | }, 600 | { 601 | "cell_type": "code", 602 | "execution_count": null, 603 | "metadata": { 604 | "collapsed": false 605 | }, 606 | "outputs": [], 607 | "source": [] 608 | }, 609 | { 610 | "cell_type": "markdown", 611 | "metadata": {}, 612 | "source": [ 613 | "**Ejercicio**\n", 614 | "\n", 615 | "Representar las curvas de nivel de esta función:\n", 616 | "\n", 617 | "$$g(x, y) = \\cos{x} + \\sin^2{y}$$\n", 618 | "\n", 619 | "Para obtener este resultado:\n", 620 | "\n", 621 | "![Ejercicio](../static/ejercicio_contour.png)" 622 | ] 623 | }, 624 | { 625 | "cell_type": "code", 626 | "execution_count": null, 627 | "metadata": { 628 | "collapsed": false 629 | }, 630 | "outputs": [], 631 | "source": [ 632 | "def g(x, y):\n", 633 | " return np.cos(x) + np.sin(y) ** 2\n", 634 | "\n", 635 | "# Necesitamos muchos puntos en la malla, para que cuando se\n", 636 | "# crucen las líneas no se vean irregularidades\n", 637 | "x = \n", 638 | "y = \n", 639 | "\n", 640 | "xx, yy = \n", 641 | "\n", 642 | "zz = \n", 643 | "\n", 644 | "# Podemos ajustar el tamaño de la figura con figsize\n", 645 | "fig = plt.figure(figsize=(6, 6))\n", 646 | "\n", 647 | "# Ajustamos para que tenga 13 niveles y que use el colormap Spectral\n", 648 | "# Tenemos que asignar la salida a la variable cs para luego crear el colorbar\n", 649 | "\n", 650 | "\n", 651 | "# Creamos la barra de colores\n", 652 | "\n", 653 | "\n", 654 | "# Con `colors='k'` dibujamos todas las líneas negras\n", 655 | "# Asignamos la salida a la variable cs2 para crear las etiquetas\n", 656 | "\n", 657 | "\n", 658 | "# Creamos las etiquetas sobre las líneas\n", 659 | "\n", 660 | "\n", 661 | "# Ponemos las etiquetas de los ejes\n" 662 | ] 663 | }, 664 | { 665 | "cell_type": "markdown", 666 | "metadata": {}, 667 | "source": [ 668 | "### El truco final: componentes interactivos" 669 | ] 670 | }, 671 | { 672 | "cell_type": "markdown", 673 | "metadata": {}, 674 | "source": [ 675 | "No tenemos mucho tiempo pero vamos a ver algo interesante que se ha introducido hace poco en el notebook: **componentes interactivos**." 676 | ] 677 | }, 678 | { 679 | "cell_type": "code", 680 | "execution_count": null, 681 | "metadata": { 682 | "collapsed": false 683 | }, 684 | "outputs": [], 685 | "source": [] 686 | }, 687 | { 688 | "cell_type": "markdown", 689 | "metadata": {}, 690 | "source": [ 691 | "## Referencias" 692 | ] 693 | }, 694 | { 695 | "cell_type": "markdown", 696 | "metadata": {}, 697 | "source": [ 698 | "* Guía de matplotlib para principiantes http://matplotlib.org/users/beginner.html\n", 699 | "* Tutorial de matplotlib en español http://pybonacci.org/tag/tutorial-matplotlib-pyplot/\n", 700 | "* Referencia rápida de matplotlib http://scipy-lectures.github.io/intro/matplotlib/matplotlib.html#quick-references" 701 | ] 702 | }, 703 | { 704 | "cell_type": "markdown", 705 | "metadata": {}, 706 | "source": [ 707 | "Si te ha gustado esta clase:\n", 708 | "\n", 709 | "Tweet\n", 710 | "\n", 711 | "\n", 712 | "---" 713 | ] 714 | }, 715 | { 716 | "cell_type": "markdown", 717 | "metadata": {}, 718 | "source": [ 719 | "####

¡Síguenos en Twitter!" 720 | ] 721 | }, 722 | { 723 | "cell_type": "markdown", 724 | "metadata": {}, 725 | "source": [ 726 | "##### \"Licencia
Curso AeroPython por Juan Luis Cano Rodriguez y Alejandro Sáez Mollejo se distribuye bajo una Licencia Creative Commons Atribución 4.0 Internacional." 727 | ] 728 | }, 729 | { 730 | "cell_type": "markdown", 731 | "metadata": {}, 732 | "source": [ 733 | "##### " 734 | ] 735 | }, 736 | { 737 | "cell_type": "markdown", 738 | "metadata": {}, 739 | "source": [ 740 | "---\n", 741 | "_Las siguientes celdas contienen configuración del Notebook_\n", 742 | "\n", 743 | "_Para visualizar y utlizar los enlaces a Twitter el notebook debe ejecutarse como [seguro](http://ipython.org/ipython-doc/dev/notebook/security.html)_\n", 744 | "\n", 745 | " File > Trusted Notebook" 746 | ] 747 | }, 748 | { 749 | "cell_type": "code", 750 | "execution_count": 34, 751 | "metadata": { 752 | "collapsed": false 753 | }, 754 | "outputs": [ 755 | { 756 | "data": { 757 | "text/html": [ 758 | "/* This template is inspired in the one used by Lorena Barba\n", 759 | "in the numerical-mooc repository: https://github.com/numerical-mooc/numerical-mooc\n", 760 | "We thank her work and hope you also enjoy the look of the notobooks with this style */\n", 761 | "\n", 762 | "\n", 763 | "\n", 764 | "El estilo se ha aplicado =)\n", 765 | "\n", 766 | "\n", 903 | "\n" 919 | ], 920 | "text/plain": [ 921 | "" 922 | ] 923 | }, 924 | "execution_count": 34, 925 | "metadata": {}, 926 | "output_type": "execute_result" 927 | } 928 | ], 929 | "source": [ 930 | "# Esta celda da el estilo al notebook\n", 931 | "from IPython.core.display import HTML\n", 932 | "css_file = '../static/styles/style.css'\n", 933 | "HTML(open(css_file, \"r\").read())" 934 | ] 935 | } 936 | ], 937 | "metadata": { 938 | "anaconda-cloud": {}, 939 | "kernelspec": { 940 | "display_name": "Python [default]", 941 | "language": "python", 942 | "name": "python3" 943 | }, 944 | "language_info": { 945 | "codemirror_mode": { 946 | "name": "ipython", 947 | "version": 3 948 | }, 949 | "file_extension": ".py", 950 | "mimetype": "text/x-python", 951 | "name": "python", 952 | "nbconvert_exporter": "python", 953 | "pygments_lexer": "ipython3", 954 | "version": "3.5.2" 955 | }, 956 | "widgets": { 957 | "state": { 958 | "d8f76be24aa9464ab37ea1b7fb8c7806": { 959 | "views": [ 960 | { 961 | "cell_index": 70 962 | } 963 | ] 964 | } 965 | }, 966 | "version": "1.2.0" 967 | } 968 | }, 969 | "nbformat": 4, 970 | "nbformat_minor": 0 971 | } 972 | -------------------------------------------------------------------------------- /notebooks/02-NumPy.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "\"AeroPython\"" 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | "# 02-NumPy" 15 | ] 16 | }, 17 | { 18 | "cell_type": "markdown", 19 | "metadata": {}, 20 | "source": [ 21 | "_Hasta ahora hemos visto los tipos de datos más básicos que nos ofrece Python: integer, real, complex, boolean, list, tuple... Pero ¿no echas algo de menos? Efectivamente, los __arrays__. _\n", 22 | "\n", 23 | "_Durante esta nos adentraremos en el paquete NumPy: veremos como los arrays mejoran la eficiencia de nuestro código, aprenderemos a crearlos y a operar con ellos_." 24 | ] 25 | }, 26 | { 27 | "cell_type": "markdown", 28 | "metadata": {}, 29 | "source": [ 30 | "## ¿Qué es un array? " 31 | ] 32 | }, 33 | { 34 | "cell_type": "markdown", 35 | "metadata": {}, 36 | "source": [ 37 | "Un array es un __bloque de memoria que contiene elementos del mismo tipo__. Básicamente:\n", 38 | "\n", 39 | "* nos _recuerdan_ a los vectores, matrices, tensores...\n", 40 | "* podemos almacenar el array con un nombre y acceder a sus __elementos__ mediante sus __índices__.\n", 41 | "* ayudan a gestionar de manera eficiente la memoria y a acelerar los cálculos.\n", 42 | "\n", 43 | "\n", 44 | "---\n", 45 | "\n", 46 | "| Índice | 0 | 1 | 2 | 3 | ... | n-1 | n |\n", 47 | "| ---------- | :---: | :---: | :---: | :---: | :---: | :---: | :---: |\n", 48 | "| Valor | 2.1 | 3.6 | 7.8 | 1.5 | ... | 5.4 | 6.3 |\n", 49 | "\n", 50 | "---\n", 51 | "\n", 52 | "__¿Qué solemos guardar en arrays?__\n", 53 | "\n", 54 | "* Vectores y matrices.\n", 55 | "* Datos de experimentos:\n", 56 | " - En distintos instantes discretos.\n", 57 | " - En distintos puntos del espacio.\n", 58 | "* Resultado de evaluar funciones con los datos anteriores.\n", 59 | "* Discretizaciones para usar algoritmos de: integración, derivación, interpolación...\n", 60 | "* ... " 61 | ] 62 | }, 63 | { 64 | "cell_type": "markdown", 65 | "metadata": {}, 66 | "source": [ 67 | "## ¿Qué es NumPy?" 68 | ] 69 | }, 70 | { 71 | "cell_type": "markdown", 72 | "metadata": {}, 73 | "source": [ 74 | "NumPy es un paquete fundamental para la programación científica que __proporciona un objeto tipo array__ para almacenar datos de forma eficiente y una serie de __funciones__ para operar y manipular esos datos.\n", 75 | "Para usar NumPy lo primero que debemos hacer es importarlo:" 76 | ] 77 | }, 78 | { 79 | "cell_type": "code", 80 | "execution_count": null, 81 | "metadata": { 82 | "collapsed": false 83 | }, 84 | "outputs": [], 85 | "source": [] 86 | }, 87 | { 88 | "cell_type": "markdown", 89 | "metadata": {}, 90 | "source": [ 91 | "## Nuestro primer array" 92 | ] 93 | }, 94 | { 95 | "cell_type": "markdown", 96 | "metadata": {}, 97 | "source": [ 98 | "¿No decíamos que Python era fácil? Pues __creemos nuestros primeros arrays__:" 99 | ] 100 | }, 101 | { 102 | "cell_type": "code", 103 | "execution_count": null, 104 | "metadata": { 105 | "collapsed": false 106 | }, 107 | "outputs": [], 108 | "source": [ 109 | "# Array de una dimensión" 110 | ] 111 | }, 112 | { 113 | "cell_type": "code", 114 | "execution_count": null, 115 | "metadata": { 116 | "collapsed": false 117 | }, 118 | "outputs": [], 119 | "source": [ 120 | "# Podemos usar print" 121 | ] 122 | }, 123 | { 124 | "cell_type": "code", 125 | "execution_count": null, 126 | "metadata": { 127 | "collapsed": false 128 | }, 129 | "outputs": [], 130 | "source": [ 131 | "# Comprobar el tipo de mi_primer_array" 132 | ] 133 | }, 134 | { 135 | "cell_type": "code", 136 | "execution_count": null, 137 | "metadata": { 138 | "collapsed": false 139 | }, 140 | "outputs": [], 141 | "source": [ 142 | "# Comprobar el tipo de datos que contiene" 143 | ] 144 | }, 145 | { 146 | "cell_type": "markdown", 147 | "metadata": {}, 148 | "source": [ 149 | "Los arrays de una dimensión se crean pasándole una lista como argumento a la función `np.array`. Para crear un array de dos dimensiones le pasaremos una _lista de listas_:" 150 | ] 151 | }, 152 | { 153 | "cell_type": "code", 154 | "execution_count": null, 155 | "metadata": { 156 | "collapsed": false 157 | }, 158 | "outputs": [], 159 | "source": [ 160 | "# Array de dos dimensiones" 161 | ] 162 | }, 163 | { 164 | "cell_type": "markdown", 165 | "metadata": {}, 166 | "source": [ 167 | "Esto sería una buena manera de definirlo, de acuerdo con el [PEP 8 (indentation)](http://legacy.python.org/dev/peps/pep-0008/#indentation)." 168 | ] 169 | }, 170 | { 171 | "cell_type": "markdown", 172 | "metadata": {}, 173 | "source": [ 174 | "### Funciones y constantes de NumPy" 175 | ] 176 | }, 177 | { 178 | "cell_type": "markdown", 179 | "metadata": {}, 180 | "source": [ 181 | "Hemos dicho que NumPy también incorporá __funciones__. Un ejemplo sencillo:" 182 | ] 183 | }, 184 | { 185 | "cell_type": "code", 186 | "execution_count": null, 187 | "metadata": { 188 | "collapsed": false 189 | }, 190 | "outputs": [], 191 | "source": [ 192 | "# Suma" 193 | ] 194 | }, 195 | { 196 | "cell_type": "code", 197 | "execution_count": null, 198 | "metadata": { 199 | "collapsed": false 200 | }, 201 | "outputs": [], 202 | "source": [ 203 | "# Máximo" 204 | ] 205 | }, 206 | { 207 | "cell_type": "code", 208 | "execution_count": null, 209 | "metadata": { 210 | "collapsed": false 211 | }, 212 | "outputs": [], 213 | "source": [ 214 | "# Seno" 215 | ] 216 | }, 217 | { 218 | "cell_type": "markdown", 219 | "metadata": {}, 220 | "source": [ 221 | "Y algunas __constantes__ que podemos neccesitar:" 222 | ] 223 | }, 224 | { 225 | "cell_type": "code", 226 | "execution_count": null, 227 | "metadata": { 228 | "collapsed": false 229 | }, 230 | "outputs": [], 231 | "source": [] 232 | }, 233 | { 234 | "cell_type": "markdown", 235 | "metadata": {}, 236 | "source": [ 237 | "## Funciones para crear arrays" 238 | ] 239 | }, 240 | { 241 | "cell_type": "markdown", 242 | "metadata": {}, 243 | "source": [ 244 | "### Rangos" 245 | ] 246 | }, 247 | { 248 | "cell_type": "markdown", 249 | "metadata": {}, 250 | "source": [ 251 | "#### np.arange" 252 | ] 253 | }, 254 | { 255 | "cell_type": "markdown", 256 | "metadata": {}, 257 | "source": [ 258 | "NumPy, dame __un array que vaya de 0 a 5__:" 259 | ] 260 | }, 261 | { 262 | "cell_type": "code", 263 | "execution_count": null, 264 | "metadata": { 265 | "collapsed": false 266 | }, 267 | "outputs": [], 268 | "source": [] 269 | }, 270 | { 271 | "cell_type": "markdown", 272 | "metadata": {}, 273 | "source": [ 274 | "__Mira con atención el resultado anterior__, ¿hay algo que deberías grabar en tu cabeza para simpre?\n", 275 | "__El último elemento no es 5 sino 4__" 276 | ] 277 | }, 278 | { 279 | "cell_type": "markdown", 280 | "metadata": {}, 281 | "source": [ 282 | "NumPy, dame __un array que vaya de 0 a 10, de 3 en 3__:" 283 | ] 284 | }, 285 | { 286 | "cell_type": "code", 287 | "execution_count": null, 288 | "metadata": { 289 | "collapsed": false 290 | }, 291 | "outputs": [], 292 | "source": [] 293 | }, 294 | { 295 | "cell_type": "markdown", 296 | "metadata": {}, 297 | "source": [ 298 | "#### np.linspace" 299 | ] 300 | }, 301 | { 302 | "cell_type": "markdown", 303 | "metadata": {}, 304 | "source": [ 305 | "Si has tenido que usar MATLAB alguna vez, seguro que esto te suena:" 306 | ] 307 | }, 308 | { 309 | "cell_type": "code", 310 | "execution_count": null, 311 | "metadata": { 312 | "collapsed": false 313 | }, 314 | "outputs": [], 315 | "source": [] 316 | }, 317 | { 318 | "cell_type": "markdown", 319 | "metadata": {}, 320 | "source": [ 321 | "En este caso sí que se incluye el último elemento." 322 | ] 323 | }, 324 | { 325 | "cell_type": "markdown", 326 | "metadata": {}, 327 | "source": [ 328 | "
Nota: \n", 329 | "También puedes probar `np.logspace()`\n", 330 | "
" 331 | ] 332 | }, 333 | { 334 | "cell_type": "markdown", 335 | "metadata": {}, 336 | "source": [ 337 | "### reshape" 338 | ] 339 | }, 340 | { 341 | "cell_type": "markdown", 342 | "metadata": {}, 343 | "source": [ 344 | "Con `np.arange()` es posible crear \"vectores\" cuyos elementos tomen valores consecutivos o equiespaciados, como hemos visto anteriormente. ¿Podemos hacer lo mismo con \"matrices\"? Pues sí, pero no usando una sola función. Imagina que quieres crear algo como esto:\n", 345 | "\n", 346 | "\\begin{pmatrix}\n", 347 | " 1 & 2 & 3\\\\ \n", 348 | " 4 & 5 & 6\\\\\n", 349 | " 7 & 8 & 9\\\\\n", 350 | " \\end{pmatrix}\n", 351 | " \n", 352 | "* Comenzaremos por crear un array 1d con los valores $(1,2,3,4,5,6,7,8,9)$ usando `np.arange()`.\n", 353 | "* Luego le daremos forma de array 2d. con `np.reshape(array, (dim0, dim1))`." 354 | ] 355 | }, 356 | { 357 | "cell_type": "code", 358 | "execution_count": null, 359 | "metadata": { 360 | "collapsed": false 361 | }, 362 | "outputs": [], 363 | "source": [ 364 | "a = np.arange(1,10)\n", 365 | "M = np.reshape(a, [3,3])\n", 366 | "M" 367 | ] 368 | }, 369 | { 370 | "cell_type": "code", 371 | "execution_count": null, 372 | "metadata": { 373 | "collapsed": false 374 | }, 375 | "outputs": [], 376 | "source": [ 377 | "# También funciona como método\n", 378 | "N = a.reshape([3,3])\n", 379 | "N" 380 | ] 381 | }, 382 | { 383 | "cell_type": "markdown", 384 | "metadata": {}, 385 | "source": [ 386 | "
Nota: \n", 387 | "No vamos a entrar demasiado en qué son los métodos, pero debes saber que están asociados a la programación orientada a objetos y que en Python todo es un objeto. Lo que debes pensar es que son unas funciones especiales en las que el argumento más importante (sobre el que se realiza la acción) se escribe delante seguido de un punto. Por ejemplo: `.método(argumentos)`\n", 388 | "
" 389 | ] 390 | }, 391 | { 392 | "cell_type": "markdown", 393 | "metadata": {}, 394 | "source": [ 395 | "## Operaciones" 396 | ] 397 | }, 398 | { 399 | "cell_type": "markdown", 400 | "metadata": {}, 401 | "source": [ 402 | "### Operaciones elemento a elemento" 403 | ] 404 | }, 405 | { 406 | "cell_type": "markdown", 407 | "metadata": {}, 408 | "source": [ 409 | "Ahora que pocas cosas se nos escapan de los arrays, probemos a hacer algunas operaciones. El funcionamiento es el habitual en FORTRAN y MATLAB y poco hay que añadir:" 410 | ] 411 | }, 412 | { 413 | "cell_type": "code", 414 | "execution_count": null, 415 | "metadata": { 416 | "collapsed": false 417 | }, 418 | "outputs": [], 419 | "source": [ 420 | "#crear un arra y y sumarle un número\n", 421 | "arr = np.arange(11)\n", 422 | "arr + 55" 423 | ] 424 | }, 425 | { 426 | "cell_type": "code", 427 | "execution_count": null, 428 | "metadata": { 429 | "collapsed": false 430 | }, 431 | "outputs": [], 432 | "source": [ 433 | "#multiplicarlo por un número\n", 434 | "arr * 2" 435 | ] 436 | }, 437 | { 438 | "cell_type": "code", 439 | "execution_count": null, 440 | "metadata": { 441 | "collapsed": false 442 | }, 443 | "outputs": [], 444 | "source": [ 445 | "#elevarlo al cuadrado\n", 446 | "arr ** 2" 447 | ] 448 | }, 449 | { 450 | "cell_type": "code", 451 | "execution_count": null, 452 | "metadata": { 453 | "collapsed": false 454 | }, 455 | "outputs": [], 456 | "source": [ 457 | "#calcular una función\n", 458 | "np.tanh(arr)" 459 | ] 460 | }, 461 | { 462 | "cell_type": "markdown", 463 | "metadata": {}, 464 | "source": [ 465 | "
Entrenamiento: \n", 466 | "Puedes tratar de comparar la diferencia de tiempo entre realizar la operación en bloque, como ahora, y realizarla elemento a elemento, recorriendo el array con un bucle.\n", 467 | "
" 468 | ] 469 | }, 470 | { 471 | "cell_type": "markdown", 472 | "metadata": {}, 473 | "source": [ 474 | "__Si las operaciones involucran dos arrays también se realizan elemento a elemento__" 475 | ] 476 | }, 477 | { 478 | "cell_type": "code", 479 | "execution_count": null, 480 | "metadata": { 481 | "collapsed": false 482 | }, 483 | "outputs": [], 484 | "source": [ 485 | "#creamos dos arrays\n", 486 | "arr1 = np.arange(0,11)\n", 487 | "arr2 = np.arange(20,31)" 488 | ] 489 | }, 490 | { 491 | "cell_type": "code", 492 | "execution_count": null, 493 | "metadata": { 494 | "collapsed": false 495 | }, 496 | "outputs": [], 497 | "source": [ 498 | "#los sumamos\n", 499 | "arr1 + arr2" 500 | ] 501 | }, 502 | { 503 | "cell_type": "code", 504 | "execution_count": null, 505 | "metadata": { 506 | "collapsed": false 507 | }, 508 | "outputs": [], 509 | "source": [ 510 | "#multiplicamos\n", 511 | "arr1 * arr2" 512 | ] 513 | }, 514 | { 515 | "cell_type": "markdown", 516 | "metadata": {}, 517 | "source": [ 518 | "#### Comparaciones" 519 | ] 520 | }, 521 | { 522 | "cell_type": "code", 523 | "execution_count": null, 524 | "metadata": { 525 | "collapsed": false 526 | }, 527 | "outputs": [], 528 | "source": [ 529 | "# >,<\n", 530 | "arr1 > arr2" 531 | ] 532 | }, 533 | { 534 | "cell_type": "code", 535 | "execution_count": null, 536 | "metadata": { 537 | "collapsed": false 538 | }, 539 | "outputs": [], 540 | "source": [ 541 | "# ==\n", 542 | "arr1 == arr2 # ¡ojo! los arrays son de integers, no de floats" 543 | ] 544 | }, 545 | { 546 | "cell_type": "markdown", 547 | "metadata": {}, 548 | "source": [ 549 | "
Nota: \n", 550 | "Por cierto, ¿qúe ocurrirá si los arrays con los que se quiere operar no tiene la misma forma? ¿apuestas? Quizá más adelante te interese buscar algo de información sobre __broadcasting__.\n", 551 | "
" 552 | ] 553 | }, 554 | { 555 | "cell_type": "markdown", 556 | "metadata": {}, 557 | "source": [ 558 | "## Álgebra lineal" 559 | ] 560 | }, 561 | { 562 | "cell_type": "markdown", 563 | "metadata": {}, 564 | "source": [ 565 | "_Una vez hemos visto el manejo básico de arrays en Python con NumPy, es hora de pasar a operaciones más interesantes como son las propias del Álgebra Lineal._\n", 566 | "\n", 567 | "_Los productos escalares y las inversiones de matrices están por todas partes en los programas científicos e ingenieriles, así que vamos a estudiar cómo se realizan en Python._" 568 | ] 569 | }, 570 | { 571 | "cell_type": "markdown", 572 | "metadata": {}, 573 | "source": [ 574 | "Como sabemos, las operaciones del álgebra lineal aparecen con mucha frecuencia a la hora de resolver sistemas de ecuaciones en derivadas parciales y en general al linealizar problemas de todo tipo, y suele ser necesario resolver sistemas con un número enorme de ecuaciones e incógnitas. Gracias a los arrays de NumPy podemos abordar este tipo de cálculos en Python, ya que todas las funciones están escritas en C o Fortran y tenemos la opción de usar bibliotecas optimizadas al límite." 575 | ] 576 | }, 577 | { 578 | "cell_type": "markdown", 579 | "metadata": {}, 580 | "source": [ 581 | "El paquete de álgebra lineal en NumPy se llama `linalg`, así que importando NumPy con la convención habitual podemos acceder a él escribiendo `np.linalg`. Si imprimimos la ayuda del paquete vemos que tenemos funciones para:\n", 582 | "\n", 583 | "* Funciones básicas (norma de un vector, inversa de una matriz, determinante, traza)\n", 584 | "* Resolución de sistemas\n", 585 | "* Autovalores y autovectores\n", 586 | "* Descomposiciones matriciales (QR, SVD)\n", 587 | "* Pseudoinversas" 588 | ] 589 | }, 590 | { 591 | "cell_type": "code", 592 | "execution_count": null, 593 | "metadata": { 594 | "collapsed": false 595 | }, 596 | "outputs": [], 597 | "source": [ 598 | "import numpy as np" 599 | ] 600 | }, 601 | { 602 | "cell_type": "code", 603 | "execution_count": null, 604 | "metadata": { 605 | "collapsed": false 606 | }, 607 | "outputs": [], 608 | "source": [ 609 | "help(np.linalg)" 610 | ] 611 | }, 612 | { 613 | "cell_type": "markdown", 614 | "metadata": {}, 615 | "source": [ 616 | "Recordemos que si queremos usar una función de un paquete pero no queremos escribir la \"ruta\" completa cada vez, podemos usar la sintaxis `from package import func`:" 617 | ] 618 | }, 619 | { 620 | "cell_type": "code", 621 | "execution_count": null, 622 | "metadata": { 623 | "collapsed": false 624 | }, 625 | "outputs": [], 626 | "source": [] 627 | }, 628 | { 629 | "cell_type": "markdown", 630 | "metadata": {}, 631 | "source": [ 632 | "El producto matricial usual (no el que se hace elemento a elemento, sino el del álgebra lineal) se calcula con la misma función que el producto matriz-vector y el producto escalar vector-vector: con la función `dot`, que **no** está en el paquete `linalg` sino directamente en `numpy` y no hace falta importarlo." 633 | ] 634 | }, 635 | { 636 | "cell_type": "code", 637 | "execution_count": null, 638 | "metadata": { 639 | "collapsed": false 640 | }, 641 | "outputs": [], 642 | "source": [ 643 | "np.dot" 644 | ] 645 | }, 646 | { 647 | "cell_type": "markdown", 648 | "metadata": {}, 649 | "source": [ 650 | "Una consideración importante a tener en cuenta es que en NumPy no hace falta ser estricto a la hora de manejar vectores como si fueran matrices columna, siempre que la operación sea consistente. Un vector es una matriz con una sola dimensión: por eso si calculamos su traspuesta no funciona." 651 | ] 652 | }, 653 | { 654 | "cell_type": "code", 655 | "execution_count": null, 656 | "metadata": { 657 | "collapsed": false 658 | }, 659 | "outputs": [], 660 | "source": [ 661 | "M = np.array([\n", 662 | " [1, 2],\n", 663 | " [3, 4]\n", 664 | "])\n", 665 | "v = np.array([1, -1])" 666 | ] 667 | }, 668 | { 669 | "cell_type": "code", 670 | "execution_count": null, 671 | "metadata": { 672 | "collapsed": false 673 | }, 674 | "outputs": [], 675 | "source": [ 676 | "# ¡La traspuesta del vector no incrementa las dimensiones!" 677 | ] 678 | }, 679 | { 680 | "cell_type": "code", 681 | "execution_count": null, 682 | "metadata": { 683 | "collapsed": false 684 | }, 685 | "outputs": [], 686 | "source": [] 687 | }, 688 | { 689 | "cell_type": "markdown", 690 | "metadata": {}, 691 | "source": [ 692 | "El mismo resultado se obtiene usando el operador `@`, nuevo en Python 3.5:" 693 | ] 694 | }, 695 | { 696 | "cell_type": "code", 697 | "execution_count": null, 698 | "metadata": { 699 | "collapsed": false 700 | }, 701 | "outputs": [], 702 | "source": [] 703 | }, 704 | { 705 | "cell_type": "markdown", 706 | "metadata": {}, 707 | "source": [ 708 | "Para hacer comparaciones entre arrays de punto flotante se pueden usar las funciones `np.allclose` y `np.isclose`. La primera comprueba si todos los elementos de los arrays son iguales dentro de una tolerancia, y la segunda compara elemento a elemento y devuelve un array de valores `True` y `False`." 709 | ] 710 | }, 711 | { 712 | "cell_type": "code", 713 | "execution_count": null, 714 | "metadata": { 715 | "collapsed": false 716 | }, 717 | "outputs": [], 718 | "source": [ 719 | "u, v" 720 | ] 721 | }, 722 | { 723 | "cell_type": "code", 724 | "execution_count": null, 725 | "metadata": { 726 | "collapsed": false 727 | }, 728 | "outputs": [], 729 | "source": [ 730 | "np.allclose(u, v)" 731 | ] 732 | }, 733 | { 734 | "cell_type": "code", 735 | "execution_count": null, 736 | "metadata": { 737 | "collapsed": false 738 | }, 739 | "outputs": [], 740 | "source": [ 741 | "np.isclose(0.0, 1e-8, atol=1e-10)" 742 | ] 743 | }, 744 | { 745 | "cell_type": "markdown", 746 | "metadata": {}, 747 | "source": [ 748 | "### Ejercicios" 749 | ] 750 | }, 751 | { 752 | "cell_type": "markdown", 753 | "metadata": {}, 754 | "source": [ 755 | "1- Hallar el producto de estas dos matrices y su determinante:\n", 756 | "\n", 757 | "$$\\begin{pmatrix} 1 & 0 & 0 \\\\ 2 & 1 & 1 \\\\ -1 & 0 & 1 \\end{pmatrix} \\begin{pmatrix} 2 & 3 & -1 \\\\ 0 & -2 & 1 \\\\ 0 & 0 & 3 \\end{pmatrix}$$" 758 | ] 759 | }, 760 | { 761 | "cell_type": "code", 762 | "execution_count": null, 763 | "metadata": { 764 | "collapsed": false 765 | }, 766 | "outputs": [], 767 | "source": [ 768 | "from numpy.linalg import det" 769 | ] 770 | }, 771 | { 772 | "cell_type": "code", 773 | "execution_count": null, 774 | "metadata": { 775 | "collapsed": false 776 | }, 777 | "outputs": [], 778 | "source": [ 779 | "A = np.array([\n", 780 | " ...\n", 781 | "])\n", 782 | "B = np.array([\n", 783 | " ...\n", 784 | "])\n", 785 | "print(A)\n", 786 | "print(B)" 787 | ] 788 | }, 789 | { 790 | "cell_type": "code", 791 | "execution_count": null, 792 | "metadata": { 793 | "collapsed": false 794 | }, 795 | "outputs": [], 796 | "source": [] 797 | }, 798 | { 799 | "cell_type": "code", 800 | "execution_count": null, 801 | "metadata": { 802 | "collapsed": false 803 | }, 804 | "outputs": [], 805 | "source": [] 806 | }, 807 | { 808 | "cell_type": "markdown", 809 | "metadata": {}, 810 | "source": [ 811 | "2- Resolver el siguiente sistema:\n", 812 | "\n", 813 | "$$ \\begin{pmatrix} 2 & 0 & 0 \\\\ -1 & 1 & 0 \\\\ 3 & 2 & -1 \\end{pmatrix} \\begin{pmatrix} 1 & 1 & 1 \\\\ 0 & 1 & 2 \\\\ 0 & 0 & 1 \\end{pmatrix} \\begin{pmatrix} x \\\\ y \\\\ z \\end{pmatrix} = \\begin{pmatrix} -1 \\\\ 3 \\\\ 0 \\end{pmatrix} $$" 814 | ] 815 | }, 816 | { 817 | "cell_type": "code", 818 | "execution_count": null, 819 | "metadata": { 820 | "collapsed": false 821 | }, 822 | "outputs": [], 823 | "source": [ 824 | "M = ..." 825 | ] 826 | }, 827 | { 828 | "cell_type": "code", 829 | "execution_count": null, 830 | "metadata": { 831 | "collapsed": false 832 | }, 833 | "outputs": [], 834 | "source": [ 835 | "x = ..." 836 | ] 837 | }, 838 | { 839 | "cell_type": "code", 840 | "execution_count": null, 841 | "metadata": { 842 | "collapsed": false 843 | }, 844 | "outputs": [], 845 | "source": [ 846 | "np.allclose(np.dot(M, x), np.array([-1, 3, 0]))" 847 | ] 848 | }, 849 | { 850 | "cell_type": "markdown", 851 | "metadata": {}, 852 | "source": [ 853 | "3- Hallar la inversa de la matriz $H$ y comprobar que $H H^{-1} = I$ (recuerda la función `np.eye`)" 854 | ] 855 | }, 856 | { 857 | "cell_type": "code", 858 | "execution_count": null, 859 | "metadata": { 860 | "collapsed": false 861 | }, 862 | "outputs": [], 863 | "source": [ 864 | "H = np.array(\n", 865 | "[[ 2, 4, 7, 12, 21, 38],\n", 866 | " [ 71, 128, 265, 512, 1035, 2048],\n", 867 | " [ 4109, 8206, 16399, 32784, 65553, 131090],\n", 868 | " [ 262145, 524308, 1048577, 2097174, 4194305, 8388632],\n", 869 | " [ 16777271, 33554488, 67108921, 134217786, 268435515, 536870972],\n", 870 | " [ 1073741855, 2147483680, 4294967329, 8589934626, 17179869219, 34359738404]]\n", 871 | ")\n", 872 | "print(H)" 873 | ] 874 | }, 875 | { 876 | "cell_type": "code", 877 | "execution_count": null, 878 | "metadata": { 879 | "collapsed": false 880 | }, 881 | "outputs": [], 882 | "source": [] 883 | }, 884 | { 885 | "cell_type": "code", 886 | "execution_count": null, 887 | "metadata": { 888 | "collapsed": false 889 | }, 890 | "outputs": [], 891 | "source": [] 892 | }, 893 | { 894 | "cell_type": "code", 895 | "execution_count": null, 896 | "metadata": { 897 | "collapsed": false 898 | }, 899 | "outputs": [], 900 | "source": [ 901 | "np.isclose(np.dot(Hinv, H), np.eye(6))" 902 | ] 903 | }, 904 | { 905 | "cell_type": "code", 906 | "execution_count": null, 907 | "metadata": { 908 | "collapsed": false 909 | }, 910 | "outputs": [], 911 | "source": [ 912 | "np.set_printoptions(precision=3)\n", 913 | "print(np.dot(Hinv, H))" 914 | ] 915 | }, 916 | { 917 | "cell_type": "markdown", 918 | "metadata": {}, 919 | "source": [ 920 | "
¡No funciona! Y no solo eso sino que los resultados varían de un ordenador a otro.
" 921 | ] 922 | }, 923 | { 924 | "cell_type": "markdown", 925 | "metadata": {}, 926 | "source": [ 927 | "4- Comprobar el número de condición de la matriz $H$." 928 | ] 929 | }, 930 | { 931 | "cell_type": "code", 932 | "execution_count": null, 933 | "metadata": { 934 | "collapsed": false 935 | }, 936 | "outputs": [], 937 | "source": [] 938 | }, 939 | { 940 | "cell_type": "markdown", 941 | "metadata": {}, 942 | "source": [ 943 | "
La matriz está mal condicionada.
" 944 | ] 945 | }, 946 | { 947 | "cell_type": "markdown", 948 | "metadata": {}, 949 | "source": [ 950 | "## Secciones de arrays" 951 | ] 952 | }, 953 | { 954 | "cell_type": "markdown", 955 | "metadata": {}, 956 | "source": [ 957 | "Hasta ahora hemos visto cómo acceder a elementos aislados del array, pero la potencia de NumPy está en poder acceder a secciones enteras. Para ello se usa la sintaxis `inicio:final:paso`: si alguno de estos valores no se pone toma un valor por defecto. Veamos ejemplos:" 958 | ] 959 | }, 960 | { 961 | "cell_type": "code", 962 | "execution_count": null, 963 | "metadata": { 964 | "collapsed": false 965 | }, 966 | "outputs": [], 967 | "source": [ 968 | "M = np.arange(36, dtype=float).reshape(4, 9)\n", 969 | "M" 970 | ] 971 | }, 972 | { 973 | "cell_type": "code", 974 | "execution_count": null, 975 | "metadata": { 976 | "collapsed": false 977 | }, 978 | "outputs": [], 979 | "source": [ 980 | "# De la segunda a la tercera fila, incluida" 981 | ] 982 | }, 983 | { 984 | "cell_type": "code", 985 | "execution_count": null, 986 | "metadata": { 987 | "collapsed": false 988 | }, 989 | "outputs": [], 990 | "source": [ 991 | "# Hasta la tercera fila sin incluir y de la segunda a la quinta columnas saltando dos" 992 | ] 993 | }, 994 | { 995 | "cell_type": "markdown", 996 | "metadata": {}, 997 | "source": [ 998 | "### Ejercicio" 999 | ] 1000 | }, 1001 | { 1002 | "cell_type": "markdown", 1003 | "metadata": {}, 1004 | "source": [ 1005 | "Pintar un tablero de ajedrez usando la función `plt.matshow`." 1006 | ] 1007 | }, 1008 | { 1009 | "cell_type": "code", 1010 | "execution_count": null, 1011 | "metadata": { 1012 | "collapsed": false 1013 | }, 1014 | "outputs": [], 1015 | "source": [ 1016 | "tablero = ..." 1017 | ] 1018 | }, 1019 | { 1020 | "cell_type": "code", 1021 | "execution_count": null, 1022 | "metadata": { 1023 | "collapsed": false 1024 | }, 1025 | "outputs": [], 1026 | "source": [ 1027 | "%matplotlib inline\n", 1028 | "import matplotlib.pyplot as plt\n", 1029 | "\n", 1030 | "plt.matshow(tablero, cmap=plt.cm.gray_r)" 1031 | ] 1032 | }, 1033 | { 1034 | "cell_type": "markdown", 1035 | "metadata": {}, 1036 | "source": [ 1037 | "## Entrada/Salida" 1038 | ] 1039 | }, 1040 | { 1041 | "cell_type": "markdown", 1042 | "metadata": {}, 1043 | "source": [ 1044 | "Con E/S (I/O en inglés) entendemos leer y escribir datos archivos. Es algo que necesitaremos hacer con relativa frecuencia, y en NumPy es muy sencillo de hacer. Para el caso de la **lectura** se usa la función `np.loadtxt`." 1045 | ] 1046 | }, 1047 | { 1048 | "cell_type": "markdown", 1049 | "metadata": {}, 1050 | "source": [ 1051 | "Para practicar, vamos a leer el archivo `temperaturas.csv` que contiene datos diarios de temperaturas en Nueva York entre el 1 de enero de 2013 y el 1 de enero de 2014, obtenidos gratuitamente de http://ncdc.noaa.gov/. Como los hemos descargado en formato CSV habrá que tener algunas precauciones a la hora de leer el archivo." 1052 | ] 1053 | }, 1054 | { 1055 | "cell_type": "code", 1056 | "execution_count": null, 1057 | "metadata": { 1058 | "collapsed": false 1059 | }, 1060 | "outputs": [], 1061 | "source": [ 1062 | "#!head ../static/temperaturas.csv # Esta línea no funciona en Windows\n", 1063 | "!type ../static/temperaturas.csv" 1064 | ] 1065 | }, 1066 | { 1067 | "cell_type": "code", 1068 | "execution_count": null, 1069 | "metadata": { 1070 | "collapsed": false 1071 | }, 1072 | "outputs": [], 1073 | "source": [ 1074 | "# Saltamos una línea\n", 1075 | "# Solo columnas 2, 3 y 4\n", 1076 | "# Separados por comas" 1077 | ] 1078 | }, 1079 | { 1080 | "cell_type": "markdown", 1081 | "metadata": {}, 1082 | "source": [ 1083 | "La primera columna es un entero con formato \"AAAAMMDD\" que vamos a ignorar. Las temperaturas están medidas en décimas de grado Celsius, así que hay que pasarlas a grados Celsius. Vamos a calcular también la temperatura media." 1084 | ] 1085 | }, 1086 | { 1087 | "cell_type": "code", 1088 | "execution_count": null, 1089 | "metadata": { 1090 | "collapsed": false 1091 | }, 1092 | "outputs": [], 1093 | "source": [] 1094 | }, 1095 | { 1096 | "cell_type": "markdown", 1097 | "metadata": {}, 1098 | "source": [ 1099 | "Como vamos a ignorar la columna de las fechas tenemos que crear un dominio para el eje x. Simplemente construiremos un array de enteros desde 0 hasta 365." 1100 | ] 1101 | }, 1102 | { 1103 | "cell_type": "code", 1104 | "execution_count": null, 1105 | "metadata": { 1106 | "collapsed": false 1107 | }, 1108 | "outputs": [], 1109 | "source": [] 1110 | }, 1111 | { 1112 | "cell_type": "markdown", 1113 | "metadata": {}, 1114 | "source": [ 1115 | "Supongamos que ahora queremos guardar nuestra tabla de datos en un archivo txt, para poder cargarlo ya modificado más adelante. Una manera fácil de hacerlo sería con otra función de NumPy: `np.savetxt`. Lo usaremos con los argumentos opcionales `fmt='%.5f', newline = '\\r\\n'` para obtener un fichero *bonito* que podamos entender de un vistazo." 1116 | ] 1117 | }, 1118 | { 1119 | "cell_type": "code", 1120 | "execution_count": null, 1121 | "metadata": { 1122 | "collapsed": false 1123 | }, 1124 | "outputs": [], 1125 | "source": [ 1126 | "matriz_datos = np.zeros([366, 4])\n", 1127 | "matriz_datos[:, 0] = x\n", 1128 | "matriz_datos[:, 1] = Tmax\n", 1129 | "matriz_datos[:, 2] = Tmin\n", 1130 | "matriz_datos[:, 3] = Tavg\n", 1131 | "\n", 1132 | "print(matriz_datos[:10])\n", 1133 | "\n", 1134 | "np.savetxt('archivo_datos.txt', matriz_datos, fmt='%.5f', newline = '\\r\\n')" 1135 | ] 1136 | }, 1137 | { 1138 | "cell_type": "markdown", 1139 | "metadata": {}, 1140 | "source": [ 1141 | "### Ejercicio" 1142 | ] 1143 | }, 1144 | { 1145 | "cell_type": "markdown", 1146 | "metadata": {}, 1147 | "source": [ 1148 | "Y ahora representamos la evolución de la temperatura media (por ejemplo de color negro), indicando \"Daily summaries\" en el título, \"Days\" en el eje x y \"Temperature (C)\" en el eje y. Podemos crear una zona rellena entre la máxima y la mínima con la función `fill_between(x, max, min)` (por ejemplo de color #4f88b1). Si los límites del eje x no quedan como queremos podemos usar la función `set_xlim(xmin, xmax)`." 1149 | ] 1150 | }, 1151 | { 1152 | "cell_type": "code", 1153 | "execution_count": null, 1154 | "metadata": { 1155 | "collapsed": false 1156 | }, 1157 | "outputs": [], 1158 | "source": [ 1159 | "plt.plot(x, Tavg, 'k')\n", 1160 | "plt.xlim(0, 366)\n", 1161 | "plt.fill_between(x, Tmin, Tmax, facecolor='#4f88b1', edgecolor='none')\n", 1162 | "plt.title(\"Resúmenes diarios\")\n", 1163 | "plt.xlabel(\"Días\")\n", 1164 | "plt.ylabel(\"Temperatura (C)\")" 1165 | ] 1166 | }, 1167 | { 1168 | "cell_type": "markdown", 1169 | "metadata": {}, 1170 | "source": [ 1171 | "---" 1172 | ] 1173 | }, 1174 | { 1175 | "cell_type": "markdown", 1176 | "metadata": {}, 1177 | "source": [ 1178 | "_Ya hemos aprendido a efectuar algunas operaciones útiles con NumPy e incluso hemos hecho nuestro primer ejercicio de lectura de datos. Estamos en condiciones de empezar a escribir programas más interesantes, pero aún queda lo mejor._\n", 1179 | "\n", 1180 | "* [Álgebra lineal en Python con NumPy en Pybonacci](http://pybonacci.org/2012/06/07/algebra-lineal-en-python-con-numpy-i-operaciones-basicas/)" 1181 | ] 1182 | }, 1183 | { 1184 | "cell_type": "markdown", 1185 | "metadata": {}, 1186 | "source": [ 1187 | "Si te ha gustado esta clase:\n", 1188 | "\n", 1189 | "Tweet\n", 1190 | "\n", 1191 | "\n", 1192 | "---" 1193 | ] 1194 | }, 1195 | { 1196 | "cell_type": "markdown", 1197 | "metadata": {}, 1198 | "source": [ 1199 | "####

¡Síguenos en Twitter!" 1200 | ] 1201 | }, 1202 | { 1203 | "cell_type": "markdown", 1204 | "metadata": {}, 1205 | "source": [ 1206 | "##### \"Licencia
Curso AeroPython por Juan Luis Cano Rodriguez y Alejandro Sáez Mollejo se distribuye bajo una Licencia Creative Commons Atribución 4.0 Internacional." 1207 | ] 1208 | }, 1209 | { 1210 | "cell_type": "markdown", 1211 | "metadata": {}, 1212 | "source": [ 1213 | "##### " 1214 | ] 1215 | }, 1216 | { 1217 | "cell_type": "markdown", 1218 | "metadata": {}, 1219 | "source": [ 1220 | "---\n", 1221 | "_Las siguientes celdas contienen configuración del Notebook_\n", 1222 | "\n", 1223 | "_Para visualizar y utlizar los enlaces a Twitter el notebook debe ejecutarse como [seguro](http://ipython.org/ipython-doc/dev/notebook/security.html)_\n", 1224 | "\n", 1225 | " File > Trusted Notebook" 1226 | ] 1227 | }, 1228 | { 1229 | "cell_type": "code", 1230 | "execution_count": null, 1231 | "metadata": { 1232 | "collapsed": false 1233 | }, 1234 | "outputs": [], 1235 | "source": [ 1236 | "%%html\n", 1237 | "Follow @Pybonacci\n", 1238 | "" 1239 | ] 1240 | }, 1241 | { 1242 | "cell_type": "code", 1243 | "execution_count": null, 1244 | "metadata": { 1245 | "collapsed": false 1246 | }, 1247 | "outputs": [], 1248 | "source": [ 1249 | "# Esta celda da el estilo al notebook\n", 1250 | "from IPython.core.display import HTML\n", 1251 | "css_file = '../static/styles/style.css'\n", 1252 | "HTML(open(css_file, \"r\").read())" 1253 | ] 1254 | } 1255 | ], 1256 | "metadata": { 1257 | "anaconda-cloud": {}, 1258 | "kernelspec": { 1259 | "display_name": "Python [default]", 1260 | "language": "python", 1261 | "name": "python3" 1262 | }, 1263 | "language_info": { 1264 | "codemirror_mode": { 1265 | "name": "ipython", 1266 | "version": 3 1267 | }, 1268 | "file_extension": ".py", 1269 | "mimetype": "text/x-python", 1270 | "name": "python", 1271 | "nbconvert_exporter": "python", 1272 | "pygments_lexer": "ipython3", 1273 | "version": "3.5.2" 1274 | } 1275 | }, 1276 | "nbformat": 4, 1277 | "nbformat_minor": 0 1278 | } 1279 | -------------------------------------------------------------------------------- /notebooks/PX - Sistema Cerrado.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Problema X Termodinámica\n", 8 | "\n", 9 | "### Definición de los sistemas y entorno \n", 10 | "Se tiene un sistema cerrado formado por un globo de goma elástica lleno de hidrógeno. \n", 11 | "El sistema de hidrógeno encerrado por el globo (sistema $h$ en lo sucesivo) está envuelto por aire en condiciones ambiente. El aire de este entorno está encerrado por un cilindro ($c$) con émbolo ($E$), conformando otro sistema cerrado. El sistema de aire (sistema $a$ en lo sucesivo) y el sistema $h$ de su interior, se encuentran rodeados por una atmósfera de aire de condiciones conocidas." 12 | ] 13 | }, 14 | { 15 | "cell_type": "markdown", 16 | "metadata": {}, 17 | "source": [ 18 | "### Datos Geométricos y Estructurales conocidos\n", 19 | "- **Fronteras del Sistema $a$ **\n", 20 | " - Cilindro ($c$):\n", 21 | " - $L_0=30cm$ (altura total del cilindro)\n", 22 | " - $\\phi_c=20cm$ (diámetro total del cilindro)\n", 23 | " - Cilindro perfecto, indeformable y sin rozamiento con el émbolo\n", 24 | " - El cilindro es perfectamente diatérmano\n", 25 | " - Émbolo ($e$):\n", 26 | " - Espesor despreciable ($\\delta_e\\approx0$)\n", 27 | " - $\\phi_e=20cm=\\phi_c$ (diámetro total del émbolo)\n", 28 | " - Émbolo perfecto, sin rozamiento e indeformable, que encaja perfectamente en el cilindro\n", 29 | " - El émbolo es perfectamente diatérmano\n", 30 | " - El émbolo tiene una masa $m_e$ desconocida\n", 31 | "- ** Fronteras del Sistema $h$**\n", 32 | " - El material elástico del que está hecho el globo tiene una masa de $m_g=2g$\n", 33 | " - La masa de globo $m_g$ ocupa un volumen despreciable\n", 34 | " - El globo de goma tiene un diámetro mínimo $\\phi_{h_0}$ hasta el cual no se aplica sobrepresión alguna en su interior. A partir de ahí se sigue una relación lineal (ley elástica) tal que por cada centímetro de variación de diámetro se requieren 15 mmHg de sobrepresión: $$\\Delta P=k·(\\phi_{h_1}-\\phi_{h_0}) $$ Donde\n", 35 | " - $\\phi_{h_0}=5 cm \\equiv$ Diámetro mínimo\n", 36 | " - $\\phi_{h_1}=15cm \\equiv$ Diámetro inicial\n", 37 | " - $k=15 \\frac {mmHg}{cm} \\equiv$ Constante Elástica\n", 38 | " \"Drawing\"\n", 39 | " - El globo se considera perfectamente diatérmano\n", 40 | " - En todo momento consideraremos que el globo es esférico e indeformable, de diámetro variable\n" 41 | ] 42 | }, 43 | { 44 | "cell_type": "markdown", 45 | "metadata": {}, 46 | "source": [ 47 | "### Datos Termodinámicos y Propiedades Termodinámicas Iniciales\n", 48 | "- **Entorno:**\n", 49 | " - Atmósfera de aire, ideal\n", 50 | " - Temperatura inicial: $T_\\infty=15ºC$\n", 51 | " - Presión inicial: $P_\\infty=1bar$\n", 52 | "- **Sistema $a$:**\n", 53 | " - Aire en condiciones atmósfericas inicialmente\n", 54 | " - Émbolo a distancia $L_1 = L_0$ de la base del cilindro inicialmente\n", 55 | "- **Sistema $h$:**\n", 56 | " - Globo con diámetro $\\phi_{h_1}$ inicialmente\n", 57 | "- **Propiedades termodinámicas y constantes**: \n", 58 | " - Aire como gas ideal:\n", 59 | " - Se asume aire compuesto al $78\\% N_2$ y $22\\% O_2$\n", 60 | " - Masas moleculares disponibles: $M_N=14 \\frac{g}{mol}$ y $M_O=16 \\frac{g}{mol}$\n", 61 | " - Coeficiente de dilatación adiabático: $\\gamma_a=1.4$\n", 62 | " - Hidrógeno como gas ideal:\n", 63 | " - Masa molecular del hidrógeno: $M_H=1 \\frac{g}{mol}$\n", 64 | " - Coeficiente de dilatación adiabático: $\\gamma_h=1.411$\n", 65 | " - Constante universal de los gases ideales: $\\overline{R}=8.3114 \\frac{J}{mol·K}$" 66 | ] 67 | }, 68 | { 69 | "cell_type": "markdown", 70 | "metadata": {}, 71 | "source": [ 72 | "### Proceso Termodinámico y Apartados\n", 73 | "|Estado Inicial: | Estado Final:|\n", 74 | "|-----------------------------------------------------------|--------------|\n", 75 | "|- Sistema $a$ en condiciones atmosféricas y émbolo a $L_0$| Se suelta el émbolo y se deja llegar al equilibrio termodinámico a los sistema $a$ y $h$|\n", 76 | "|- Globo a $\\phi_{h1}$ | El proceso es rápido. Se deja suficiente tiempo hasta el atemperamiento de todos los sistemas|\n", 77 | "Se pide obtener:\n", 78 | "1. Propiedades termodinámicas que resuelven el estado inicial de ambos sistemas\n", 79 | "2. Condición de flotabilidad del globo en función de la masa del émbolo $m_e$" 80 | ] 81 | }, 82 | { 83 | "cell_type": "markdown", 84 | "metadata": {}, 85 | "source": [ 86 | "### **Primer Paso:** Conversión de los datos a Sistema Internacional y obtención de las constantes básicas\n", 87 | "En primer lugar, se calculan las constantes de los gases a partir de la constante universal y las masas moleculares. Todas las unidades se pasan a SI. Todas. Sin excepción!" 88 | ] 89 | }, 90 | { 91 | "cell_type": "markdown", 92 | "metadata": {}, 93 | "source": [ 94 | "##### Obtención de las Constantes de los Gases" 95 | ] 96 | }, 97 | { 98 | "cell_type": "code", 99 | "execution_count": 1, 100 | "metadata": { 101 | "collapsed": false 102 | }, 103 | "outputs": [], 104 | "source": [ 105 | "## Constantes de los Gases:\n", 106 | "# Masas moleculares\n", 107 | "M_O = 16 #g/mol - Masa molecular del oxígeno\n", 108 | "M_N = 14 #g/mol - Masa molecular del nitrógeno\n", 109 | "M_H = 1 #g/mol - Masa molecular del hidrógeno\n", 110 | "\n", 111 | "M_a = 0.78*2*M_N + 0.22*2*M_O #masa molecular del aire en g/mol\n", 112 | "M_h = 2*M_H #masa molecular del sistema hidrógeno (diatómico) en g/mol\n", 113 | "\n", 114 | "# Constante de los Gases\n", 115 | "R_u = 8.3114 #J/mol/K - Constante universal de los gases\n", 116 | "R_a = R_u/M_a * 1000 #J/kg/K\n", 117 | "R_h = R_u/M_h * 1000 #J/kg/K" 118 | ] 119 | }, 120 | { 121 | "cell_type": "markdown", 122 | "metadata": {}, 123 | "source": [ 124 | "##### Ley Elástica y Condiciones Iniciales del Globo\n" 125 | ] 126 | }, 127 | { 128 | "cell_type": "code", 129 | "execution_count": 2, 130 | "metadata": { 131 | "collapsed": true 132 | }, 133 | "outputs": [], 134 | "source": [ 135 | "import numpy as np" 136 | ] 137 | }, 138 | { 139 | "cell_type": "code", 140 | "execution_count": 3, 141 | "metadata": { 142 | "collapsed": true 143 | }, 144 | "outputs": [], 145 | "source": [ 146 | "#Ley elástica del globo\n", 147 | "mmHg2Pa = 101325/760 #conversión de mmHg a Pa\n", 148 | "K = 15*mmHg2Pa*100 #constante elástica en Pa/m\n", 149 | "phi_h0 = 0.05 #diámetro mínimo en m" 150 | ] 151 | }, 152 | { 153 | "cell_type": "code", 154 | "execution_count": 4, 155 | "metadata": { 156 | "collapsed": true 157 | }, 158 | "outputs": [], 159 | "source": [ 160 | "def delta_p(phi_h):\n", 161 | " if phi_h<=phi_h0:\n", 162 | " return 0\n", 163 | " else:\n", 164 | " return K*(phi_h - phi_h0)" 165 | ] 166 | }, 167 | { 168 | "cell_type": "code", 169 | "execution_count": 5, 170 | "metadata": { 171 | "collapsed": false 172 | }, 173 | "outputs": [], 174 | "source": [ 175 | "#Obtención del volumen inicial del globo:\n", 176 | "phi_h1 = 0.15 #diámetro inicial del globo en metros\n", 177 | "V_h1 = np.pi/6*(phi_h1**3) # Volumen inicial del sistema de hidrógeno (globo) en m3\n", 178 | "\n", 179 | "#Masa del globo\n", 180 | "m_g = 0.002 #kg\n", 181 | " \n", 182 | "#Condiciones Ambiente:\n", 183 | "T_inf = 15 + 273.15 #Temperatura ambiente en K\n", 184 | "P_inf = 1e5 #Presión ambiente en Pa\n", 185 | "\n", 186 | "#Aceleración de la gravedad:\n", 187 | "grav = 9.8 #m/s2" 188 | ] 189 | }, 190 | { 191 | "cell_type": "markdown", 192 | "metadata": {}, 193 | "source": [ 194 | "##### Obtención de las ligaduras Geométricas" 195 | ] 196 | }, 197 | { 198 | "cell_type": "code", 199 | "execution_count": 6, 200 | "metadata": { 201 | "collapsed": true 202 | }, 203 | "outputs": [], 204 | "source": [ 205 | "#Condiciones geométricas del problema:\n", 206 | "L1 = 0.3 # distancia inicial entre émbolo y base del cilindro en metros\n", 207 | "phi_c = 0.2 #diámetro del cilindro\n", 208 | "\n", 209 | "#Obtención del Volumen inicial del aire\n", 210 | "V_a1 = L1*np.pi/4*phi_c**2 - V_h1#Volumen incial del sistema aire en m3" 211 | ] 212 | }, 213 | { 214 | "cell_type": "markdown", 215 | "metadata": {}, 216 | "source": [ 217 | "### Segundo Paso: Resolución del Estado Termodinámico inicial a ambos sistemas" 218 | ] 219 | }, 220 | { 221 | "cell_type": "markdown", 222 | "metadata": {}, 223 | "source": [ 224 | "##### Sistema $a$\n", 225 | "El cilindro es perfectamente diatérmano y el proceso se puede considerar cuasiestático, con lo que la temperatura se mantendrá siempre constante e igual al valor de la temperatura ambiente (ejerce de baño térmico): el proceso del sistema $a$ es **Isotermo**.\n", 226 | "Además, el estado inicial se adquiere llenando el cilindro de aire atmósférico y tapándolo con el émbolo sin dejaro caer. La presión será, por tanto, la del ambiente, y el volumen el máximo posible (émbolo a distancia $L_0$ de la base del cilindro)." 227 | ] 228 | }, 229 | { 230 | "cell_type": "code", 231 | "execution_count": 7, 232 | "metadata": { 233 | "collapsed": false 234 | }, 235 | "outputs": [ 236 | { 237 | "name": "stdout", 238 | "output_type": "stream", 239 | "text": [ 240 | "Masa del Sistema Aire: 0.00923kg\n" 241 | ] 242 | } 243 | ], 244 | "source": [ 245 | "import eq_state \n", 246 | "## Sistema Aire en Estado Inicial (1):\n", 247 | "# Se considera estado de equilibrio. Como las fronteras son diatérmanas:\n", 248 | "T_a1 = T_inf\n", 249 | "\n", 250 | "# Como está en condiciones atmosféricas:\n", 251 | "P_a1 = P_inf\n", 252 | "\n", 253 | "#Como el volumen es conocido por ligaduras geométricas, la ecuación de estado nos da la masa:\n", 254 | "m_a = eq_state.solve_m(R=R_a, P=P_inf, T=T_inf, V=V_a1)\n", 255 | "rho_a1 = m_a/V_a1 #densidad del aire en 1\n", 256 | "print('Masa del Sistema Aire: {0:1.5f}kg'.format(m_a))" 257 | ] 258 | }, 259 | { 260 | "cell_type": "markdown", 261 | "metadata": {}, 262 | "source": [ 263 | "##### Sistema $h$\n", 264 | "El sistema del hidrógeno se resuelve de manera análoga al sistema de aire: la temperatura $T_{h_1}$ es directa al estar $a$ y $h$ atemperados. La presión del sistema se puede calcular a través de la ley elástica del globo, y ésta a su vez se obtiene con el diámetro (inicialmente $\\phi_{h_1}$ es dato, con lo que se tiene también el volumen del globo)." 265 | ] 266 | }, 267 | { 268 | "cell_type": "code", 269 | "execution_count": 8, 270 | "metadata": { 271 | "collapsed": false 272 | }, 273 | "outputs": [ 274 | { 275 | "name": "stdout", 276 | "output_type": "stream", 277 | "text": [ 278 | "Masa de 1.77086e-04kg y presión de 119998Pa en el Sistema Hidrógeno\n" 279 | ] 280 | } 281 | ], 282 | "source": [ 283 | "## Sistema Hidrógeno en Estado Inicial (1):\n", 284 | "#Como es diatérmano:\n", 285 | "T_h1 = T_inf\n", 286 | "\n", 287 | "#La presión se puede obtener mediante balance de fuerzas y la sobrepresión debida al globo:\n", 288 | "P_h1 = P_a1 + delta_p(phi_h1)\n", 289 | "\n", 290 | "#Con la presión,la temperatura y el volumen (debido al diámetro):\n", 291 | "m_h = eq_state.solve_m(R=R_h, P=P_h1, T=T_h1, V=V_h1)\n", 292 | "print('Masa de {0:1.5e}kg y presión de {1:6.0f}Pa en el Sistema Hidrógeno'.format(m_h, P_h1))" 293 | ] 294 | }, 295 | { 296 | "cell_type": "markdown", 297 | "metadata": {}, 298 | "source": [ 299 | "### Tercer Paso: Condición de Flotabilidad\n", 300 | "La condición de flotabilidad depende de igualar el peso del peso del globo (incluyendo la masa de la goma elástica) al peso que tendría la masa de aire que ocupase su lugar: $$E=P$$ Donde: $$E=\\rho_{a_1}·V_{h_1}·g$$ $$ P=(m_h + m_g)·g$$ La densidad del aire se obtiene de la ecuación de estado: $$P=\\rho ·R_g ·T$$Por tanto:" 301 | ] 302 | }, 303 | { 304 | "cell_type": "code", 305 | "execution_count": 9, 306 | "metadata": { 307 | "collapsed": false 308 | }, 309 | "outputs": [ 310 | { 311 | "name": "stdout", 312 | "output_type": "stream", 313 | "text": [ 314 | "Empuje neto sobre el globo: -0.00045N\n" 315 | ] 316 | } 317 | ], 318 | "source": [ 319 | "##Condición de Flotabilidad:\n", 320 | "# Peso total del Sistema Hidrógeno y Globo:\n", 321 | "P = (m_h + m_g)*grav\n", 322 | "\n", 323 | "#Empuje del aire sobre h+g:\n", 324 | "E = rho_a1*V_h1*grav\n", 325 | "\n", 326 | "print('Empuje neto sobre el globo: {0:4.5f}N'.format(E-P))" 327 | ] 328 | }, 329 | { 330 | "cell_type": "markdown", 331 | "metadata": {}, 332 | "source": [ 333 | "Para obtener la condición de flotabilidad hay que igualar el peso al empuje $E=P$. Como el peso del sistema hidrógeno + globo es **constante**, la densidad del aire será el elemento que regule la flotabilidad del globo. La densidad del aire a su vez depende de la **presión del aire** a través de la ecuación de estado, y la presión del sistema aire se obtiene por **balance de fuerzas en el émbolo**. Por lo tanto:" 334 | ] 335 | }, 336 | { 337 | "cell_type": "markdown", 338 | "metadata": {}, 339 | "source": [ 340 | "#### Balance de Fuerzas al Émbolo (Ec. 1)\n", 341 | "$$P_{a_2} = P_\\infty + m_e·\\frac{g}{A_c}$$" 342 | ] 343 | }, 344 | { 345 | "cell_type": "markdown", 346 | "metadata": {}, 347 | "source": [ 348 | "#### Balance de Fuerzas al Globo (Ec. 2)\n", 349 | "$$P_{h_2} = P_{a_2} + K·(\\phi_{h_2} - \\phi_{h_0})$$" 350 | ] 351 | }, 352 | { 353 | "cell_type": "markdown", 354 | "metadata": {}, 355 | "source": [ 356 | "#### Ecuación de Estado al Hidrógeno (Ec. 3)\n", 357 | "$$P_{h_2}·V_{h_2}=m_h·R_h·T_{h_2}$$" 358 | ] 359 | }, 360 | { 361 | "cell_type": "markdown", 362 | "metadata": {}, 363 | "source": [ 364 | "#### Flotabilidad (Ec. 4)\n", 365 | "$$\\frac{P_{a_2}}{R_a·T_{a_2}}·\\frac{\\pi}{6}·\\phi³_{h_2}·g = (m_h + m_g)·g$$" 366 | ] 367 | }, 368 | { 369 | "cell_type": "markdown", 370 | "metadata": {}, 371 | "source": [ 372 | "Para resolver la masa del émbolo que hace que el globo flote ($E=P$), sustituiremos las ecuaciones **1 y 2** en **3** y asumiremos una masa inicial del émbolo. El resultado del volumen del sistema de hidrógeno debe cumplir la ecuación **4**.\n", 373 | "De esta forma, si la diferencia entre la presión de la **Ec. 1** y la de la **Ec. 4** es pequeña $(\\approx 10^{-6})$." 374 | ] 375 | }, 376 | { 377 | "cell_type": "markdown", 378 | "metadata": { 379 | "collapsed": false 380 | }, 381 | "source": [ 382 | "#### Iterando para resolver $m_e$" 383 | ] 384 | }, 385 | { 386 | "cell_type": "code", 387 | "execution_count": 10, 388 | "metadata": { 389 | "collapsed": false 390 | }, 391 | "outputs": [ 392 | { 393 | "name": "stdout", 394 | "output_type": "stream", 395 | "text": [ 396 | "Masa del émbolo que resuelve la condición de flotabilidad: 33.00kg\n", 397 | "La condición de presiones es P_h2=129547Pa; P_a2=110294Pa\n" 398 | ] 399 | } 400 | ], 401 | "source": [ 402 | "residual = 1\n", 403 | "m_e = 5\n", 404 | "while residual>1e-6:\n", 405 | " m_e += 0.2\n", 406 | " P_a2 = P_inf + m_e*grav/(np.pi/4*phi_c**2)\n", 407 | " phi_h2 = ((m_h + m_g)/(P_a2/R_a/T_inf)*6/np.pi)**(1/3)\n", 408 | " \n", 409 | " P_h2 = eq_state.solve_P(R=R_h, V=np.pi/6*phi_h2**3, m=m_h, T=T_inf)\n", 410 | " residual = (P_a2 + K*(phi_h2 - phi_h0) - P_h2)/P_h2\n", 411 | " \n", 412 | "print('Masa del émbolo que resuelve la condición de flotabilidad: {0:4.2f}kg'.format(m_e))\n", 413 | "print('La condición de presiones es P_h2={0:6.0f}Pa; P_a2={1:6.0f}Pa'.format(P_h2, P_a2))" 414 | ] 415 | }, 416 | { 417 | "cell_type": "markdown", 418 | "metadata": {}, 419 | "source": [ 420 | "#### Resolviendo el Sistema de manera interactiva:\n", 421 | "La siguiente función resuelve el sistema de cuatro ecuaciones anterior para cada valor de masa $m_e$ que seleccionemos con el deslizador. " 422 | ] 423 | }, 424 | { 425 | "cell_type": "code", 426 | "execution_count": 11, 427 | "metadata": { 428 | "collapsed": true 429 | }, 430 | "outputs": [], 431 | "source": [ 432 | "%matplotlib inline\n", 433 | "from ipywidgets import interact\n", 434 | "import matplotlib.pyplot as plt\n", 435 | "from matplotlib import patches" 436 | ] 437 | }, 438 | { 439 | "cell_type": "code", 440 | "execution_count": 12, 441 | "metadata": { 442 | "collapsed": true 443 | }, 444 | "outputs": [], 445 | "source": [ 446 | "m_a = P_a1*V_a1/R_a/T_a1" 447 | ] 448 | }, 449 | { 450 | "cell_type": "code", 451 | "execution_count": 13, 452 | "metadata": { 453 | "collapsed": false 454 | }, 455 | "outputs": [], 456 | "source": [ 457 | "def embolo(m_e3):\n", 458 | " \n", 459 | " P_a3 = P_inf + m_e3*grav/(np.pi/4*phi_c**2)\n", 460 | " V_a3 = eq_state.solve_V(R=R_a, m=m_a, P=P_a3, T=T_inf)\n", 461 | " \n", 462 | " V_h3 = (m_h + m_g)/(P_a2/R_a/T_inf)\n", 463 | " \n", 464 | " L = (V_a3 + V_h3)/(np.pi/4*phi_c**2)\n", 465 | " \n", 466 | " P_h3 = P_a3 + K*((V_h3/np.pi*6)**(1/3)-phi_h0)\n", 467 | " \n", 468 | " width = phi_c/2\n", 469 | " piston_height = 0.3\n", 470 | " emb_heigth = 0.01\n", 471 | " R = phi_c/2\n", 472 | " \n", 473 | " fig, ax = plt.subplots(1, 3)\n", 474 | "\n", 475 | " embolo_down = patches.Rectangle((0, 0), width, L, alpha=1-L)\n", 476 | " embolo_up = patches.Rectangle((0, L), width, piston_height-L, alpha=0.1)\n", 477 | " piston = patches.Rectangle((0, L), width, emb_heigth, color='black')\n", 478 | "\n", 479 | " fig.set_figwidth(10)\n", 480 | " ax[0].add_patch(embolo_down)\n", 481 | " ax[0].add_patch(embolo_up)\n", 482 | " ax[0].add_patch(piston)\n", 483 | " \n", 484 | " ax[0].set_xlim(0, width)\n", 485 | " ax[0].set_ylim(0, piston_height)\n", 486 | " ax[0].axes.get_xaxis().set_visible(False)\n", 487 | " ax[0].axes.get_yaxis().set_visible(False)\n", 488 | " \n", 489 | " ax[0].text(width/5, L/2,\n", 490 | " '$P_h={0:6.0f} Pa$ \\n$P_a={1:6.0f} Pa$'.format(P_h3, P_a3),\n", 491 | " size=12)\n", 492 | " \n", 493 | " m_e3_ = np.linspace(0.01, 300, 100)\n", 494 | " \n", 495 | " P_a3_ = P_inf + m_e3_*grav/(np.pi/4*phi_c**2)\n", 496 | " V_a3_ = eq_state.solve_V(R=R_a, m=m_a, P=P_a3_, T=T_inf) \n", 497 | " \n", 498 | " ax[1].plot(m_e3_, V_a3_)\n", 499 | " ax[1].plot(m_e3, V_a3, 'ro')\n", 500 | " ax[1].set_ylabel('$V_{a3}\\ (m^{3})$', size=12)\n", 501 | " ax[1].set_xlabel('$m_{e3}\\ (kg)$', size=12)\n", 502 | " \n", 503 | " \n", 504 | " ax[2].plot(m_e3_, P_a3_)\n", 505 | " ax[2].plot(m_e3, P_a3, 'ro')\n", 506 | " ax[2].set_ylabel('$P_{a3}\\ (Pa)$', size=12)\n", 507 | " ax[2].set_xlabel('$m_{e3}\\ (kg)$', size=12)\n", 508 | " \n", 509 | " plt.tight_layout()" 510 | ] 511 | }, 512 | { 513 | "cell_type": "code", 514 | "execution_count": 14, 515 | "metadata": { 516 | "collapsed": false 517 | }, 518 | "outputs": [ 519 | { 520 | "data": { 521 | "image/png": "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\nbge8Q1jKbH0JfgQiInHqRmie9mbS9s7AKCjytbAnYRbRC0A14DWge9IxzwTuJ3Qzz4lify+Y3T3H\nzE4gXIvfI6wX/jjQLyFmlZkdQ7h2TyfcstTf3Udu0bsXkbSmwltE0oq7Dyc09clrX+c8tr1NGMEu\n6JhjgDEF7F8D9IoeBR2nP9C/oBgRkfLK3Yu0jG1h18Kox8Zl0SO/mF+Asws5z0LghEJi/gf8q6AY\nEZGi0DreIiIiIiIiIqVIhbeIiJSKzMzMuFMokPLbOspPpHxK9f83lN/WUX6py9y98CCzFsCMCRM+\nUHM1kXLm448/5IQTDgZo6e4z485HSkbudXnGjBnloUmJiCSYOXMmLVu2BF2XKxxdm0XKr9K+NmvE\nW0RERERERKQUqfAWERERERERKUUqvEVERERERERKkQpvERERERERkVKkwltERERERESkFKnwFhER\nERERESlFKrxFRERERERESpEKbxEREREREZFSpMJbREREREREpBSp8BYREREREREpRSq8RURERERE\nJK1t2FC6x1fhLSIiIiIiImlrxQo444zSPYcKbxEREREREUlLGzfC6afD8uWlex4V3iIiIiIiIpKW\neveGKVNg0KDSPU/l0j28iIiIiIiISOp5/HEYMgTuuw8OOqh0z6URbxGRcmz27LgzEBERESl/3nsP\nLroIunaF7t1L/3wqvEVEyrGRI+POQERERKR8WbAATj45jHIPGwZmpX9OFd4iIuXYG2/A//4XdxYi\nIiIi5cOaNXDSSbDNNjBmDFStWjbnVeEtIlKONWwIt94adxYiIiIiqc8dOneGefNg/Hj4y1/K7twq\nvEVEyrHzzoNnn4W5c+PORERERCS13XorPP88jBoF++5btudW4S0iUo61bw877gi33RZ3JiIiIiKp\na+xY6NsX+veHU08t+/Or8BaRtGJm3c3sGzP7zczeN7MDC4k/0sxmmFm2mc0zs3PziDnNzGZHx5xl\nZm2T9n9jZjl5PO5LiHksj/2vFvZ+qlWDa66Bp56Cr74qzichIiIikh4+/RTOOScU3H37xpODCm8R\nSRtmdjpwF9AP2B+YBUwys3r5xO8KTABeB/YFhgKPmNnRCTGHAk8DDwP7AeOAl8xs74RDHQA0THgc\nDTjwXNIpJwINEuIyi/K+LrgA6tfXvd4iIiIiyZYtC83U9toLnngCKsVUAavwFpF00hMY4e6j3H0O\n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\nQmCC\n", 522 | "text/plain": [ 523 | "" 524 | ] 525 | }, 526 | "metadata": {}, 527 | "output_type": "display_data" 528 | } 529 | ], 530 | "source": [ 531 | "i = interact(embolo, m_e3=(0.01, 300))" 532 | ] 533 | } 534 | ], 535 | "metadata": { 536 | "anaconda-cloud": {}, 537 | "kernelspec": { 538 | "display_name": "Python [default]", 539 | "language": "python", 540 | "name": "python3" 541 | }, 542 | "language_info": { 543 | "codemirror_mode": { 544 | "name": "ipython", 545 | "version": 3 546 | }, 547 | "file_extension": ".py", 548 | "mimetype": "text/x-python", 549 | "name": "python", 550 | "nbconvert_exporter": "python", 551 | "pygments_lexer": "ipython3", 552 | "version": "3.5.2" 553 | }, 554 | "widgets": { 555 | "state": { 556 | "39297bbfbee3433dade3d731441b98c1": { 557 | "views": [ 558 | { 559 | "cell_index": 33 560 | } 561 | ] 562 | } 563 | }, 564 | "version": "1.2.0" 565 | } 566 | }, 567 | "nbformat": 4, 568 | "nbformat_minor": 1 569 | } 570 | --------------------------------------------------------------------------------