Ana içeriğe geç

Veri Analizinde Fonksiyonel Programlama ve Nesne Yönelimli Programlama

Veri biliminde en yaygın kullanılan iki programlama yaklaşımını keşfedin: nesne yönelimli programlama ve fonksiyonel programlama.
· 15 dk. oku

Yapay Zeka ile Keşfedin

İyi yapı, iyi yazılmış kodun ayırt edici özelliğidir. Bu yapının niteliği, kullanılan programlama paradigmasına göre belirlenir. Programlama paradigması, bilgisayar programları tasarlanırken ve kodlanırken izlenen yaklaşımı tanımlayan, tutarlı bir ilke ve teknikler bütünüdür.

Yordamcı (procedural), buyurgan (imperative), bildirime dayalı (declarative), nesne yönelimli ve fonksiyonel gibi pek çok programlama paradigması vardır. Bazı diller yalnızca birkaç paradigmaya sınırlıdır. Örneğin SQL, bildirime dayalı bir dildir. Ancak Python da dâhil olmak üzere birçok modern dil, esneklik sağlayarak birden çok programlama paradigmasını destekler.

Saf fonksiyonlar yazmayı uygulayın; Programlama Paradigmalarına Giriş kursumuzdan uygulamalı bir alıştırma ile. 

Birçok programcı varsayılan olarak tercih ettiği bir paradigmaya yönelir; çoğu zaman ilk öğrendiğine. Daha ileri düzey programcılar ise bu paradigmaların güçlü ve zayıf yanlarını anlar ve farklı senaryolarda her birinin güçlü yönlerinden yararlanabilir. Şimdi veri biliminde en yaygın kullanılan iki programlama paradigmasını ele alalım: fonksiyonel programlama ve nesne yönelimli programlama.

image1.png

Fonksiyonel Programlama (FP) nedir?

Fonksiyonel programlamada, kod programın nasıl çalışacağını belirleyen fonksiyonlar tanımlanarak ve kullanılarak oluşturulur. Bildirime dayalı programlama paradigmasının bir alt türüdür.

Fonksiyonlar, argümanlar alan ve çıktılar döndüren kod satırlarıdır. Basit bir fonksiyon, örneğin bir sayı listesi alıp yalnızca çift değerleri döndürebilir.

def get_even_numbers(list):
    return [num for num in list if num % 2 == 0]

Python ve R dillerinde fonksiyon yazmanın en iyi uygulamalarını öğrenmek için bu kurslara göz atın.

Fonksiyonel programlama, izlenebilirlik ve öngörülebilirlik açısından öne çıkar. Bunun nedeni, kullanılan fonksiyonların değişmez (immutable) olması, yani değiştirilememesidir. Fonksiyonlar genellikle kodun ayrı bir bölümünde (bazen farklı bir dosyada) tanımlanır ve ihtiyaç duyuldukça kod boyunca kullanılır. Bu özellik, bir kod bölümünde neler olduğunu anlamayı kolaylaştırır; zira fonksiyon her yerde aynı şekilde çalışır ve aynı şekilde çağrılır.

Değişmezlik kavramı, fonksiyonel programlamada veri yapıları için de geçerlidir. FP’de, bir verinin durumunu değiştirirken verinin bir kopyasını oluşturmak tercih edilir. Bu, veriye uygulanan tüm dönüşümlerin sanal bir izini oluşturarak izlenebilirliği artırır ve sorunların tespitini kolaylaştırır. Ancak bunun, her durum değişikliğinin tüm veri kümesinin kopyalanmasını gerektirdiği için genellikle artan bellek kullanımına yol açtığını belirtmek gerekir.

Fonksiyonel programlamada kullanılan fonksiyonların bir alt kümesi de yüksek mertebeden (higher-order) fonksiyonlardır. Bunlar, argüman olarak bir fonksiyon alan veya çıktı olarak bir fonksiyon döndüren fonksiyonlardır. Örneğin, aşağıda gösterildiği gibi bir fonksiyonu birden fazla kez çalıştıran ya da belirli bir senaryoda kullanılacak doğru fonksiyonu seçen bir yüksek mertebeden fonksiyona sahip olabilirsiniz.

def run_function_multiple_times(function, num_times):
	for i in range(num_times):
        function()

Veri analizinde fonksiyonel programlamanın avantajları

