{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# AST416 Astronomide Sayısal Çözümleme - II #\n", "## Ders - 01b Bilgisayarda Sayıların Temsili ve Yuvarlama Problemleri ile Python Çözümleri ##" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Doç. Dr. Özgür Baştürk
\n", "Ankara Üniversitesi, Astronomi ve Uzay Bilimleri Bölümü
\n", "obasturk at ankara.edu.tr
\n", "http://ozgur.astrotux.org" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Bu derste neler öğreneceksiniz?#\n", "## Bilgisayarda Sayıların Temsili ve Yuvarlama Problemleri ile Python Çözümleri ##\n", "\n", "* [Yuvarlama Yanlışları](#Yuvarlama-Yanlışları)\n", "* [Kayan Noktalı Sayılar](#Kayan-Noktalı-Sayılar)\n", "* [Sayı Sistemlerindeki Temsil Sorunları](#Sayı-Sistemlerindeki-Temsil-Sorunları)\n", "* [Kesme ve Yuvarlama İşlemleri](#Kesme-ve-Yuvarlama-İşlemleri)\n", " * [Makine Epsilonu](#Makine-Epsilonu)\n", " * [ Aritmetik İşlemlerin Getirdiği Kesme ve Yuvarlama Yanlışları](#Aritmetik-İşlemlerin-Getirdiği-Kesme-ve-Yuvarlama-Yanlışları)\n", " * [1 Toplama ve Çıkarma](#1-Toplama-ve-Çıkarma)\n", " * [2 Çarpma ve Bölme](#2-Çarpma-ve-Bölme)\n", " * [3 Büyük ve Küçük Sayıyla Toplama Çıkarma](#3-Büyük-ve-Küçük-Sayıyla-Toplama-Çıkarma)\n", " * [4 Yakın İki Sayıyı Çıkarma](#4-Yakın-İki-Sayıyı-Çıkarma)\n", "* [Python'da Kayan Noktalı Sayılar](#Python'da-Kayan-Noktalı-Sayılar)\n", "* [Decimal Modülü](#Decimal-Modülü)\n", " * [Decimal Modülünde Yuvarlama Seçenekleri](#Decimal-Modülünde-Yuvarlama-Seçenekleri)\n", "* [Numpy Modülünde Yuvarlama Seçenekleri](#Numpy-Modülünde-Yuvarlama-Seçenekleri)\n", "* [Pandas Modülünde Yuvarlama Seçenekleri](#Pandas-Modülünde-Yuvarlama-Seçenekleri)\n", "* [Kaynaklar](#Kaynaklar)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Yuvarlama Yanlışları #\n", "\n", "Yuvarlama konusunun ayrıntlı olarak anlatıldığı [Hata Analizi](Ders01_Hata_Analizi_Uygulamalar.html) dersinde ayrıntılı olarak verildiği gibi Yuvarlama yanlışları temelde iki problemden kaynaklanır:\n", "\n", "1. $\\pi$, $e$, $\\sqrt{7}$ gibi sayılar sabit sayıda anlamlı rakamla temsil edilemezler. Bu nedenle bilgisayarlar da bu sayıları tam olarak temsil edemez. \n", "\n", "2. Bilgisayarlar ikilik sayı sistemini kullandıklarından onluk sayı sistemine dayalı tüm sayıları aynı duyarlılıkta temsll edemezler.\n", "\n", "Yuvarlama işlemi kaynaklı hatalar sayıların bilgisayarda tutulma şekline doğrudan bağlıdır. 10 parmağımız olduğu için onluk sayı sistemi biz insanlar için “doğal” olandır. Bir bilgisayar ise sadece 2 parmağı olan bir varlık gibi düşünülebilir. Bu nedenle ikilik sayı sistemi de onun için “doğal” olandır. \n", "\n", "Herhangi bir sayıyı onluk ve ikilik sayı sistemlerinde aşağıdaki şekillerde çözümlenir (Chapra & Canale 2009). " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from IPython.display import Image\n", "Image(filename='images/sayi_sistemleri.png', width=400)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Bu durum her iki sayı sisteminde ve diğer tüm sayı sistemlerinde sayıların mükemmel temsil edilemiyor olmasına yol açar. Buna ek olarak bilgisayarlar sınırlı kapasiteleri nedeniyle sadece belirli bir sayı aralığını temsil edebilirler. Örneğin 16 bitlik bir sayı sistemi varsayılan olarak -32768 ile 32767 arasındaki tam sayıları saklayabilir. 16 bitlik CCD dedektörlerde algılanan fotonların ADU biriminden karşılığının 0-65535 arasında olbilmesinin nedeni de budur. Foton algılama işlemi doğası gereği bir \"sayım\" işlemi olduğundan negatif sayılara ihtiyaç duyulmaz." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from IPython.display import Image\n", "Image(filename='images/sayi_sistemleri_16bit.png', width=400)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Yukarıdaki şekilde şematik olarak gördüğünüz 16 bit ikilik sayı sisteminde, her bir basamak sadece 0 veya 1 değerini alabilir ve toplamda 16 basamak vardır. 1. basamak işaret için ayrılır ve 0 iki kere sayılmaz. En baştaki 0: pozitif, 1: negatif sayıları ifade eder. Bu durumda yazılabilecek en küçük sayı $1111111111111111$ (bu sayının onluk sayı sistemindeki karşılığı -32767 olduğu halde 0'ı iki kez saymamak için onu pozitif sayılara dahil edip saymaya -1'den başladığımız için -32768) olurken en büyük sayı $0111111111111111$ (32767) olur. \n", "\n", "[Başa Dön](#Bu-derste-neler-öğreneceksiniz?)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Kayan Noktalı Sayılar #\n", "\n", "Bilgisayarlar tam sayılar dışındaki sayıları göstermek üzere kayan noktalı sayıları (ing. floating point numbers) kullanırlar. Aşağıda kayan noktaları sayıların hafızada saklanma şeklini örnekleyen bir şekil bulunmaktadır. Herhangi bir sayı bu şekilde m: mantis, b: taban, e: üssü göstermek üzere\n", "\n", "$$ m~b^e $$\n", "\n", "şeklinde ifade edilir." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from IPython.display import Image\n", "Image(filename='images/sayi_sistemleri_floatingpoint.png', width=400)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Örneğin onluk sayı sistemindeki $156.78$ sayısını düşünelim. Bu sayıyı bilimsel gösterimde (ing. scientific notation) şu şekilde ifade edebiliriz: $0.15678 x 10^{3}$.\n", "\n", "Eğer mantis başında gereksiz $0$ sayıları varsa normalize edilebilir. Örneğin, $1 / 34 = 0.029411765...$ sadece 4 basamağa izin verilen 10'luk bir kayan sayı sisteminde $0.0294 x 10^0$ şeklinde saklanabilir. Ancak bu şekilde noktadan sonraki 0 nedeniyle 5. basamaktaki $1$ sayısını kaybetmiş oluruz. Bunu sayıyı $0.2941 x 10^{-1}$ şeklinde göstererek aşabiliriz. Noktanın bu şekilde kaydırılması bu sayıların \"kayan noktalı sayılar” (ing. floating point numbers) olarak adlandırılmış olmasının da kaynağıdır. Böylece noktadan sonra gelen gereksiz 0'a da izin verilmemiş olur. Bu işleme normalizasyon adı verilir ve sayılarda anlamsız rakamların elenmesi işlevini de görür.\n", "\n", "Normalizasyonun doğal bir sonucu mantis (m) için limitli bir değer aralığına sahip olunmasıdır. b, tabanı; m, mantisi göstermek üzere\n", "\n", "$$\\frac{1}{b} \\le m \\lt 1$$\n", "\n", "Örneğin onluk sayı sisteminde \n", "\n", "$$0.1 \\le m \\lt 1$$ iken ikilik sayı sisteminde \n", "\n", "$$ 0.5 \\le m \\lt 1$$ " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Örnek: Yedi bitlik ikilik bir sayı sistemini ele alalım (Şekil Chapra & Canale 2009'dan alınmış ve Türkçeleştirilmiştir) ve bu sayı sisteminde ifade edilebilecek en büyük ve en küçük sayıları hesaplamak istiyor olalım." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from IPython.display import Image\n", "Image(filename='images/sayi_sistemleri_7bit.png', width=300)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Bu sistemde ifade edebileceğimiz en küçük poziftif sayı $011100$ 'dir. bu sayının onluk sayı sistemindeki karşılığı $m = 1x2^{-1} + 0x2^{-2} + 0x2^{-3} = 0.5$, $b=2$ , $e = -(1x2^0 + 1x2^1) = -3$ olduğundan \n", "\n", "$$ - (1x2^{-1} + 0x2^{-2} + 0x2^{-3}) x 2^{-(1x2^1+1x2^0)} = -0.5 x 2^{-3} = -0.0675 $$\n", "\n", "sayısıdır. \n", "\n", "En büyük poziftif sayı ise $0011111$ sayısı olup onluk sayı sistemindeki karşılığı $m = 1x2^{-1} + 1x2^{-2} + 1x2^{-3} = 0.875$, $b=2$ , $e = 1x2^0 + 1x2^1 = 3$ olduğundan \n", "\n", "$$ 1x2^{-1} + 1x2^{-2} + 1x2^{-3}) x 2^{1x2^1 + 1x2^0} = 0.875 x 2^{3} = 7 $$\n", "\n", "sayısıdır.\n", "\n", "Aslında bu sayı sisteminde daha küçük mantisler yazabiliriz ($000$, $001$, $010$, $011$) ancak normalizsyon gereği noktadan sonraki gereksiz $0$'ları atmaya yönelik bir işlem uygulanacağından buna izin verilmez. Dolayısı ile en küçük olarak ifade edilebilien bu sayıyı aşağıdaki sayılar takip eder:\n", "\n", "$$ 0111101 = (1 x 2^{−1} + 0 x 2^{−2} + 1x2^{−3}) x 2^{−3} = (0.078125)_{10} $$\n", "$$ 0111101 = (1 x 2^{−1} + 1 x 2^{−2} + 0x2^{−3}) x 2^{−3} = (0.093750)_{10} $$\t\n", "$$ 0111101 = (1 x 2^{−1} + 1 x 2^{−2} + 1x2^{−3}) x 2^{−3} = (0.109375)_{10} $$\t\n", "\n", "Bu sayı sistemiyle $0.0625$ sayısından küçük sayıların temsil edilememesinin yanı sıra her sayının da temsil edilemediğine dikkat ediniz. Sayılar $0.015625$ aralıkla artmakta, bu artışın altındaki artışlarla ulaşılacak sayılar gösterilememektedir! Bu noktadan sonra sayıyı büyütmek için üssü büyütmeliyiz!\n", "\n", "$$ 0110100 = (1 x 2^{−1} + 0 x 2^{−2} + 0x2^{−3}) x 2^{−2} (0.125000)_{10} $$\n", "$$ 0110101 = (1 x 2^{−1} + 0 x 2^{−2} + 1x2^{−3}) x 2^{−2} = (0.156250)_{10} $$\n", "
\n", "...
\n", "...
\n", "...
\n", "
\n", "$$ 0011111 = (1 x 2^{−1} + 0 x 2^{−2} + 1x2^{−3}) x 2^{3} = (7)_{10} $$\n", "\n", "Görüldüğü üzere sayılar büyüdükçe aralarındaki farklar da artmakta ve tam olarak temsil edilemeyen sayıların sayısı da buna bağlı olarak artmaktadır. Ayrıca 7 sayısının üstündeki sayılar da bu sayı sisteminde temsil edilememektedir. 7 bitlik sayı sistemi kuşkusuz modern bilgisayarlarda kullanılan 32 bit ve 64 bitlik sayı sistemleriyle karşılaştırıldığında son derece basit ve kullanışsız ve bir sistemdir. Ancak onlarda karşılaşılan güçlükleri örneklemek açısından faydaldır.\n", "\n", "[Başa Dön](#Bu-derste-neler-öğreneceksiniz?)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Sayı Sistemlerinde Temsil Sorunları ve Sonuçları #\n", "\n", "Yukarıdaki örneklerden çıkarsanması gereken dört temel sonuç:\n", "\n", "1. Sadece belirlii bir aralıktaki sayılar temsil edilebilir. Tıpkı tam sayıda olduğu gibi ($-32768 – 32767$) kayan noktalı sayı gösteriminde de çok büyük pozitif ve çok küçük negatif sayıların temsili sorunu vardır. Bu sınırların dışına çıkmaya kalkarsanız taşma (overflow) hatası alırsınız. \n", "\n", "2. Normalizasyon nedeniyle çok küçük pozitif sayıların gösterimiyle de ilgili bir problem yaşanır. Bu problem 0'dan sonra ilk temsil edilebilen sayıya kadar büyük bir boşluğun (underflow) oluşmasına neden olur.\n", "\n", "3. Tüm nicelikler temsil edilemezler! Bu nedenle hassasiyet de sınırlıdır. Açık ki irrasyonel sayılar tam olarak temsil edilemezler. Ayrıca temsil edilebilen küme dışında kalan rasyonel sayılar da tam olarak temsil edilemezler. Bu sorun kuantizasyon hatası (quantization error) olarak bilinen hataya yol açar.\n", "\n", "4. Temsil edilmek istenen sayı büyüdükçe temsil edilebilen sayılar arasındaki uzaklığın da artmasıdır (64 bitlik sayı sisteminde $1.0$ ile $2.0$ arasında $8~388~607$ tane kayan noktalı sayı varken $1023$ ile $1024$ arasında sadece $8191$ tane vardır).\n", "\n", "Çözüm: Temsil edemediğniz basamakları ya keser atarsınız (ing. truncation) ya da yuvarlarsınız (rounding)! " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from IPython.display import Image\n", "Image(filename='images/sayi_sistemleri_yuvarlama_kesme.png', width=500)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "[Başa Dön](#Bu-derste-neler-öğreneceksiniz?)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Kesme ve Yuvarlama İşlemleri #\n", "\n", "“Kırpma” ya da kesme (truncation, chopping) işlemi uygulanırsa, şekilden de görüleceği gibi sondan atılan $\\Delta x$ kadar hata yapılmış olur. Bu durumda yapılan hata her zaman pozitiftir. Örneğin $\\pi = 3.14159265358...$ sayısına bakalım. Sayımız 6. basamaktan sonra temsil edilemiyor olsun. Bu durumda kesme işi uygularsak sayı $\\pi ~ 3.141592$ şeklinde ifade edilir ve hatası $|\\epsilon_t = 0.00000065...$ olur. Oysa ki yuvarlama işlemi uygulanmış olsaydı sayı $\\pi ~ 3.141593$ şeklinde ifade edilir ve hatası $\\epsilon_t = 0.00000035...$ olurdu. Dolaysı ile kesme işleminde hatanın üst sınırı $\\Delta x$ iken, yuvarlamada $\\Delta x / 2$'dir. Ayrıca yuvarlama işleminden gelen hatada yanlılık da mevcut değildir; pozitif olabileceği gibi negatif de olabilir. Ancak yuvarlama işlemi ek kod gerektirir. Bu ek koddan kaçınmak için, modern bilgisayarların oldukça büyük ve küçük sayıları saklayabiliyor olması gerekçesiyle, kesme işlemine gidilir.\n", "\n", "Bir başka problem de temsil edilmek istenen sayı büyüdükçe temsil edilebilen sayılar arasındaki uzaklığın da artmasıdır. Dolayısı ile kuantizasyon hatası sayının büyüklüğüne bağlıdır. Bu ilişki kesme işlemi için $\\Delta x / x \\le \\epsilon$, yuvarlama işlemi için $\\Delta x / x \\lt \\epsilon / 2$ şeklinde ifade olunur. Burada $\\epsilon$ sistem (ya da makine) epsilonu olarak bilinir ve $\\epsilon = b^{1 - t}$ olarak ifade edilir. t, mantisteki anlamlı rakam sayısını göstermektedir. \n", "\n", "Örnek: 7 bitlik hayali sayı sistemimiz için makine epsilonu değerini hesaplayalım:\n", "\n", "Sistemimizin tabanı 2 olduğundan $b = 2$ 'dir. Mantisteki anlamlı rakam sayısı $t = 3$ 'tür. Bu durumda makine epsilonu $\\epsilon = b^{1 - t} = 2^{-2} \\rightarrow \\epsilon = 0.25$ olarak bulunur. Dolayısı ile kesme işlemi uygulandığında yapılabilecek maksimum göreli hata da bu kadar olacaktır. Sayılar büyüdükçe kesme işlemi sonucu yapılan hata büyüyor olsa da sayının mutlak değeri de büyüdüğünden göreli hata değeri bu üst sınırın altında kalmaya devam edecektir. Örneğimizde maksimum hata $(0.125000)_{10}$ ile $(0.156250)_{10}$ arasında gerçekleşmektedir. Dolayısı ile hata $(0.156250 – 0.125000) / 0.125000 = 0.25$ olur. \n", "\n", "Önemli Sonuç: Bu problem özellikle iki kayan noktalı sayıyı karşılaştırırken karşımıza çıkar. Matematiksel olarak birbirine eşitmiş gibi görünen iki ifade kesme ya da yuvarlama nedeniyle birbirinden farklı iki kayan noktalı sayıyla sonuçlanabilir. Doğrusu iki kayan noktalı sayıyı karşılaştırmak yerine, aralarındaki farkın mutlak değerini yeterince küçük seçilmiş bir tolerans değeriyle karşılaştırmak; bu tolerans değerini seçerken ise programı sistem bağımsız hale getirmek üzere makine epsilonundan faydalanmaktır! \n", "\n", "$a$ ve $b$ kayan noktalı sayıları için; $a == b$ ya da $a != b$ yerine $abs(a – b) \\gt tolerans $ (örn. $2~\\epsilon$) gibi bir karşılaştırma yapmak daha doğrudur.\n", "\n", "[Başa Dön](#Bu-derste-neler-öğreneceksiniz?)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Makine Epsilonu ##\n", "\n", "Makine epsilonu aşağıdaki basit kodla hesaplanabileceği gibi her sistemin makine epsilionu sys.float_info.epsilon ile de öğrenilebilir." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "epsilon = 1.\n", "while epsilon + 1 > 1:\n", " epsilon /= 2.\n", "epsilon *= 2 \n", "print(\"Makine epsilonu\", epsilon)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import sys\n", "print(sys.float_info.epsilon)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "7 bitlik hayali sayı sistemi örneğimiz konuyu daha iyi kavratabilmek amacıyla oldukça abartılı limitlere sahiptir. Modern bilgisayarlar kayan noktalı sayı hassasiyeti konusunda çok daha başarılıdır. IEEE standardını takip eden bilgisayarlar sayının işareti için 1 bit, üs için 8 bit, mantis için 24 bit (toplam 32 bit) kullanırlar. Normalizasyon nedeniyle bu bitlerini birincisi her zaman $1$ olduğundan geriye kalan 23 bitle $10^{-38} - 10^{39}$ arasındaki sayılar ifade edilebilmektedir.\n", "\n", "Daha da fazla hassasiyete ihtiyaç duyulduğunda pek çok sistem ek bir hassasiyet seçeneği sunar: Çifte Duyarlılık (Double Precision). 15-16 ek basamağın sağlandığı bu seçenekle $10^{-308} - 10^{308}$ arasındaki sayıları ifade edilebilecek duyarlılığa kadar inilebilmektedir. \n", "\n", "Tabi bu ek duyarlılık işlemci ve RAM üzerine daha çok yük ve dolayısı ile daha uzun çalışma dezavantajıyla birlikte gelir, bu nedenle uygun yerlerde, dikkatli ve seçilerek kullanılması gereklidir!\n", "\n", "[Başa Dön](#Bu-derste-neler-öğreneceksiniz?)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Aritmetik İşlemlerin Getirdiği Kesme ve Yuvarlama Yanlışları ##\n", "\n", "### 1 Toplama ve Çıkarma ### \n", "\n", "Toplama ve çıkarma işlemleri küçük sayının mantisi her iki sayının üssü eşit olacak şekilde düzenlendikten sonra yapılır. \n", "\n", "Örnek 1. $1.5571$ ile $0.4381$ sayılaırnı topluyor olalım. Bu sayılar alt alta yazılırken noktanın aynı yere getirilmesine dikkat ediliyor.\n", "\n", "$$ 0.1557~~~~10^1 $$\n", "$$ 0.004381~~~10^1 $$\n", "$$ +----------- $$\n", "$$ 0.160081~~~10^1 $$\n", "\n", "Sayı daha az sayıda anlamlı kamak içeren (4) sayı kadar anlamlı rakam içereceğinden ve bunun için kesileceğinden;\n", "\n", "$$ 0.1600 x 10^1 $$\n", "\n", "şeklinde ifade edilir.\n", "\n", "Örnek 2. $364.1$'den $268.6$'yı çıkarıyor olalım.\n", "\n", "$$ ~0.3641~~~~10^2 $$\n", "$$- 0.2686~~~~10^2 $$\n", "$$ ------------ $$\n", "$$ ~0.0955~~~~10^2 $$\n", "\n", "Normalizasyon gereği sayı,\n", "\n", "$$ 0.9550 x 10^1 $$\n", "\n", "şeklinde ifade edilir.\n", "\n", "Örnek 3. \n", "\n", "$$ ~0.7642~~~~10^3 $$\n", "$$- 0.7641~~~~10^2 $$\n", "$$ ------------ $$\n", "$$ ~0.0001~~~~10^3 $$\n", "\n", "Normalizasyon gereği sayı,\n", "\n", "$$ 0.1000 x 10^0 = 0.1$$\n", "\n", "şeklinde ifade edilir.\n", "\n", "### 2 Çarpma ve Bölme ### \n", "\n", "Çarpma ve bölme işlemleri görece daha kolaydır: Mantisler çarpılır, üsler toplanır! Pek çok sistem ara adımları çift duyarlılıkta saklayarak ilerler.\n", "\n", "Örnek: \n", "\n", "$$ 0.6313~10^3~x~0.6423~10^{-1} = 0.08754549~10^2 $$\n", "\n", "Normalizasyon kesme işlemleri sonrası\n", "\n", "$$ 0.8754549~10^1 \\Rightarrow 0.8754~10^1$$\n", "\n", "### 3 Büyük ve Küçük Sayıyla Toplama Çıkarma ### \n", "\n", "Kesme / yuvarlama nedeniyle bazen işlem yapılmamış gibi bir sonuçla dahi karşılaşılabilir.\n", "\n", "$$ 0.4000~~~~10^4 $$\n", "$$ 0.0000001~~~10^4 $$\n", "$$ +----------- $$\n", "$$ 0.4000001~~~10^4 $$\n", "\n", "Kesme işlemi sonrası\n", "\n", "$$ 0.4000~10^4 $$" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "x = 0.000001\n", "toplam = 0.\n", "for i in range(0, 1000000, 1):\n", " toplam += x\n", "print(\"Toplam: \", toplam)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 4 Yakın İki Sayıyı Çıkarma ### \n", "\n", "Birbirlerine çok yakın iki sayı çıkarılmaya çalışıldığında sayıların tam temsil edilememesi ve yuvarlama / kesme işlemi kaynaklı olan, ancak beklenmeyen sorunlarla karşılaşılabilir. Buna iyi bir örnek ikinci dereceden bir denklemin kökleri hesaplanırken karşılaşılabilecek olan aşağıdaki gibi bir sonuçtur.\n", "\n", "$$ \\frac{-b \\pm \\sqrt{b^2 - 4~a~c}}{2~a} $$" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from math import sqrt\n", "a = 1.\n", "b = 3000.001\n", "c = 3.\n", "x1 = (-1*b - sqrt(b**2 - 4*a*c))/(2*a)\n", "x2 = (-1*b + sqrt(b**2 - 4*a*c))/(2*a)\n", "print(\"x1: \", x1)\n", "print(\"x2: \", x2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "[Başa Dön](#Bu-derste-neler-öğreneceksiniz?)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Python'da Kayan Noktalı Sayılar #\n", "\n", "Görüldüğü gibi kayan noktalı sayılar ilgili olarak karşılaşılan en temel sorun onluk sayı sistemindeki bir sayının ikilik sayı sisteminde tam olarak ifade edilememesidir. Örneğin, $0.1$ sayısının ikilik sayı sistemindeki karşılığı $0.0001100110011001100110011001100110011001100110011...$ \n", "\n", "Python $repr()$ fonksiyonu ve Python promptu kayan noktalı bir sayıyı 17 basamak duyarlıılıkla gösterir. İstenirse sayı ekrana daha büyük bir duyarlılıkla da getirilebilir." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "scrolled": true }, "outputs": [], "source": [ "from math import pi \n", "print(format(pi, '.12g'))\n", "print(repr(pi))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Gerçekte bu bir ilüzyondur. Zira bu sayının tam bir gösterimini yapabilmek mümkün değildir. Ekrana bindirilen görüntü her zaman belirli duyarlılıkta bir yaklaşımdan ibarettir. Aşağıdaki örnek daha açıklayıcıdır." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print(.1 + .1 + .1 == .3)\n", "print(round(.1, 1) + round(.1, 1) + round(.1, 1) == round(.3, 1))\n", "print(round(.1 + .1 + .1, 10) == round(.3, 10))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "math modülü fonksiyonlarından fsum ise toplama işleminde yuvarlama kaynaklı hataların giderilmesini sağlar." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from math import fsum\n", "print(sum([.1, .1, .1, .1, .1, .1, .1, .1, .1, .1]))\n", "print(fsum([.1, .1, .1, .1, .1, .1, .1, .1, .1, .1]))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Python'da bir kayan noktalı sayının hangi iki tam sayının oranı olarak ifade edildiğini veren bir metot bulunmaktadır. Kayan sayı nesnelerinin bir metodu olan as_integer_ratio()." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print(3.5.as_integer_ratio())\n", "from math import pi\n", "print(pi.as_integer_ratio())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Bu fonksiyonu kullanarak 0.1 sayısının nasıl temsil edildiğine bakalım. 1/10 değil, çünkü 1/10 ikilik sayı sisteminde tam olarak gösterilemez!" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ " 0.1.as_integer_ratio()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$fractions$ modülü fonksyonları bu tür sorunların çözümünü kolaylaştırmak konusunda oldukça yardımcı olur. " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from fractions import Fraction\n", "Fraction.from_float(0.1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "[Başa Dön](#Bu-derste-neler-öğreneceksiniz?)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Decimal Modülü #\n", "\n", "Decimal modülü kayan noktalı sayılarla işlemlerde hızlı ve doğru bir şekilde yuvarlama olanağı sağlayan fonksiyonlara sahiptir. Modül float nesnesine göre bazı önemli avantajlarla gelmektedir.\n", "\n", "Decimal insanların elle işlem yapma prensiplerini temel alarak dizayn edilen bir kayan noktalı sayı modeline dayanır (IEEE Standart 854-1987). Bu şekilde bilgisayarın insanların okulda öğrendikleri şekilde işlem yapabilmelerine olanak sağlamış olur. $Decimal$ modülünde sayılar onluk sayı sisteminde temsil edildikleri şekliyle \"tam olarak\" temsil edilebilirler. $0.1$ gibi sayılar standart float veri türü ile gördüğünüz gibi tam olarak temsil edilememektedir. \n", "\n", "Modül üç kavram üzerine kuruludur: Decimal sayı nesnesi (decimal number), aritmetik işlemler ve uyarılar (Warnings). Decimal sayı nesnesi içeriği değiştirilemezdir (immutable). İşareti, mantisi ve üssü ile tanımlıdır. Sayıların sonundaki sıfırlar anlamlı rakam sayısını korumak üzere atılmaz. $Infinity$, $-Infinity$ ve $NaN$ gibi özel türleri de içerir. Aritmetik işlemler için kurallar tanımlıdır ve pek çok yuvarlama opsiyonu sağlanır (ROUND_CEILING, ROUND_DOWN, ROUND_FLOOR, ROUND_HALF_DOWN, ROUND_HALF_EVEN, ROUND_HALF_UP, ROUND_UP ve ROUND_05UP). İşlem sonucuna göre de pek çok uyarı döndürme opsiyonu bulunmaktadır ($InvalidOperation$, $DivisionByZero$, $Inexact$, $Rounded$, $Subnormal$, $Overflow$, $Underflow$ ve $FloatOperation$).\n", "\n", "Genel olarak onluk sayı sisteminde ifade edildiği şekilde bir metin nesnesi (string) olarak tanımlanır ve daha sonra `decimal.Decimal` fonksiyonu kullanılarak bir `Decimal` nesnesine dönüştürülür." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from decimal import *\n", "Decimal(\"0.1\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Bir kayan noktalı sayıyı da bir `Decimal` dönüştürmek mümkündür. Ancak bu kez sayı amaçlandığı şekilde onluk sayı sisteminde ifade edildiği şekilde değil ikilik sayı sisteminde ifade edilebildiği şekilde onluk sayı sistemine dönüştürülmüş olur." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "Decimal(0.1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Sayıları ondalık sayı sisteminde olduğu şekilde temsil edebildiğiniz gibi ölçüm duyarlılığı da korunur. Ancak işlemlerde anlamlı rakam sayısı sonuca tam olarak [anlamlı rakamlarla aritmetik kurallarına](Ders01_Hata_Analizi.ipynb#Anlamlı-Rakamlarla-Aritmetik) göre transfer edilmez." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print(Decimal('0.1') + Decimal('0.1') + Decimal('0.1') == Decimal('0.3'))\n", "print(Decimal('3.40') + Decimal('1.60'))\n", "print(Decimal('0.745')*Decimal('2.2')/Decimal('3.885'))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Decimal modülünde işlem yaparken sisteminiz tarafından sağlanan duyarlılığın üzerine çıkabilirsiniz. Daha fazla bilgi için [bkz](https://docs.python.org/3/library/decimal.html#module-decimal)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "getcontext().prec = 6\n", "print(Decimal(1) / Decimal(7))\n", "getcontext().prec = 28\n", "print(Decimal(1) / Decimal(7))\n", "Decimal('0.1428571428571428571428571429')\n", "print(getcontext()) # limitler" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Pek çok uygulama için $getcontext$ fonksiyonu ile mevcut ortama (kontekst) ulaşılarak ayarlar değiştirilebliir. Ancak bazen kullanıcı kendi ortamını (kontekst) yaratmaya ya da birden fazla ortamda işlem yapmaya ihtiyaç duyabilir. $Context()$, $setcontext()$ metodları yeni bir ortam oluşturmak için kullanılabilir. $decimal$ modülü tarafından sağlanan standart ortamlar (kontekstler) ise $BasicContext$ ve $ExtendedContext$'tir. " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "benimortamim = Context(prec=60, rounding=ROUND_HALF_DOWN)\n", "setcontext(benimortamim)\n", "print(\"Benim Ortamim\")\n", "print(\"1/7 = \", Decimal(1) / Decimal(7))\n", "print(\"----------------------\")\n", "print(\"Extended Context\")\n", "ExtendedContext\n", "setcontext(ExtendedContext)\n", "print(\"1/7 = \", Decimal(1) / Decimal(7))\n", "print(\"42 / 0 =\", Decimal(42) / Decimal(0))\n", "print(\"------------------------\")\n", "print(\"Basic Context\")\n", "setcontext(BasicContext)\n", "print(\"42 / 0 = \", Decimal(42) / Decimal(0))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Kontekstlerin problemli durumlar karşısında kullanıcıyı uyarmak üzere uyarı mesajı işaretleri ($flag$) vardır. Bu işaretleri kaldırmadan işlemlere devam edilmez, bunun için clear_flags() metodu kullanılır." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "setcontext(ExtendedContext)\n", "print(\"355 / 113 = \", Decimal(355) / Decimal(113))\n", "print(getcontext().clear_flags())\n", "print(\"355 / 113 = \", Decimal(355) / Decimal(113))\n", "print(getcontext())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Burada yapılan bölmenin yaklaşık sonucunun yuvarlandığını ($Rounded$) ve bu nedenle sonucun tam olmadığını ($Inexact$) gösteren bir işaret ($Flag$) bulunmaktadır. \n", "\n", "İstendiğinde bir kontekstin (ortamın) hata mesajı verecek şekilde ayarlanması mümkündür. Bunun için kontekstin traps sözlüğünden faydalanılır." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "setcontext(ExtendedContext)\n", "print(\"1 / 0 = \", Decimal(1) / Decimal(0))\n", "getcontext().traps[DivisionByZero] = 1\n", "print(\"1 / 0 = \", Decimal(1) / Decimal(0))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "[Başa Dön](#Bu-derste-neler-öğreneceksiniz?)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Decimal Modülünde Yuvarlama Seçenekleri ##\n", "\n", "`Decimal` modülünde yuvarlama işlemleri için `quantize` metodu kullanılır. Herhangi bir yuvarlama stratejisi kullanılabilir ancak varsayılan yöntemde (ROUND_HALF_EVEN) yuvarlamada tıpkı `round` fonksiyonunda olduğu gibi, klasik olarak takip edilen yuvarlama kurallarına uyulur. Ancak bu kez yuvarlamada sayının gösterim şekli esas alınır." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print(Decimal('1.65').quantize(Decimal('1.0')))\n", "print(Decimal('7.325').quantize(Decimal('.01'), rounding=ROUND_DOWN))\n", "print(Decimal('7.325').quantize(Decimal('1.'), rounding=ROUND_UP))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Son olarak kullanılan yuvarlama kuralı kalıcı hale getirildiğinden varsayılana dönmek için varsayılan kuralın tekrar hatırlatılması gerekir." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print(Decimal('1.65').quantize(Decimal('1.0')))\n", "print(Decimal('1.65').quantize(Decimal('1.0'), rounding=ROUND_HALF_EVEN))\n", "print(Decimal(\"2.675\").quantize(Decimal(\"1.00\")))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`Decimal` modülünün yuvarlama fonksiyonlarından bazıları klasik yuvarlama stratejilerinden bir miktar farklı çalışmaktadır. Örneğin ROUND_HALF_UP fonksiyonu beklentinin (ve genel kuralın) aksine sayıları 0'dan uzaklatırşacak şekilde yuvarlarken ROUND_HALF_DOWN sayıları 0'a yaklaştıracak şekilde yuvarlar." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "getcontext().rounding = ROUND_UP\n", "print(Decimal(\"4.42\").quantize(Decimal(\"1.0\")))\n", "print(Decimal(\"-4.42\").quantize(Decimal(\"1.0\")))\n", "getcontext().rounding = ROUND_DOWN\n", "print(Decimal(\"6.28\").quantize(Decimal(\"1.0\")))\n", "print(Decimal(\"-6.28\").quantize(Decimal(\"1.0\")))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Bu iki fonksiyonun sondaki rakamın 5 olması durumundaki her iki yöndeki (pozitif ve negatif) davranışları da aynı şekildedir. ROUND_UP 0'dan uzaklaştıracak, ROUND_DOWN 0'a yaklaştıracak şekilde sayıları yuvarlar. ROUND_HALF_UP ve ROUND_HALF_DOWN 'ın davranışları da benzerdir." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "getcontext().rounding = ROUND_HALF_UP\n", "print(Decimal(\"4.45\").quantize(Decimal(\"1.0\")))\n", "print(Decimal(\"-4.45\").quantize(Decimal(\"1.0\")))\n", "getcontext().rounding = ROUND_HALF_DOWN\n", "print(Decimal(\"6.25\").quantize(Decimal(\"1.0\")))\n", "print(Decimal(\"-6.25\").quantize(Decimal(\"1.0\")))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`Decimal` modülündeki ROUND_05UP fonksiyonu ise ilginç bir yuvarlama stratejisi daha sağlar. " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "getcontext().rounding = ROUND_05UP\n", "print(Decimal(\"3.76\").quantize(Decimal(\"1.0\")))\n", "print(Decimal(\"4.45\").quantize(Decimal(\"1.0\")))\n", "print(Decimal(\"-4.45\").quantize(Decimal(\"1.0\")))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Yukarıdaki davranışı itibarı ile strateji her sayıyı 0'a doğru yuvarlar gibi görünmektedir. Ancak aşağıdaki ilk örnekte sayı önce 0'a doğru yuvarlanmaktadır ($1.4$). Ancak bir sonraki sayı 4 olduğu (0 ya da 5 olmadığı) için sayı olduğu gibi bırakılmaktadır. İkinci örnekte ise sayı 0' doğru yuvarlandıktan sonra ($1.5$) 5 ile karşılaşılmakta, bu ise sayının yukarıya yuvarlanmasına neden olmaktadır. Karşılaşılan sayı 0 olduğunda da davranış şekli aynıdır." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print(Decimal(\"1.49\").quantize(Decimal(\"1.0\")))\n", "print(Decimal(\"1.51\").quantize(Decimal(\"1.0\")))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "[Başa Dön](#Bu-derste-neler-öğreneceksiniz?)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Numpy Modülünde Yuvarlama Seçenekleri #\n", "\n", "`Numpy` modülündeki `around` fonksiyonu, Python'un `round` fonksiyonuna benzer şekilde çalışır ve tıpkı onun gibi sayıların mükemmel temsil edilememesi sorunundan muzdariptir." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", "np.random.seed(444) #her calismada ayni ciktiyi uretmek icin\n", "dizi = np.random.randn(3, 5)\n", "print(dizi)\n", "yeni_dizi = np.around(dizi, decimals=4)\n", "print(yeni_dizi)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`math` modülündeki `ceil`, `trunc` ve `floor` fonksiyonlarının diziler için çalışan eşlenikleri `numpy` için de vardır." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print(np.ceil(dizi))\n", "print(np.trunc(dizi))\n", "print(np.floor(dizi))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Sayılardan bazılarının negatif sıfıra ($-0$) yuvarlandığında dikkat ediniz. Negatif 0, bize bu sayının yuvarlandığını ve 0'dan küçük bir sayıdan yuvarlandığını söylemektedir. Bu durum, örneğin suyun sıcaklığı olduğunda onun donma noktasnın altında bir sıcaklığa sahip olduğu konusunda bir fikir verebilir.\n", "\n", "Tam sayıya yuvarlamada klasik yuvarlama stratejisi numpy modülünde `rint` fonksiyonu ile sağlanmıştır." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print(np.rint(dizi))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Diğer yuvarlama seçenekleri numpy'da verilen bu seçenekler kullanılarak kodlanabilir. Numpy modülü `Decimal` modülü ile birlikte kullanılamamaktadır. Yine de istenirse, istenen gösterimdeki metin nesneleri bir liste içinde `Decimal` nesnesine dönüştürüldükten sonra `numpy.array` fonksiyonu ile numpy dizilerine dönüştürülebilir." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "[Başa Dön](#Bu-derste-neler-öğreneceksiniz?)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Pandas Modülünde Yuvarlama Seçenekleri #\n", "\n", "`math` modülündeki `round` ve `numpy` modülündeki `around` fonksiyonlarının `pandas` modülündeki karşılığı da `round` fonksiyonudur ve hem pandas serileri (`pandas.series`) hem de veri çeerçeveleri (`pandas.DataFrame`) üzerinde kullanılabilir." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import pandas as pd\n", "import numpy as np\n", "np.random.seed(444)\n", "seri = pd.Series(np.random.randn(5))\n", "print(seri)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print(seri.round(3))" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "df = pd.DataFrame(np.random.randn(4, 4), columns=[\"A\", \"B\", \"C\", \"D\"])\n", "print(df)\n", "df.round(2) " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "İstenirse her bir sütun (seriye) ayrı bir basamağa da yuvarlanabilir." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "df.round({\"A\": 1, \"B\": 2, \"C\": 3, \"D\": 0})" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "basamak_sayisi = pd.Series([3, 0, 1, 2], index=[\"A\", \"B\", \"C\", \"D\"])\n", "df.round(basamak_sayisi)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`numpy` modülünün `ceil`, `trunc`, `floor` ve `rint` fonksiyonları `pandas` serileri ve dizileri üzerinde de kullanılabilir." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print(np.ceil(dizi))\n", "print(np.floor(dizi))\n", "print(np.trunc(dizi))\n", "print(np.rint(dizi))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Gerek yuvarlamada, gerekse hata hesaplarında kritik olan tüm kavramalara hakim olup yerli yerince kullanmaktır. Kullanılan gözlemsel / deneysel verinin duyarlılığına bağlı seçimler yapılarak uygun yuvarlama / kesme seçenekleri kullanılmalı; anlamlı rakamlara özen gösterilmelidir. İşlemler ve fonksiyonlar sonucu elde edilen niceliklerin duyarlılıkları (üzerlerindeki belirsizlikler) verilirken işleme giren parametrelerin üzerindeki belirsizlikler dikkate alınmalıdır. Bir sonucun hatası verileriken hatanın türü (standart sapma, standart hata, tahmini hata, uyumlamadan fark karelerin toplamının karekökü (ing. root-mean-square, rms), varyans ...) mutlaka belirtilmeli, verilen niceliğin basamak sayısı ile hatasının basamak sayısnın aynı olmasına özen gösterilmeldir. \n", "\n", "[Başa Dön](#Bu-derste-neler-öğreneceksiniz?)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Kaynaklar #\n", "\n", "* Sayı Sistemleri ve Yuvarlama Problemleri: \"Numerical Methods for Engineers\", 6th ed., Steven C. Chapra, Raymond P. Canale, McGraw Hill, 2009\n", "\n", "* Yuvarlama ve Decimal Modülü Konuları: [Real Python](https://realpython.com/python-rounding/), Dan Bader\n", "\n", "* Yuvarlama Konusu (round fonksiyonu): [round Fonksiyonu Dokümantasyonu](https://docs.python.org/3/library/functions.html#round)\n", "\n", "* [Decimal Modülü Dokümantasyonu](https://docs.python.org/2/library/decimal.html)\n", "\n", "* [Kayan Noktalı Sayılarla Aritmetik](http://perso.ens-lyon.fr/jean-michel.muller/goldberg.pdf)\n", "\n", "* [Python'da Kayan Noktalı Sayılar](https://docs.python.org/3/tutorial/floatingpoint.html)\n", "\n", "* [IEEE-754 Standartları](https://en.wikipedia.org/wiki/IEEE_754)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "[Başa Dön](#Bu-derste-neler-öğreneceksiniz?)" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.6.9" } }, "nbformat": 4, "nbformat_minor": 2 }