Fonksiyonel programlama, kodunuzdaki yinelemeyi azaltmaya yönelik bir yapı sunar. Amacı, tekrarlanan işlemleri üst düzey fiillerle kolayca ifade etmektir.

Örneğin, kodunuzun birkaç farklı yerinde dizeleri belirli bir biçime uyarlamanız gerektiğini düşünün. Bu biçim; boşlukları, noktalama işaretlerini ve sayıları kaldırmayı ve tüm harfleri küçük yapmayı gerektiriyor olsun. Gerekli yerlere birkaç satır kodu kopyalayıp yapıştırabilirsiniz. Ancak bu yineleme kodunuzu şişirir. Bunun yerine, dizelerinizi dönüştürecek bir fonksiyon tanımlayıp uygun yerlerde bu fonksiyonu çağırabilirsiniz. Bu yaklaşım, kodunuzu sadeleştirir; daha okunaklı ve yazması daha hızlı hale getirir.

def clean_string(string):
	exclude = set(string.punctuation + string.digits + " ")
    return ''.join(ch.lower() for ch in string if ch not in exclude)
 
my_string = "Hello, World! 123"
new_string = clean_string(my_string)
print(new_string)

Karmaşık veri boru hatları kurmak için fonksiyonları birleştirme

Fonksiyonel programlama, büyük veri kümeleri veya veri boru hatlarıyla çalışırken kullanışlıdır; çünkü karmaşık veri işleme görevlerini daha küçük, yönetilebilir adımlara ayırmamıza olanak tanır. Çoğunlukla, boru hattındaki her adım için en az bir fonksiyon oluşturulur. Boru hattınızda ihtiyaç duyduğunuz adımların bir akış şemasını yazdıysanız, hangi fonksiyonlara ihtiyaç duyacağınıza dair kabaca bir fikriniz olur. Genel olarak konuşursak, her adım için en az bir fonksiyon gerekir.

Veri Boru Hattı plan diyagramı

Çoğu dilde birçok yerleşik fonksiyon ve indirilebilir ek fonksiyon kütüphaneleri bulunur. R’deki purrr paketi buna harika bir örnektir. Bu sayede, oluşturmanız gereken özel fonksiyon sayısını en aza indirirken hızlıca bir boru hattı kurmak kolaylaşır.

FP ilkeleriyle etkileyici ve okunaklı kod yazma

Fonksiyonel programlama, son derece okunaklı kod yazmayı hedefler. Fonksiyonel programlamayı kullanarak ifade gücü yüksek ve okunaklı kod yazmak için birkaç yönerge vardır:

  • Fonksiyonları küçük ve odaklı tutun: Her fonksiyonun net ve tek bir sorumluluğu olmalı; bu da anlamayı ve bakımı kolaylaştırır. Bunlara saf fonksiyonlar denir.
  • Değişebilir (mutable) durumdan kaçının: Yan etkileri ortadan kaldırmak ve veri akışını daha öngörülebilir kılmak için verinizin durumunu değiştirmekten kaçının. Değişmez veri yapıları kodun açıklığını ve akıl yürütmeyi destekler. Bir yapının durumunu değiştirmek yerine, istenen değişikliklerle yeni bir veri yapısı oluşturun.
  • Yüksek mertebeden fonksiyonlardan yararlanın: Diğer fonksiyonları argüman olarak alan ya da fonksiyon döndüren fonksiyonlara yüksek mertebeden fonksiyonlar denir. Daha ifade gücü yüksek ve özlü kod yazmanızı sağlarlar.
  • Fonksiyon bileşiminden faydalanın: Fonksiyon bileşimi, iki veya daha fazla fonksiyonu birleştirerek yeni bir fonksiyon üretme sürecidir. Bu sayede karmaşık problemleri daha küçük, yönetilebilir parçalara ayırabilirsiniz.

Nesne Yönelimli Programlama (OOP) nedir?

Nesne yönelimli programlama, odağı nesnelere yönelten bir paradigmadır. Nesneler, hem veriyi hem de bu veri üzerinde çalışan yöntemleri (method) içeren, kendi kendine yeten varlıklardır. Fonksiyonel programlamada veriler ve işlemler sıklıkla ayrıyken, burada birbirine yakın tutulur. Bu da veriniz üzerinde neler olup bittiğini takip etmeyi kolaylaştırabilir.

Nesne yönelimli programlamada veri ve yöntemler, sınıfın tanımladığı nesneler içinde örgütlenir. Sınıflar, her nesnenin nasıl davranması gerektiğini belirler ve nesneler bu sınıf içinde tasarlanır. Sınıfların Python’da nasıl çalıştığına dair daha fazla ayrıntı için DataCamp’in rehberine göz atın.

OOP örneği

Örneğin, her hesabın nasıl davranması gerektiğini tanımlayan BankAccount adında bir sınıfınız olabilir. Bu sınıf, bakiye, kimlik numarası ve müşteri adı gibi özniteliklere (attribute) sahip olabilir. Ayrıca hesaptan para çekme, para yatırma ve bakiye görüntüleme gibi bu sınıf içindeki bilgilerin nasıl etkileneceğini tanımlayan yöntemler içerebilir.

Bu sınıf tanımlandıktan sonra, bir kişi için bir nesne, yani bir hesap oluşturabilirsiniz. Nesne oluşturulduktan sonra, her özellik için verileri doldurup hesap üzerinde değişiklik yapmak için yöntemleri kullanabilirsiniz. Aşağıdaki örneğe bakın.

# Define a class representing a bank account
class BankAccount:
    def __init__(self, account_id, customer_name, initial_balance=0.0):
        # Initialize account properties
        self.account_id = account_id
        self.customer_name = customer_name
        self.balance = initial_balance

    # Method to deposit money into the account
    def deposit(self, amount):
        """Deposit money into the account."""
        if amount > 0:
            # Update balance and print deposit information
            self.balance += amount
            print(f"Deposited ${amount}. New balance: ${self.balance}")
        else:
            # Print message for invalid deposit amount
            print("Invalid deposit amount. Please deposit a positive amount.")

    # Method to withdraw money from the account
    def withdraw(self, amount):
        """Withdraw money from the account."""
        if 0 < amount <= self.balance:
            # Update balance and print withdrawal information
            self.balance -= amount
            print(f"Withdrew ${amount}. New balance: ${self.balance}")
        elif amount > self.balance:
            # Print message for insufficient funds
            print("Insufficient funds. Withdrawal canceled.")
        else:
            # Print message for invalid withdrawal amount
            print("Invalid withdrawal amount. Please withdraw a positive amount.")

    # Method to check the current balance of the account
    def check_balance(self):
        """Check the current balance of the account."""
        print(f"Current balance for {self.customer_name}'s account (ID: {self.account_id}): ${self.balance}")

Kapsülleme (Encapsulation)

Veri ve davranışınızı bir sınıfın tanımladığı bir nesne içinde toplama sürecine kapsülleme denir. Bu, kodun geri kalanının nesneyle kolayca etkileşime girmesini sağlayan, iyi tanımlanmış bir yapı oluşturmanıza olanak tanır. Nesne içinde tutulan değişkenlere öznitelik (attribute), nesnenin davranışını belirleyen fonksiyonlara ise yöntem (method) denir.

Kalıtım (Inheritance)

Kod tekrarını azaltmanın bir yolu, yeni sınıflar oluştururken kalıtım kullanmaktır. Bu, üst sınıfların işlevselliğini koruyan yeni sınıflar oluşturma yöntemidir. Banka hesabı örneğimizde, tüm hesaplar için genelleştirilmiş bir ebeveyn sınıf ve vadeli/mevduat gibi farklı hesap türlerine özel alt sınıflarınız olabilir.

Çok Biçimlilik (Polymorphism)

Çok biçimlilik, girdiye bağlı olarak farklı davranışları olan farklı yöntemler için aynı adı kullanmanıza olanak tanıyan bir kavramdır. Genellikle kalıtımla birlikte kullanılır.

Örneğin, alanı hesaplayan bir yöntemi olan Shape adında bir ebeveyn sınıfımız olduğunu varsayalım. İki alt sınıfınız olabilir: Circle ve Square. Her ikisinde de Area adlı bir yöntem bulunurken, bu yöntemin tanımı her şekil için farklıdır. Aşağıdaki kod bloğunda farklı şekiller için alan yöntemlerini karşılaştırın.

# Define the parent class Shape
class Shape:
	# Initialize the attributes for the shape
	def __init__(self, name):
    	    self.name = name
	
	# Define a generic method for calculating the area
	def area(self):
    	    print(f"The area of {self.name} is unknown.")
 
# Define the child class Circle that inherits from Shape
class Circle(Shape):
	# Initialize the attributes for the circle
	def __init__(self, name, radius):
    	# Call the parent class constructor
    	    super().__init__(name)
    	    self.radius = radius
	
	# Override the area method for the circle
	def area(self):
    	# Use the formula pi * r^2
    	    area = 3.14 * self.radius ** 2
    	    print(f"The area of {self.name} is {area}.")
 
# Define the child class Square that inherits from Shape
class Square(Shape):
	# Initialize the attributes for the square
	def __init__(self, name, side):
    	# Call the parent class constructor
    	    super().__init__(name)
    	    self.side = side
	
	# Override the area method for the square
	def area(self):
    	# Use the formula s^2
    	    area = self.side ** 2
    	    print(f"The area of {self.name} is {area}.")

Nesne yönelimli programlamayı öğrenmek için çeşitli kaynaklar mevcuttur; DataCamp’in Python ile OOP kursu, R ile OOP kursu ve Python OOP rehberi bunlardan bazılarıdır.

Veri analizinde Nesne Yönelimli Programlamanın avantajları

Nesne Yönelimli Programlama, veri analizine çeşitli avantajlar sunabilir:

  • Veri kapsülleme: Kapsülleme, veri analizi algoritmalarının ve yapıların iç ayrıntılarını gizleyip yalnızca gerekli arayüzleri sunar. Bu, bileşenlerin temiz biçimde ayrılmasını sağlar ve kodun sürdürülebilirliğini artırır.
  • Kodun yeniden kullanılabilirliği: Nesne yönelimli programlama, diğer sınıfların devralabileceği genel sınıfların oluşturulmasını teşvik eder; bu da kod tekrarını önler ve bakım çabasını azaltır. Bu durum veri analizinde üretkenliği ve verimliliği artırabilir.
  • Tasarım faydaları: Veri ve davranışı temsil eden sınıflar ve nesnelerle gerçek dünya sorunlarının modellenmesi, veri analizinin okunabilirliğini, açıklığını ve modülerliğini güçlendirir.

OOP vs. FP: Doğru Paradigmayı Seçmek

Veri projenizde OOP ya da FP kullanmaya karar verirken dikkate alınacak birçok etken vardır. Bazı veri ekipleri bir paradigmayı diğerine güçlü biçimde tercih edebilir; böyle durumlarda, ekibinizin paradigmasıyla uyumlu olmak tercih edilir. Diğer durumlarda, verinizin veya uygulamalarınızın doğası teraziyi bir yöne doğru kaydırabilir.

Veri görevlerinde OOP ne zaman kullanılmalı

Nesne yönelimli programlama, varlıkları modellemeniz gereken uygulamalar için mükemmeldir. Bir kütüphane yönetim sistemi tasarlamak veya bir yatırım portföyündeki her şirketi puanlayan bir sistem oluşturmak buna iyi örneklerdir. Kapsüllenmiş nesneler oluşturma süreci, bu varlıkların takibini kolaylaştırır. Ayrıca bellek kısıtlı olduğunda OOP tercih edilir; çünkü FP genellikle OOP’ye kıyasla çok daha fazla bellek gerektirir.

Veri görevlerinde FP ne zaman kullanılmalı

Fonksiyonel programlama, paralel işlem gerektiren veri analizi görevlerinde öne çıkar. Değişmezliğe ve yan etkilerden kaçınmaya odaklanması, bir veri kümesi üzerinde işlemleri paralelleştirmeyi kolaylaştırır. Fonksiyonlar, verinin farklı parçaları üzerinde birbirine müdahale etmeden eşzamanlı olarak uygulanabilir.

FP’nin değişmezliğe odaklanması, onu veri dönüşüm boru hatları için de ideal kılar. Fonksiyonlar, durumu değiştirmeden bir dizi dönüşüm oluşturacak şekilde birleştirilebilir; bu da daha okunaklı ve sürdürülebilir koda yol açar.

FP ayrıca genel olarak bilim insanları ve akademisyenler tarafından tercih edilir; çünkü saf matematik odağını daha fazla korur.

OOP ve FP tekniklerini birleştirmek

Bazı pratik uygulamalarda, bir program içinde OOP ve FP birleştirilebilir. Bu, geliştiricilerin her paradigmanın güçlü yanlarından yararlanmasına olanak tanır. OOP’nin kapsüllemesi ve FP’nin ifade gücü, bir makine öğrenmesi boru hattı veya web uygulaması inşa etmek için birlikte kullanılabilir.

Ancak bu şekilde paradigmaları birleştirmek zorluklar doğurabilir ve dikkatle yapılmalıdır. Birden fazla paradigma arasında gidip gelen kodu çözümlemek daha zor olabilir. Bileşenlerin sorunsuz entegrasyonunu sağlamak da güçleşebilir.

Aşağıdaki örneği ele alın. Bu Python kodu, bir sayı listesi üzerinde matematiksel işlemler gerçekleştirmek için OOP ve FP’yi birleştirir. MathOperation sınıfı, FP kullanılarak oluşturulan belirli bir matematiksel işlem için bir kap görevi görür. filter_odd_numbers ve square_operation fonksiyonları sırasıyla tek sayıları süzmek ve bir sayının karesini almak için yardımcı olur. Bu bileşenleri birlikte kullanarak kod, verilen bir listeden tek sayıları verimli biçimde süzer ve ardından bunların karesini alır; bu da OOP ve FP’nin veri analizi görevlerini basitleştirmek ve düzenlemek üzere nasıl birlikte çalışabileceğini gösterir.

# Object-Oriented Programming (OOP) - Class Definition
class MathOperation:
	def __init__(self, operation):
    	    self.operation = operation
 
	def apply_operation(self, number):
    	    return self.operation(number)
 
# Functional Programming (FP) - Higher-Order Functions
def filter_odd_numbers(numbers):
    return list(filter(lambda x: x % 2 != 0, numbers))
 
def square_operation(number):
    return number ** 2
 
def process_numbers(numbers, operation_function):
    return list(map(operation_function, numbers))
 
# Example Usage:
# Define an OOP class representing a mathematical operation (square in this case)
square_operation_instance = MathOperation(square_operation)
 
# Sample list of numbers
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
 
# Use FP to filter odd numbers
odd_numbers = filter_odd_numbers(numbers)
 
# Use OOP to apply the square operation to odd numbers
result = process_numbers(odd_numbers, square_operation_instance.apply_operation)
 
# Display the result
print(result)

OOP ve FP’yi birlikte kullanmaya başka bir örnek için bu github deposuna göz atın.

Sonuç

Hangi programlama paradigmasının en iyi seçim olduğuna dair kesin bir yanıt yoktur; bu, bağlama, amaca, dile ve programcının tercihine bağlıdır. Ancak, durumunuza en uygun paradigmayı belirlemek için izleyebileceğiniz bazı genel yönergeler vardır.

Birden çok varlık ve etkileşime sahip karmaşık sistemleri modellemeniz ve veri ile davranışı yeniden kullanılabilir bileşenler içinde kapsüllemeniz gerektiğinde OOP kullanın. Basit girdiler ve çıktılarla saf hesaplamalar yapmanız ve yan etkilerden ya da durum değişikliklerinden kaçınmanız gerektiğinde FP kullanın. Her iki paradigmanın güçlü yanlarından yararlanmanız ve dilin özelliklerine ve sınırlamalarına uyum sağlamanız gerektiğinde karma bir yaklaşım kullanın.

En iyi programcılar bir paradigmaya ya da diğerine bağlı kalmaz; hedeflerine ulaşmak için gerektiğinde paradigmalar arasında geçiş yapabilirler. Programlama Paradigmalarına Giriş kursuyla farklı paradigmaları derinlemesine keşfedin. Bir veri projesi üzerinde çalışmayı deneyin ve hem OOP hem FP kullanarak projeyi çözün. Her paradigmayı uygulamak, projeniz için hangisinin en iyi işleyeceğine dair güçlü bir sezgi kazandıracaktır.

Amberle McKee

Yazar

Amberle McKee

Biyolojik araştırma ortamında verilerle çalışma konusunda 13 yıllık deneyime sahip bir doktora sahibiyim. Python, MATLAB ve R dahil olmak üzere birkaç programlama dilinde yazılım geliştiriyorum. Öğrenme sevgimi dünyayla paylaşma konusunda tutkuluyum.

Konular

İlgili