Kategori arşivi: Yazılım

Flutter’da Provider ile BottomNavigationBar Mimarisi

Flutter’da bir şeyi yapmanın birçok yolu var. Bu bazen işlerimizi kolaylaştırsa da bazen karmaşıklığa neden olabiliyor.

En çok kafa karışıklığına yol açan bir tanesi de BottomNavigationBar kullanımı. Hatta bu zamana kadar, daha işlevsel olduğunu düşündüğüm için Cupertino kütüphanesinden CupertinoTabBar kullanıyordum. Artık BottomNavigationBar’ın nasıl ilginç olabileceğini keşfedeceğiz.

İşi ilginç yapan kısım şu; sayfalar arası dolaşırken gezinme geçmişini kaybetmeyeceğiz. Şuradaki gibi: 

Bu yazı başlangıç düzeyi için uygun değildir. Öncesinde şu konuları bilmeniz gerekir:

İlk başta da dediğim gibi, Flutter’da bir şeyi yapabiliyor olmanın bir çok alternatifi vardır. Bu konuyla alakalı daha iyi bir yol biliyorsanız lütfen benimle paylaşın.

Gün sonunda proje dosyalarımız şu şekilde olacak:

Logic

İlk başta tüm navigasyon ekranlarımızın tüm bilgilerini saklayabilecek bir Screen modeli oluşturmamız gerekiyor. screen.dart isimli bu amaçla oluşturuyoruz.

import 'package:flutter/material.dart';

/// [Screen] Ekran oluşturrurken gereken tüm bilgileri tutacak
class Screen {
  /// BottomNavigationBar'da gösterilecek olan başlık
  final String title;

  /// BottomNavigationBar'da gösterilecek olan ikon
  final IconData icon;

  /// Ekranda akacak olan WidgetTree tutucusu
  final Widget child;

  /// İlgili ekranın devamı için isimlendirilmiş rota oluşturucu [Navigator]
  final RouteFactory onGenerateRoute;

  /// İlk rotanın [onGenerateRoute] içinde implemente edilmesi gerekiyor
  final String initialRoute;

  /// İlgili ekranın [NavigatorState]'i için key gerekiyor
  final GlobalKey<NavigatorState> navigatorState;

  /// ISTEGE BAGLI: İlgili ekranın en üstüne dönerken hareket animasyonunu değiştirmek için gerekli
  final ScrollController scrollController;

  Screen({
    @required this.title,
    @required this.icon,
    @required this.child,
    @required this.onGenerateRoute,
    @required this.initialRoute,
    @required this.navigatorState,
    this.scrollController,
  });
}

Screen modeli sayesinde navigasyon sekmeleri ile çalışırken işimizi çok kolaylaştırmış olacağız. Buradaki child nesnesi sayesinde Scaffold inşa ediyorken, navigatorState ile çoklu contextler ile çalışabileceğiz.

Screen implementasyonu

navigation_provider.dart isimli bir dosya oluşturup aşağıdaki kodları takip edelim.

Her navigasyon ekranının index ile referanslandırılması gereklidir. Bu sebeple class üstünde ekran indexlerimiz için aşağıdaki sabitleri tanımlayacağız. Bunları enum yapısı veya class içerisinde static const olarak da tanımlayabilirdik ancak benim aklıma bu yattı.

const FIRST_SCREEN = 0;
const SECOND_SCREEN = 1;
const THIRD_SCREEN = 2;
const FOURTH_SCREEN = 3;

Şimdi ise Screen sınıfından türeteceğimiz ekranlarımızı oluşturalım. Oluşturacağımız ekranlardan, FirstScreen, SecondScreen, ThirdScreen ve FourthScreen navigasyonumuzun sekmeleri olacakken PushedScreen ve SimpleScreen ise navigatorState‘i test etmemize ve örneğimizi pekiştirmeye yarayacak.

first_screen.dart

class FirstScreen extends StatelessWidget {
  static const route = '/first';

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('First Screen')),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            RaisedButton(
              onPressed: () {
                Navigator.of(
                  context,
                  // BottomNavigationBar ile diğer ekrana git
                  rootNavigator: false,
                ).pushNamed(PushedScreen.route);
              },
              child: Text('BottomNavigationBar ile sayfaya git'),
            ),
            RaisedButton(
              onPressed: () {
                Navigator.of(
                  context,
                  // BottomNavigationBar olmadan diğer ekrana git (Kök dizin)
                  rootNavigator: true,
                ).pushNamed(PushedScreen.route);
              },
              child: Text('BottomNavigationBar olmadan sayfaya git'),
            ),
          ],
        ),
      ),
    );
  }
}

second_screen.dart

class SecondScreen extends StatelessWidget {
  static const route = '/second';

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Second Screen')),
      body: ListView.builder(
        controller: NavigationProvider.of(context)
            .screens[SECOND_SCREEN]
            .scrollController,
        itemBuilder: (context, index) {
          return ListTile(
            title: Text('Item: $index'),
          );
        },
      ),
    );
  }
}

third_screen.dart

class ThirdScreen extends StatelessWidget {
  static const route = '/third';

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Third Screen')),
      body: Center(
        child: FlutterLogo(
          size: 128,
        ),
      ),
    );
  }
}

fourth_screen.dart

class FourthScreen extends StatelessWidget {
  static const route = '/extra';

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Extra Screen')),
      body: Center(
        child: RaisedButton(
          onPressed: () {
            // Push with bottom navigation visible
            Navigator.of(
              context,
              rootNavigator: true,
            ).pushNamed(SimpleScreen.route);
          },
          child: Text('Go to Simple Page'),
        ),
      ),
    );
  }
}

pushed_screen.dart

class PushedScreen extends StatelessWidget {
  static const route = '/first/pushed';

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Pushed Screen')),
      body: Center(
        child: Text('Hello world!'),
      ),
    );
  }
}

simple_screen.dart

class SimpleScreen extends StatelessWidget {
  static const route = '/extra/simple';

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Simple Screen"),
      ),
    );
  }
}

Yukarıda belirttiğim gibi, PushedScreen ve SimpleScreen‘ın kendi sabiti yoktur çünkü BottomNavigationBar‘da gösterilmeyecek.

Navigation Provider

İşleri ilginçleştirmeye başlıyoruz. Bu işi yaparken, state management’i Provider paketi ile çok kolay hale getireceğiz.

Provider.of<NavigationProvider>(context, listen: false) (varsayılan olarak listen true değeri alır) ile tüm işi hızlıca halledeceğiz. navigation_provider.dart dosyasına geri dönüp devam edelim.

NavigationProvider

class NavigationProvider extends ChangeNotifier {
  /// [NavigationProvider] edinmek için shortcode
  static NavigationProvider of(BuildContext context) =>
      Provider.of<NavigationProvider>(context, listen: false);

  // Açılış sayfası
  int _currentScreenIndex = FIRST_SCREEN;
  int get currentTabIndex => _currentScreenIndex;

  Route<dynamic> onGenerateRoute(RouteSettings settings) {
    print('Oluşturulan rota: ${settings.name}');
    switch (settings.name) {
      case PushedScreen.route:
        return MaterialPageRoute(builder: (_) => PushedScreen());
      case SimpleScreen.route:
        return MaterialPageRoute(builder: (_) => SimpleScreen());
      default:
        return MaterialPageRoute(builder: (_) => Root());
    }
  }

  final Map<int, Screen> _screens = {
    FIRST_SCREEN: Screen(
      title: 'First',
      icon: Icons.home,
      child: FirstScreen(),
      initialRoute: FirstScreen.route,
      navigatorState: GlobalKey<NavigatorState>(),
      onGenerateRoute: (settings) {
        print('Oluşturulan rota: ${settings.name}');
        switch (settings.name) {
          case PushedScreen.route:
            return MaterialPageRoute(builder: (_) => PushedScreen());
          default:
            return MaterialPageRoute(builder: (_) => FirstScreen());
        }
      },
      scrollController: ScrollController(),
    ),
    SECOND_SCREEN: Screen(
      title: 'Second',
      icon: Icons.search,
      child: SecondScreen(),
      initialRoute: SecondScreen.route,
      navigatorState: GlobalKey<NavigatorState>(),
      onGenerateRoute: (settings) {
        print('Oluşturulan route: ${settings.name}');
        switch (settings.name) {
          default:
            return MaterialPageRoute(builder: (_) => SecondScreen());
        }
      },
      scrollController: ScrollController(),
    ),
    THIRD_SCREEN: Screen(
      title: 'Third',
      icon: Icons.favorite,
      child: ThirdScreen(),
      initialRoute: ThirdScreen.route,
      navigatorState: GlobalKey<NavigatorState>(),
      onGenerateRoute: (settings) {
        print('Oluşturulan route: ${settings.name}');
        switch (settings.name) {
          default:
            return MaterialPageRoute(builder: (_) => ThirdScreen());
        }
      },
      scrollController: ScrollController(),
    ),
    FOURTH_SCREEN: Screen(
      title: 'Fourth',
      icon: Icons.message,
      child: FourthScreen(),
      initialRoute: FourthScreen.route,
      navigatorState: GlobalKey<NavigatorState>(),
      onGenerateRoute: (settings) {
        print('Oluşturulan route: ${settings.name}');
        switch (settings.name) {
          case SimpleScreen.route:
            return MaterialPageRoute(builder: (_) => SimpleScreen());
          default:
            return MaterialPageRoute(builder: (_) => FourthScreen());
        }
      },
      scrollController: ScrollController(),
    ),
  };

  List<Screen> get screens => _screens.values.toList();

  Screen get currentScreen => _screens[_currentScreenIndex];

//...

İşler karmaşıklaşmadan önce yukarıdaki kod parçacığını anlamaya çalışalım.

NavigationProvider sınıfımızın üstünde, sayfaların gerekli index değerlerini oluşturabilmemiz için değişkenler tanımlamıştık. Açılış sayfamızı tanımlamak ve mevcut ekran değişikliklerini uygulayabilmek adına, _currentScreenIndex isimli bir değişken oluşturduk ve ilk değer olarak FIRST_SCREEN (anasayfa) verdik. Gizli değişken olduğu için getter kullandık.

NavigationProvider’ın içinde saklaması gereken bir diğer şey ise tüm ekranlarımız. Ekranlarımızı saklamanın en iyi yolu Map<int, Screen> olduğunu varsayarak Map türünde _screen isimli bir değişken oluşturduk.

Daha da açıklamak gerekirse, tam da burada Screen isimli model sınıfımızı kullanmış olduk. Yani aslında, Map<int, Screen>‘ın int olan kısmı en üstte tanımladığımız sayfa indexlerini alıyorken, Screen kısmı için, yukarıda oluşturduğumuz ekran sayfalarını Screen modelinden türetmiş oluyoruz.

Peki 2. satırda bulunan [NavigationProvider] edinmek için shortcode dediğimiz kısım ne işe yarıyor?

Yukarıda da bahsettiğim gibi, Provider erişimine ihtiyaç duyduğumuz zaman aşağıdaki tanımlamayı kullanıyoruz:

Provider.of<NavigationProvider>(context, listen: false);

Ama artık biz, 3. satırdaki kod sayesinde artık işleri daha da kolaylaştırarak, tıpkı InheritedWidget‘larda olduğu gibi şu şekilde çağırabiliriz:

NavigationProvider.of(context)

Devam edelim. Navigasyon sekmelerimiz arasında geçiş yapabilmemiz için bir metot tanımlamamız gerekiyor. Bu metodun aldığı tab değeri ile currentTabIndex değeri aynı ise, (eğer ekranın en üstünde değilse) ekranın en üst kısmına dönme işlemlerini yaparken, eğer farklı ise ekranlar arası geçiş işlemlerini yapıp dinleyicimiz olan Provider’ı haberdar edeceğiz.

   void setTab(int tab) {
    if (tab == currentTabIndex) {
      // TODO: Ekranın en üstüne dönme işlemleri: _scrollToTopOfPage();
    } else {
      _currentScreenIndex = tab;
      notifyListeners();
    }
  }
//...

Root Widget

Bildiğiniz gibi isimlendirilmiş rotalarda kök dizin olarak “/” belirtiriz. Kök dizini, Consumer ile sarmalayarak değişiklikleri dinleyebiliriz.

root.dart

class Root extends StatelessWidget {
  static const route = '/';

  @override
  Widget build(BuildContext context) {
    return Consumer<NavigationProvider>(
      builder: (context, provider, child) {
        // Tanımladığımız screen'lardan BottomNavigationBar oluşturma
        final bottomNavigationBarItems = provider.screens
            .map(
              (screen) => BottomNavigationBarItem(
                icon: Icon(screen.icon),
                label: screen.title,
              ),
            )
            .toList();

        // Her ekran için [Navigator] örneğini başlatma
        final screens = provider.screens
            .map(
              (screen) => Navigator(
                key: screen.navigatorState,
                onGenerateRoute: screen.onGenerateRoute,
              ),
            )
            .toList();

        return WillPopScope(
          onWillPop: () async => provider.onWillPop(context),
          child: Scaffold(
            body: IndexedStack(
              children: screens,
              index: provider.currentTabIndex,
            ),
            bottomNavigationBar: BottomNavigationBar(
              type: BottomNavigationBarType.fixed,
              items: bottomNavigationBarItems,
              currentIndex: provider.currentTabIndex,
              onTap: provider.setTab,
            ),
          ),
        );
      },
    );
  }
}

Burada iki önemli olay var: WillPopScope ve IndexedStack.

IndexedStack, widget listesi alır (bizim için screens) ve tüm durumlarını saklar. Bizim durumumuzda, index değişikliğine göre, hangi widget’ın (ekranın) görünür olacağını ayarlar. Örnek olarak, birinci sayfanın belirli bir yerinde iken ikinci sayfaya geçiş yaptığımızı varsayalım. Tekrardan birinci sayfaya dönüş yaptığımızda birinci sayfanın kaldığımız yerinden devam ederiz.

WillPopScope, Android’in geri dönme butonunu yakalamaya yarar. Sistemin, geri düğmesini kullanıp kullanılmayacağını ve bu işi özelleştirmek istiyorsak kullanırız.

WillPopScope ile onWillPop olma durumunu tanımlamış olduk. Şimdi ise tekrardan NavigationProvider sınıfımıza dönerek onwillPop olduğu durumda neler yapacağımızı ele alalım.

navigator_provider.dart dosyasına şu metodu ekleyelim:

 Future<bool> onWillPop(BuildContext context) async {
    final currentNavigatorState = currentScreen.navigatorState.currentState;

    if (currentNavigatorState.canPop()) {
      currentNavigatorState.pop();
      return false;
    } else {
      if (currentTabIndex != FIRST_SCREEN) {
        setTab(FIRST_SCREEN);
        notifyListeners();
        return false;
      } else {
        return false;
        );
      }
    }
  }
//...

İlk başta canPop() ile bir geri dönüş rotasının olup olmadığını kontrol ediyoruz. Normal bir şekilde bulunduğumuz sayfaya push işlemi gerçekleştirdiysek bu metot geriye true değeri döndürecektir. Biz uygulamamızda, Root widget’a dönmek ve back butonuna basıldığında uygulamanın kapanmasını önlemek adına false döndürüyoruz.

İsimlendirilmiş Rotalar

Rotalar, ilgili rota içerisinde ele alınır. Ne zaman isimlendirilmiş rota kullanırsak Navigator en yakın durumu bulur ve bu aşamada onGenerateRoute(RouteSettings settings) metodunu çağırır.

Biz bu işlemi zaten yukarıda yaptık. Dahili (ana) Navigator için rota üreticisini yukarıdaki _screens değişkeni içerisinde bulabilirsiniz. Yine de aşağıdaki kod parçacığı ile durumu inceleyelim.

 .... 
 Route<dynamic> onGenerateRoute(RouteSettings settings) {
    print('Oluşturulan rota: ${settings.name}');
    switch (settings.name) {
      case PushedScreen.route:
        return MaterialPageRoute(builder: (_) => PushedScreen());
      case SimpleScreen.route:
        return MaterialPageRoute(builder: (_) => SimpleScreen());
      default:
        return MaterialPageRoute(builder: (_) => Root());
    }
  }
....

Bu alan bizim kök dizinden başlayarak isimlendirilmiş rotaları oluşturmamıza olanak sağlıyor.

Biz, oluşturduğumuz ekranların herhangi birinden başka bir sayfaya push işlemi gerçekleştirirken, Navigator.push() ile durumu ele alabiliriz. Ancak bu durum gidilen ekranda BottomNavigationBar‘ın görünüyor olmasına sebebiyet verecektir.

Eğer biz gidilen ekranda BottomNavigationBar‘ın görünüyor olmasını istemiyorsak, bunun için kök Navigator üzerinden push işlemi yapmamız gerekiyor. Yani, onGenerateRoute(RouteSettings settings) metodunda tanımlamamız gerekiyor. Bu işlemin ardından ilgili ekrana gitmek istediğimizde rootNavigator: true değeri atayarak BottomNavigationBar‘ın görünmesini engellemiş oluruz.

// BottomNavigationBar olmadan push işlemi
Navigator.of(context, rootNavigator: true).pushNamed(PushedScreen.route);

Main Widget (main.dart)

Şimdi ise main.dart dosyamızı düzenleyelim. Bildiğiniz gibi Flutter uygulamaları ilk buradan başlar. İşte tam da burada çağıracağımız MaterialApp() widget’ını, MultiProvider ile sarmalayarak tüm uygulamamızı NavigationProvider‘dan haberdar etmiş oluruz.

void main() => runApp(App());

class App extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MultiProvider(
      providers: [
        ChangeNotifierProvider(create: (_) => NavigationProvider()),
      ],
      child: Builder(
        builder: (context) {
          return MaterialApp(
            onGenerateRoute: NavigationProvider.of(context).onGenerateRoute,
          );
        },
      ),
    );
  }
}

Sekmeler Arası Geçiş Yapma

Sekmelerin push işlemini zaten yukarıda ele almıştık. Peki ya sekmeler arasında geçiş yapmamız gerekirse? Bu esnada mevcut state’i korumak zor olacaktır. Örneğin, SECOND_SCREEN ekranından FIRST_SCREEN ekranına dönmeniz gerektiğini varsayalım. Bu durum için Navigator ile bir push gerçekleştirirseniz hata alırsınız. Peki ya ne yapmamız gerekiyor?

Yapmamız gereken basit bir şey var; İlgili ekran için NavigatonProvider içindeki setTab() metodunu çağırın. Yani tıpkı yukarıda yaptığımız gibi of() kullanarak sekmeler arası geçiş yapabiliriz.

NavigationProvider.of(context).setTab(FIRST_SCREEN);

Ekranın En Üstüne Dönme İşlemi

Bir çok mobil uygulamada karşılaştığınız bir durumu ele alacağız. BottomNavigationBar’da bulunan sekmelerden bir tanesinin ekranında aşağılara indiniz diyelim. Tekrardan ilgili sekmenin BottomNavigationBar’daki ikonuna tıklarsanız sizi sayfanın en üstüne götürür. Örnek olarak Instagram’a veya Twitter’a girip deneyimleyebilirsiniz.

Biz durumu ele alabilmek için zaten Screen modeli oluştururken screenController adında bir değişken tanımlamıştık.

Ayrıca bu işlemi test edebilmek adına SECOND_SCREEN içinde sınırsız elemanlı bir liste oluşturduk. Bu sayfaya giderek kaydırma işleminin çalışıp çalışmadığını test edebiliriz.

Şimdi ise tekrardan navigation_provider.dart dosyasına giderek ilgili metodu yazabiliriz.

void _scrollToTopOfPage() {
    if (currentScreen.scrollController != null &&
        currentScreen.scrollController.hasClients) {
      currentScreen.scrollController.animateTo(
        0.0,
        duration: const Duration(seconds: 1),
        curve: Curves.easeInOut,
      );
    }
  }

Yukarıda görüldüğü üzere ilk başta ele aldığımız ekranın bir screenController nesnesine sahip olmadığını kontrol ettik. Ayrıca aynı kontrol içerisinde ekranın pozisyonunun değişip değişmediğini kontrol ettik. hasClients() eğer ekranın pozisyonu değiştiyse true döndürür.

Şimdi tekrardan aynı sınıf içerisinde bulunan setTab() metoduna dönerek yukarıda tanımladığımız _scrollToTopOfPage() metodu çağıralım.

void setTab(int tab) {
    if (tab == currentTabIndex) {
      _scrollToTopOfPage(); // <= Eklenen yeni satır
    } else {
      _currentScreenIndex = tab;
      notifyListeners();
    }
  }

Artık ilgili ekranın en üstüne dönmek istediğimizde BottomNavigationBar elemanına tıklamamız yeterli.

Uygulamadan Çıkış Yapılıyor Uyarısı

Neredeyse tüm olasıkları ele aldık. Şimdi ise uygulamanın kök rotasında iken sistemin geri tuşuna basılırsa ekranda gösterilecek bir AlertDialog tanımlayalım.

exit_dialog.dart

class ExitAlertDialog extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return AlertDialog(
      title: Text('Çkış'),
      content: Text("Çıkış yapmak istediğinizden emin misiniz?"),
      actions: <Widget>[
        FlatButton(
          onPressed: () {
            Navigator.of(context).pop(false);
          },
          child: Text(
            'İptal',
            style: Theme.of(context).textTheme.button.copyWith(
                  fontWeight: FontWeight.normal,
                ),
          ),
        ),
        FlatButton(
          onPressed: () {
            Navigator.of(context).pop(true);
          },
          child: Text('Çıkış'),
        ),
      ],
    );
  }
}

Yukarıdaki FlatButton’a tıklandığında pop() çağırılacak. Ancak bu sıradan bir pop() değil. Geriye bir değer döndürüyor. Eğer kullanıcı Çıkış‘a basarsa true, İptal‘e basarsa false değeri dönecek. Biz de buradan aldığımız değer ile işlemleri gerçekleştirmeye devam edeceğiz.

Bu işlemi de tamamladıktan sonra NavigationProvider’a dönerek onWillPop callback’ini tekrardan ele almamız gerekiyor.

Future<bool> onWillPop(BuildContext context) async {
    final currentNavigatorState = currentScreen.navigatorState.currentState;

    if (currentNavigatorState.canPop()) {
      currentNavigatorState.pop();
      return false;
    } else {
      if (currentTabIndex != FIRST_SCREEN) {
        setTab(FIRST_SCREEN);
        notifyListeners();
        return false;
      } else {
        return await showDialog(
          context: context,
          builder: (context) => ExitAlertDialog(),
        );
      }
    }
  }

Her şeyimiz hazır. Metotlarımızı çağırıp çağırmadığımızı kontrol edebilmemiz adına Root widget’ımıza dönelim. Orada WillPopScope içerisinde onWillPop durumunu ele alıp almadığımıza bakalım.

return WillPopScope(
          onWillPop: () async => provider.onWillPop(context),
          child: Scaffold(
          //...

Root widget içerisinde return ettiğimiz WillPopScope widget’ın, onWillPop durumuna, NavigationProvider sınıfında tanımladığımız onWillPop metodunu geçtik.

İşlem tamam. onWillPop durumu kontrol edip uygulamanın kök dizininde olduğunu belirlediğinde, burada gerçekleşen olası geri tuşuna basılmasında bir AlertDialog oluşacak ve AlertDialog’dan gelen bool verisi iie işlem yapacak.

Test Edelim

Uygulamamız istediğimiz gibi çalışıyor.

Kapanış

Artık uygulamalarımızın ekran state durumlarını kaybetmeden ekranlar arası gezinme yapabiliyoruz. Üstelik bunu yaparken dilersek kök dizinden oluşturulmuş isimlendirilmiş rotalar kullanarak BottomNavigationBar’dan kurtulabiliyoruz. Bu mimarinin üzerine uygulamamızı inşa etmeye devam ederek profesyonele yakın uygulamalar geliştirebiliriz.

Kaynak kodlarına buraya tıklayarak erişebilirsiniz.

İlk başta da belirttiğim gibi, bu uygulama daha önceden bilmediğiniz terimleri içerebilir. Bu yazıda, ilgili terimlerin nasıl çalıştığından ziyade başlıktaki probleme odaklanıldı. Lütfen bilmediğiniz terimler için resmi Flutter dökümantasyonunu inceleyin. Provider paketi ile ilgili bilgilere ulaşmak için buradaki bağlantıyı inceleyin.

Titanic veri seti ile makine öğrenmesi uygulaması

GitHub üzerinden tam haline bakabilirsiniz: https://github.com/berkanaslan/TitanicDatasetClassificationTutorial

Merhaba, makine öğrenmesinin popüler veri setlerinden birisi olan “Titanic: Machine Learning from Disaster” ile karar ağaçlarını kullanarak bir Python projesi geliştireceğim. Amaç kimin hayatta kaldığını tahmin etmek olacak.

BAŞLANGIÇ

Veri setini yerel bilgisayarıma indirdikten sonra ilk başta Pandas kütüphenesini, ardından veri setlerini uygulamama gömdüm.

#Küpüthaneler
import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split 
 
egitim = pd.read_csv('train.csv', header = 0)

Ardından, yüklediğim eğitim veri setinin özniteliklerini inceledim.

egitim.info()

Çıktı:

 #   Column       Non-Null Count  Dtype  
---  ------       --------------  -----  
 0   PassengerId  891 non-null    int64  
 1   Survived     891 non-null    int64  
 2   Pclass       891 non-null    int64  
 3   Name         891 non-null    object 
 4   Sex          891 non-null    object 
 5   Age          714 non-null    float64
 6   SibSp        891 non-null    int64  
 7   Parch        891 non-null    int64  
 8   Ticket       891 non-null    object 
 9   Fare         891 non-null    float64
 10  Cabin        204 non-null    object 
 11  Embarked     889 non-null    object 
dtypes: float64(2), int64(5), object(5)

Yukardaki tablo sayesinde toplam 891 satır verinin olduğunu ve “Cabin”, “Age” ve “Embarked” adlı özniteliklerde eksik verilerin olduğunu öğrendim. Ayrıca, “Dtype” adlı sütun sayesinde veri tiplerini de öğrendim.

VERİ SETİ DÜZENLEME

Amacımız kimin hayatta kalacağını tahmin etmek olduğu için, pek de işimize yaramayacak olan öznitelikleri modelimizden çıkarabiliriz. Veriyi, hızlı bir inceleme sonucunda, çokça eksik verisi olan Cabin, makine öğrenmesine faydası olmayacak olan Name ve Ticket başlıklı özniteliklerini veri setimden çıkarıyorum.

cikarilacaklar = ['Name','Ticket','Cabin']
egitim = egitim.drop(cikarilacaklar, axis = 1)


Bir sonraki veri düzenleme işlemime, egitim.info() metodu kullanımı sonucunda eksik verilerin olduğunu öğrendiğim veri setinden eksik verilerin bulunduğu satırların silinmesini sağlayacağım.

egitim = egitim.dropna()


Bu işlemin ardından tekrardan egitim.info() metodunu kullanarak veri hakkında bilgi ediniyorum.

Çıktı:

Data columns (total 9 columns):
 #   Column       Non-Null Count  Dtype  
---  ------       --------------  -----  
 0   PassengerId  712 non-null    int64  
 1   Survived     712 non-null    int64  
 2   Pclass       712 non-null    int64  
 3   Sex          712 non-null    object 
 4   Age          712 non-null    float64
 5   SibSp        712 non-null    int64  
 6   Parch        712 non-null    int64  
 7   Fare         712 non-null    float64
 8   Embarked     712 non-null    object 
dtypes: float64(2), int64(5), object(2)

Yukarıda görüldüğü üzere, toplam satır sayım başlangınçta 891 iken artık 712 olarak uygulamamda duruyor ve artık 9 öznitelikten oluşuyor.

VERİ DÖNÜŞTÜRME İŞLEMLERİ

Verilerimizi, makine öğrenmesinin anlayabileceği bir dile yani 0 ve 1’lerden oluşan bir yapıya dönüştürmemiz gerekiyor. Bu amaçla, öğrenme algoritması üzerinde büyük bir öneme sahip olacağını düşündüğüm 3 özniteliği; Pclass, (Yolcu sınıfı) Sex, (Cinsiyet) ve Embarked (Biniş noktası) başlıklı öznitelikleri kullanacağım.

Veri dönüştürme işlemleri için Pandas kütüphanesinin get_dummies metodunu kullanacağım. Bu metot sayesinde kategorik verileri, yer tutucu değişkenler ile değiştirmiş olacağım. Bu işlemi, her bir satıra uygulayabilmek adına da bir for döngüsünden yararlanacağım.

yolcular = []
sutunlar = ['Pclass','Sex','Embarked']
for sutun in sutunlar:
    yolcular.append(pd.get_dummies(egitim[sutun]))

Yukarıdaki kod sayesinde yolcular adlı bir liste içerisinde, Pclass, Sex ve Embarked adlı özniteliklerimizin ayrı dönüşümlerini tamamladık. Dönüşüm sonucunda öznitelikler, içerisinde bulundurduğu kategorik sınıfların, sütun başlıklarından oluşmaya başladı. Örnek vermek gerekirse, x satırı y sütunu özelliği taşıyor ise eşleşen hücre değeri 1, diğerleri 0 değeri taşıyacak hale geldi. Buna One Hot Encoding dönüşümü de diyebiliriz. Artık kodlarımı çalıştırıp elde ettiğim DataFrame’leri inceleyebilirim.

Yukarıdaki görüntüde soldan sağa, Pclass, Sex ve Embarked özniteliklerinin dönüşüm geçirmiş DataFrame’lerini başarıyla görüntüleyebildim. Her bir öznitelik ayrı bir DataFrame olduğu için, bu DataFrame’leri birleştirmem gerekiyor.

Tek bir DataFrame elde etmek için yine Pandas kütüphanesinin bir metotu olan concat metodunu kullanıyorum. Metodu kullandıktan sonra sonucu görüntüleyebilmek adına değişkenimi konsola yazdıracağım. Burada belirttiğim axis, “x’inci index değerinden itibaren” anlamı taşımakta.

yolcular = pd.concat(yolcular, axis = 1)
print(yolcular)

Çıktı:

     1  2  3  female  male  C  Q  S
0    0  0  1       0     1  0  0  1
1    1  0  0       1     0  1  0  0
2    0  0  1       1     0  0  0  1
3    1  0  0       1     0  0  0  1
4    0  0  1       0     1  0  0  1
..  .. .. ..     ...   ... .. .. ..
885  0  0  1       1     0  0  1  0
886  0  1  0       0     1  0  0  1
887  1  0  0       1     0  0  0  1
889  1  0  0       0     1  1  0  0
890  0  0  1       0     1  0  1  0

Yukarıdaki veri kesitinden de anlaşılacağı üzere, female ve female adında iki özniteliğimiz bulunmakta. Birisi 1 iken diğeri 0 değeri alıyor. İki öznitelikten birisini silip bir tanesi ile devam edebiliriz. Örneğin, 4. satır için female değeri 0 iken male değeri 1 olarak gözükmekte. Ben eğer female sütununu silersem ilgili satırın male değerine baktığımda zaten 1 mi yoksa 0 mı olduğunu anlayabilirim.

yolcular = yolcular.drop(['female'], axis = 1)

Bu işlem sonrasında artık dönüştürdüğümüz öznitelikler ile dönüşüm uygulamadığımız öznitelikleri birleştirebiliriz. Ancak bu birleştirme işleminde kullanacağımız eğitim adlı değişken de bulunduğu için, içerisinden dönüşüm uyguladığımız öznitelikeri çıkaracağız. Yine bu işlemler için tekrardan concat ve drop metotlarını kullanacağım.

egitim = pd.concat((egitim,yolcular), axis = 1)
egitim = egitim.drop(['Pclass','Sex','Embarked'], axis = 1)

MAKİNE ÖĞRENMESİ İŞLEMLERİ

Artık makine öğrenmesi işlemlerine geçebilirim. Bu bölümde,
X: Öznitelikler
Y: Survived çıktıları değerlerini taşıyacak.

X = egitim.values
Y = egitim['Survived'].values

X, içerisinde 1 sütun başlıklı olarak hala Survived özniteliğini taşıyor. Bunu girdi değişkenlerinden çıkarmam gerekiyor.

X = np.delete(X,1,axis=1)

Artık veri setimi, scikit kütüphanesinden kullanacağım train_test_split ile %70 eğitim – %30 test olarak parçalayabilirim.

X_train, X_test, y_train, y_test=train_test_split(X,Y,test_size=0.3, random_state=0)

MAKİNE ÖĞRENMESİ ALGORİTMASI KULLANIMI VE MODELİN TESTİ

Basit bir makine öğrenmesi algoritması olan Decision Tree algoritmasını kullanacağım.

from sklearn import tree
siniflama = tree.DecisionTreeClassifier(max_depth=5)
siniflama.fit(X_train,y_train)
skor = siniflama.score(X_test,y_test)

Yukardaki kod bloğu sayesinde,  train verilerimizi fit() metodu sayesinde eğittim. Ardından, önceden parçalama işlemi yaptığımız test verilerimiz ile başarı oranımızı skor adlı değişkenin içine attım. Son olarak skoru konsola yazdıracağım.

print("Başarı: ",skor)

Çıktı:

Başarı:  0.7476635514018691

Başarılı bir modelleme sonucunda 0.747 gibi bir skor elde ettim. Modelin %74 doğrulukla çalışıyor olmasını kabul edilebilir bir değer olarak yorumlayabiliriz.

TAHMİN VE DOĞRULUK TABLOSU SKORU

Buraya kadar özetle, verilerin düzenlenmesi, öğrenme algoritmasının kullanılması ve test edilmesi adımlarını tamamladım. Şimdi ise eğitim veri setimizi kullanarak kendi Survived tahminlerimi elde edeceğim. Ardından gerçek Survived değerleri ile kıyaslayarak bir doğruluk tablosu skoru elde edeceğim.

from sklearn.metrics import accuracy_score
tahminler = siniflama.predict(X)
as_egitim = accuracy_score(tahminler, Y)

print("Doğruluk tablosu skoru: ", as_egitim)

Yukarıdaki kod parçacığında, scikit kütüphanesinden accuracy_score’u uygulamama import ettikten sonra predict() metodu ile uygulamamın, eğitim veri içerisinden Survived sütununu göstermeden tahminlerde bulunmasını ve bu tahminleri tahminler adlı değişkende saklanmasını sağladım. Ardından accuracy_score() metodunu kullanarak tahminler ile, içerisinde eğitim verimden gerçek Survived değerlerini taşıyan Y değişkenlerinin skorunu konsola yazdırdım.

Çıktı:

Doğruluk tablosu skoru:  0.848314606741573

CONFUSION MATRIX KULLANIMI

Yukarıda uygulamanın doğruluk skorunu elde ettik ancak, doğruluk skoru tek başına bir başarı ölçme kriteri olamaz. Diğer kriterlere (hata oranı, hassasiyet vb.) bakabilmek için confusion matrix kullanacağım. Bunun için pandas kütüphanesinden crosstab() metodu kullancağım.

confusion_matrix = pd.crosstab(Y, tahminler, rownames=['Gerçek'], colnames=['Tahmin'])
print (confusion_matrix)

Çıktı:

 	  0    1       
0       371  53
1       55   233

Yukarıda elde ettiğim değerler sayesinde, accuracy score’un sağlamasını yapabilir, hata payını ve hassasiyetini ölçebilirim.


Accuracy Score:               (371+233) / 712 = 0.848314606741573
Error Rate:                        (55+53) / 712 = 0. 151685393258427
True Positive Rate:         233 / (55+233) = 0,8090277777777778
False Positive Rate:        53 / (371+53) = 0,125

SONUÇ

Yukarıdaki confusion matrix değerleri ışığında accuracy score değerimizin doğruluğunu sağladık. Ayrıca elde ettiğimiz hassaslık değerine ve diğer başarı kriterlerine bakacak olursak, uygulamamızın yaklaşık olarak her 5 kişiden 4’ünü doğru tahmin ediyor olduğundan bahsedebilir, başarılı bir sınıflandırma işlemi yapıyor diyebiliriz.

Detaylı bilgiler için:

  1. Titanic: Machine Learning from Disaster, https://www.kaggle.com/c/titanic
  2. pandas.concat, https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.concat.html
  3. pandas.get_dummies, https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.get_dummies.html
  4. 1.10. Decision Trees, https://scikit-learn.org/stable/modules/tree.html
  5. Decision Tree Classification in Python, https://www.datacamp.com/community/tutorials/decision-tree-classification-python
  6. Example of Confusion Matrix in Python, https://datatofish.com/confusion-matrix-python/
  7. Hata Matrisini Yorumlama, https://www.veribilimiokulu.com/hata-matrisini-confusion-matrix-yorumlama/
  8. Öne çıkan görsel: https://www.pinterest.dk/pin/684758318332122509/

Python ile öznitelik seçimi ve parametre optimizasyonu

GitHub üzerinden tam haline bakabilirsiniz: https://github.com/berkanaslan/FeatureSelection-ParamOptimizationTutorial

Bu yazıda 3 adet regresyon modeli kullanarak, sırasıyla öznitelik seçimi ve parametre optimizasyonu örneği yapacağım.

Kullanacağım veri setini buraya tıklayarak indirebilirsiniz.

VERİ ÖN ANALİZİ (VERİ KEŞFİ)

import pandas as pd
fileName = 'data.xlsx'
colNames=['id','cycle','setting1','setting2','setting3','s1','s2','s3','s4','s5','s6','s7','s8','s9','s10','s11','s12','s13','s14','s15','s16','s17','s18','s19','s20','s21','ttf']
data = pd.read_excel(fileName, name=colNames)
peek = data.head(5)
print(peek)

Çıktı:

   id  cycle  setting1  setting2  setting3  ...   s18  s19    s20      s21  ttf
0   1      1   -0.0007   -0.0004       100  ...  2388  100  39.06  23.4190  191
1   1      2    0.0019   -0.0003       100  ...  2388  100  39.00  23.4236  190
2   1      3   -0.0043    0.0003       100  ...  2388  100  38.95  23.3442  189
3   1      4    0.0007    0.0000       100  ...  2388  100  38.88  23.3739  188
4   1      5   -0.0019   -0.0002       100  ...  2388  100  38.90  23.4044  187

Veri setini gömme işleminin ardından, veri seti özniteliklerinin değişken türlerini inceledim.

types = data.dtypes
print(types)

Çıktı:

[5 rows x 27 columns]
id            int64 - cycle         int64 - setting1    float64 - setting2    float64
setting3      int64 - s1          float64 - s2          float64 - s3          float64
s4          float64 - s5          float64 - s6          float64 - s7          float64
s8          float64 - s9          float64 - s10         float64 - s11         float64
s12         float64 - s13         float64 - s14         float64 - s15         float64
s16         float64 - s17           int64 - s18           int64 - s19           int64
s20         float64 - s21         float64 - ttf           int64
dtype: object

Bu işlemin ardından özniteliklerimin, toplam değişken sayısı, ortalaması, medyanı, standart sapması gibi istatistiklerini görüntüleyebileceğim kodlarımı yazdım.

from pandas import set_option
set_option('display.width', 125)
set_option('precision',3)
description = data.describe()
print(description)

Çıktı:

             id     cycle   setting1  ...       s20       s21       ttf
count  3635.000  3635.000  3.635e+03  ...  3635.000  3635.000  3635.000
mean      9.080   108.486  1.851e-05  ...    38.841    23.303   109.580
std       5.092    67.751  2.182e-03  ...     0.187     0.112    67.393
min       1.000     1.000 -7.400e-03  ...    38.220    22.940     0.000
25%       5.000    51.000 -1.400e-03  ...    38.720    23.231    53.000
50%       9.000   104.000  1.000e-04  ...    38.860    23.314   106.000
75%      14.000   158.000  1.500e-03  ...    38.980    23.388   159.000
max      18.000   287.000  7.600e-03  ...    39.430    23.601   286.000

[8 rows x 27 columns]

İstatistiklerin ardından özniteliklerimin içerisinde eksik veri olup olmadığını kontrol ettim.

data.isnull().sum()

Bu kod bana çıktı olarak veri setimden eksik veri olmadığını döndürdü.

VERİ DAĞILIMLARI

Veri keşfi ve ön analiz işlemlerine özniteliklerimin histogram grafiklerini inceleyerek devam ettim.

import matplotlib.pyplot as plt
data.hist(figsize=(30,30))
plt.show()

VERİ DAĞILIMLARI MATRİSİ

Histogram grafiklerini elde ettikten sonra özniteliklerin matris grafiklerini inceledim.

from pandas.plotting import scatter_matrix
scatter_matrix(data,figsize=(30,40))
plt.show()

KORELASYON

Histogramın ardından belki de en önemli analiz aşaması olan korelasyonu inceleyebilmek adına gerekli kodlarımı yazdım. Bu bilgiler ışığında veri setimden düşük varyans gösteren öznitelikleri verimden çıkartabilirim.

import seaborn as sn
correlation = data.corr()
sn.heatmap(correlation, linewidths = .5)

VERİ SETİ DÜZENLEME

Yukarıda elde ettiğim korelasyon çıktısı sayesinde düşük varyans gösteren özniteliklerimi öğrenmiş oldum. Bu özniteliklerin isimlerini bir liste değişkenine atayıp veri setimden çıkardım. Ardından geriye kalan özniteliklerimin detaylarını info() metodu ile ekrana yazdırdım.

Bu metot, yukarıda kullanmış olduğum dtypes() metodundan farklı bir şey yapmayacak. Sadece silmek istediğimiz özniteliklerin silinip silinmeme durumunu kontrol etmiş oldum.

dropList = ['setting3', 's5', 's10', 's16', 's18', 's19']
data = data.drop(dropList, axis=1)
print(data.info())

Gerekli öznitelikler ile DataFrame oluşturmak için gerekli kodu yazdım.

newColNames=['id','cycle','setting1','setting2','s1','s2','s3','s4','s6','s7','s8','s9','s11','s12','s13','s14','s15','s17','s20','s21','ttf']
veri = pd.DataFrame(data)
veri.columns = newColNames
print(veri.head())

Çıktı:

   id  cycle   setting1   setting2      s1  ...     s15  s17    s20     s21  ttf
0   1      1 -7.000e-04 -4.000e-04  518.67  ...  84.195  392  39.06  23.419  191
1   1      2  1.900e-03 -3.000e-04  518.67  ...  84.318  392  39.00  23.424  190
2   1      3 -4.300e-03  3.000e-04  518.67  ...  84.178  390  38.95  23.344  189
3   1      4  7.000e-04  0.000e+00  518.67  ...  83.682  392  38.88  23.374  188
4   1      5 -1.900e-03 -2.000e-04  518.67  ...  84.294  393  38.90  23.404  187

Bu işlemin ardından çıktı değişkenim olan ttf sütunu ile diğer özniteliklerimi farklı değişkenlere (X ve Y) atamamı sağlayacak olan kodları yazdım. Ardından işlemin gerçekleşip gerçekleşmediğini teyit etmek için konsola yazdırdım.

veri = data.values
X = veri[:,0:20]
Y = veri[:,20]
print(X.shape)
print(Y.shape)

Bu işlemin ardından veri setimi eğitim – test olarak parçaladım. Parçalama işleminde verilerimin %70’ini eğitim için kullanacağımı belirttim.

from sklearn.model_selection import train_test_split
x_train, x_test, y_train, y_test = train_test_split(X,Y,test_size=0.3, random_state = 0)

Veri seti düzenleme işleminin son adımı olarak veri setimin normalizasyon işlemine tabii tuttum. Normalizasyon işlemini 0 ila 1’e indirgeme yöntemi olan MinMaxScaler ile yaptım. Normalizasyona giren verileri x_train_norm ve x_test_norm değişkenlerinin içerisinde sakladım.

#Normalizasyon işlemi
from sklearn.preprocessing import MinMaxScaler
minMaxScaler = MinMaxScaler()
minMaxScaler.fit(x_train)
x_train_norm = minMaxScaler.transform(x_train)
x_test_norm = minMaxScaler.transform(x_test)

MODELLEME İŞLEMLERİ

Modelleme işlemlerine girmeden, ilk olarak gerekli olan kütüphaneleri uygulamama dahil ettim.

import numpy
from sklearn.model_selection import GridSearchCV
from sklearn.neighbors import KNeighborsRegressor
from sklearn.metrics import r2_score, mean_squared_error
from sklearn.feature_selection import SelectKBest
from sklearn.feature_selection import chi2
from sklearn.ensemble import GradientBoostingRegressor
from sklearn.ensemble import RandomForestRegressor

1.1. K-NEAREST NEIGHBORHOOD MODELLEMESİ

İlk olarak K Nearest Neighborhood modellemesini kullanacağım. K Nearest Beighborhood modellemesi, Türkçe en yakın komşu anlamına gelir. Seçili gözlem(ler)e en yakın gözlemlerin uzaklık mesafeleri baz alınarak modelleme işlemi yapılır.

İlk olarak, hiçbir parameter girmeden modelimi çalıştırdım.

knn_model_ham =  KNeighborsRegressor()
knn_model_ham_fit = knn_model_ham.fit(x_train_norm, y_train)

Modelimi çalıştırdıktan sonra fit() methodu ile eğitim işlemini knn_model_ham_fit içerisinde sakladım. Şimdi, bu eğitim sonucunu test etmek için yeni tahminler üreteceğim.

tahmin_train = knn_model_ham_fit.predict(x_train_norm)
tahmin_test = knn_model_ham_fit.predict(x_test_norm)

Eğitim ve test tahminlerimiz predict() metodu ile gerçekleştirdim. Artık sonucumun skorlarına göz atabilirim.

print("KNeighborsRegressor: Test Sonucu:")
print('KNN R² (Eğitim): %.4f' % r2_score(y_train, tahmin_train))
print('KNN MSE (Eğitim): %.4f' % mean_squared_error(y_train, tahmin_train))
print('KNN R² (Test): %.4f' % r2_score(y_test, tahmin_test))
print('KNN MSE (Test): %.4f' % mean_squared_error(y_test, tahmin_test))

Çıktı:

KNeighborsRegressor: Test Sonucu:
KNN R² (Eğitim): 0.9298
KNN MSE (Eğitim): 316.5318
KNN R² (Test): 0.8825
KNN MSE (Test): 540.5024

Eğitim veri seti ile yaptığım test sonucunda değeri 0.9298, test verisi ile yaptığım skorlamada değerini 0.8825 sonuçlarını elde ettim.

Öte yandan elde edilen sonuçlarda bulunan MSE (Ortalama Kare Hata) değerleri sıfıra yakın değerler olması gerekirken, veri setininde bulunan 0 ve eksili değerler yüzünden yüksel çıktıları vermekte.

1.2. GRADIENT BOOSTING REGRESSOR MODELLEMESİ

İkinci öğrenme algoritması olarak Gradient Boosting Regressor algoritmasını kullanacağım. Bu modelleme algoritması, zayıf öğrenicileri, güçlü öğreniciyedönüştürme yöntemidir. Bu işlemi iterasyon sayısı ile yapar.

İlk olarak, modellemeyi kullanabilmek adına gerekli olan sınıfımı çağırdım.

gbr_model_ham =  GradientBoostingRegressor()
gbr_model_ham_fit = gbr_model_ham.fit(x_train_norm, y_train)

Ardından modelimi kullanarak tahmin işlemlerine gerçekleştirdim.

tahmin_train_gbr = gbr_model_ham_fit.predict(x_train_norm)
tahmin_test_gbr = gbr_model_ham_fit.predict(x_test_norm)

Tahminlerimin skorunu inceleyebilmek adına gerekli kodları uygulamama dahil ettim.

print('GradientBoosting R² (Eğitim): %.4f' % r2_score(y_train, tahmin_train_gbr))
print('GradientBoosting MSE (Eğitim): %.4f' % mean_squared_error(y_train, tahmin_train_gbr))
print('GradientBoosting R² (Test): %.4f' % r2_score(y_test, tahmin_test_gbr))
print('GradientBoosting MSE (Test): %.4f' % mean_squared_error(y_test, tahmin_test_gbr))

Çıktı:

GradientBoosting R² (Eğitim): 0.9819
GradientBoosting MSE (Eğitim): 81.5374
GradientBoosting R² (Test): 0.9721
GradientBoosting MSE (Test): 128.3062

1.3. RANDOM FOREST REGRESSOR MODELLEMESİ

Son olarak Random Forest (Rastgele Karar Ormanları) algoritmasını kullanacağım. İlk olarak sınıfımı uygulamama dahil ettim.

rfg_model_ham =  RandomForestRegressor()
rfg_model_ham_fit = rfg_model_ham.fit(x_train_norm, y_train)

Ardından oluşturduğum değişkeni kullanarak yeni tahminler elde etmek için gerekli kodları uygulamama dahil ettim.

tahmin_train_rfg = rfg_model_ham_fit.predict(x_train_norm)
tahmin_test_rfg = rfg_model_ham_fit.predict(x_test_norm)

Skorlarımı elde edip ekrana yazdırmak için gerekli kodları uygulamama dahil ettim.

print('RandomForestRegressor R² (Eğitim): %.4f' % r2_score(y_train, tahmin_train_rfg))
print('RandomForestRegressor MSE (Eğitim): %.4f' % mean_squared_error(y_train, tahmin_train_rfg))
print('RandomForestRegressor R² (Test): %.4f' % r2_score(y_test, tahmin_test_rfg))
print('RandomForestRegressor MSE (Test): %.4f' % mean_squared_error(y_test, tahmin_test_rfg))

Çıktı:

RandomForestRegressor R² (Eğitim): 0.9945
RandomForestRegressor MSE (Eğitim): 25.0243
RandomForestRegressor R² (Test): 0.9584
RandomForestRegressor MSE (Test): 191.3424

Sırasıyla

  1. KNeighborsRegressor
  2. GradientBoosting
  3. RandomForestRegressor

öğreme algoritmalarını kullandım ve değerlerini elde ettim. Şimdi bu skorları geliştirmek için öznitelik seçme işlemlerini uygulamama dahil etmem gerekiyor.

ÖZNİTELİK SEÇME İŞLEMLERİ (SELECT KBEST)

Veri setinde bulunan öznitelikleri, modelleme işlemlerinde gerekli olmayan öznitelikleri uygulamadan dışlamak adına Select KBest methodunu uygulaya dahil edeceğim. Bu işlem ile modelleme skorlarında ve test – eğitim skorlarının arasındaki farkın azalmasında iyileşme bekliyorum. Bu amaçla k değerini 15 verdim.

#SelectKBest: Öznitelik seçimi:
select_feature = SelectKBest(chi2, k=15).fit(x_train_norm, y_train)

# SelectKBest ile gelen değerleri değişkenlere atadım.
x_train_k = select_feature.transform(x_train_norm)
x_test_k = select_feature.transform(x_test_norm)

Gerekli kodları uygulamama dahil ettim ve artık öncesinde x_train_norm olarak kullandığım eğitim veri setini artık x_train_k olarak; test için kullandığım veri setini de x_test_k olarak kullanacağım.

2.1. K-NEAREST NEIGHBORHOOD MODELLEMESİ

En iyi özniteliklerin seçimi işlemi tamamlandı. Şimdi tekrardan K-Nearest Beighborhood modelimi yeni değişkenler ile kullanarak yeni skorlarımı elde edebilirim.

knn_model =  KNeighborsRegressor()
knn_model_fit = knn_model.fit(x_train_k, y_train)

Yeni tahminler elde etmek için gerekli olan kodlar:

tahmin_train = knn_model.predict(x_train_k)
tahmin_test = knn_model.predict(x_test_k)

Skorları ekrana yazdırmak için gerekli olan kodlar:

print('KNN R² (Eğitim): %.4f' % r2_score(y_train, tahmin_train))
print('KNN MSE (Eğitim): %.4f' % mean_squared_error(y_train, tahmin_train))
print('KNN R² (Test): %.4f' % r2_score(y_test, tahmin_test))
print('KNN MSE (Test): %.4f' % mean_squared_error(y_test, tahmin_test))

Çıktı:

KNN R² (Eğitim): 0.9149
KNN MSE (Eğitim): 383.7701
KNN R² (Test): 0.8617
KNN MSE (Test): 636.5264

Eğitim ve Test skorlarını sırasıyla; 0.9149 ve 0.8617 olarak elde ettim.

2.2. GRADIENT BOOSTING REGRESSOR MODELLEMESİ

İkinci öğrenme algoritması olarak kullandığım Gradient Boosting Regressor modelini, seçilen en iyi öznitelikler ile tekrardan çalışacağım.

gbr_model =  GradientBoostingRegressor()
gbr_model_fit = gbr_model.fit(x_train_k, y_train)

Yeni tahminler elde etmek için gerekli olan kodlar:

tahmin_train = gbr_model.predict(x_train_k)
tahmin_test = gbr_model.predict(x_test_k)

Skorları ekrana yazdırmak için gerekli olan kodlar:

print('Gradient Boosting R² (Eğitim): %.4f' % r2_score(y_train, tahmin_train))
print('Gradient Boosting MSE (Eğitim): %.4f' % mean_squared_error(y_train, tahmin_train))
print('Gradient Boosting R² (Test): %.4f' % r2_score(y_test, tahmin_test))
print('Gradient Boosting MSE (Test): %.4f' % mean_squared_error(y_test, tahmin_test))

Çıktı:

Gradient Boosting R² (Eğitim): 0.9302
Gradient Boosting MSE (Eğitim): 314.8260
Gradient Boosting R² (Test): 0.8796
Gradient Boosting MSE (Test): 553.7993

Eğitim ve Test skorlarını sırasıyla; 0.9302 ve 0.8796 olarak elde ettim.

2.3. RANDOM FOREST REGRESSOR MODELLEMESİ

Üçüncü  öğrenme algoritması olarak kullandığım Random Forest Regressor modelini, seçilen en iyi öznitelikler ile tekrardan çalışacağım.

rfg_model =  RandomForestRegressor()
rfg_model_fit = rfg_model.fit(x_train_k, y_train)

Yeni tahminler elde etmek için gerekli olan kodlar:

tahmin_train_rfg = rfg_model.predict(x_train_k)
tahmin_test_rfg = rfg_model.predict(x_test_k)

Skorları ekrana yazdırmak için gerekli olan kodlar:

print('RandomForestRegressor R² (Eğitim): %.4f' % r2_score(y_train, tahmin_train_rfg))
print('RandomForestRegressor MSE (Eğitim): %.4f' % mean_squared_error(y_train, tahmin_train_rfg))
print('RandomForestRegressor R² (Test): %.4f' % r2_score(y_test, tahmin_test_rfg))
print('RandomForestRegressor MSE (Test): %.4f' % mean_squared_error(y_test, tahmin_test_rfg))

Çıktı:

RandomForestRegressor R² (Eğitim): 0.9860
RandomForestRegressor MSE (Eğitim): 63.1946
RandomForestRegressor R² (Test): 0.8903
RandomForestRegressor MSE (Test): 504.8358

PARAMETRE OPTİMİZASYONU

Kullanmayı tercih ettiğim öğrenme algoritmaları ile sırasıyla ham ve öznitelik seçme işlemlerinin skorlarını elde ettim. Son iyileştirme işlemi olarak tüm öğrenme algoritmalarını parameter optimizasyonuna tabii tutacağım.

3.1. K-NEAREST BEIGHBORHOOD MODELLEMESİ

Parametre optimizasyonunu ilk olarak kNN modellemesi için gerçekleştireceğim.

model = KNeighborsRegressor()

kNN modellemesi parametre olarak yalnızca K değeri alır. Bu sebepke K değerlerini içeren bir dizi oluşturdum.

k_values = numpy.array([1,3,5,7,9,11,13,15,17,19,21])

Her K değeri için modelin çalışmasını sağlayacak kodları uygulamama dahil ettim.

param_grid = dict(n_neighbors=k_values)
grid = GridSearchCV(estimator=model, param_grid=param_grid, cv=None)
grid_result = grid.fit(x_train_k, y_train)

Bu işlem her parametre değeri için modeli çalıştıracak. İşlem sonunda elde edilen en iyi parametre değerini bir değişken içerisinde saklayarak, bu değişkeni kullanıp parametre optimizasyonu sonucunda elde edilen eğitim – test skorumu görüntüleyeceğim.

parameter = grid_result.best_params_
print("En iyi KNN %f ile %s" % (grid_result.best_score_, grid_result.best_params_))

Çıktı:

En iyi KNN 0.880979 ile {‘n_neighbors’: 21}

En iyi parametre değerimiz 21 ile modelimizi çalıştırdığımızda sonucunu 0.880979 elde ettim. Şimdi tüm parametre denemeleri sonucu elde edilen skorları görüntüleyeceğim.

means = grid_result.cv_results_['mean_test_score']
stds = grid_result.cv_results_['std_test_score']
params = grid_result.cv_results_['params']
for mean, stdev, param in zip(means, stds, params):
    print("%f (%f) : %r" % (mean, stdev, param))

Çıktı:

0.776193 (0.015633) : {'n_neighbors': 1}
0.856460 (0.010748) : {'n_neighbors': 3}
0.869698 (0.007955) : {'n_neighbors': 5}
0.873395 (0.006285) : {'n_neighbors': 7}
0.876393 (0.004045) : {'n_neighbors': 9}
0.878516 (0.004130) : {'n_neighbors': 11}
0.879304 (0.004197) : {'n_neighbors': 13}
0.879061 (0.004479) : {'n_neighbors': 15}
0.879493 (0.004729) : {'n_neighbors': 17}
0.880180 (0.004300) : {'n_neighbors': 19}
0.880979 (0.004763) : {'n_neighbors': 21}

3.1. K-NEAREST NEIGHBORHOOD MODELLEMESİ

Son olarak, en iyi parametre değerimiz olan 21 ile yeni tahminler oluşturup skorlarına göz atacağım.

model = KNeighborsRegressor(**parameter)
model.fit(x_train_k, y_train)

tahmin_train = model.predict(x_train_k)
tahmin_test = model.predict(x_test_k)

Modelimi kurdum ve **parameter ile elde ettiğim en iyi parametre değerini modelime atadım. Tahmin değişkenlerini oluşturdum; artık skorlama yapabilirim.

print('KNN R² (Eğitim): %.4f' % r2_score(y_train, tahmin_train))
print('KNN MSE (Eğitim): %.4f' % mean_squared_error(y_train, tahmin_train))
print('KNN R² (Test): %.4f' % r2_score(y_test, tahmin_test))
print('KNN MSE (Test): %.4f' % mean_squared_error(y_test, tahmin_test))

Çıktı:

KNN R² (Eğitim): 0.8924
KNN MSE (Eğitim): 485.5508
KNN R² (Test): 0.8681
KNN MSE (Test): 606.9657

3.2. GRADIENT BOOSTING REGRESSOR MODELLEMESİ

kNN modellemesi için işlemler tamamlandı. Bu kez, aynı işlemleri Gradient Boosting için uygulayacağım.

model = GradientBoostingRegressor()

Gradient Boosting için yapacağım parametre optimizasyonunda 3 farklı parametre yer olacak. Bunlar;

  1. learning_rate:    Öğrenme katsayısı
  2. n_esimators:      Ağaç sayısı
  3. max_depth:        Maksimum derinlik
param_grid = {'learning_rate': [0.01,0.02,0.03],
                  'n_estimators' : [100,500,1000],
                  'max_depth'    : [4,6,8] 
                 }

Ardından bu parametreler ile tek deneme yapması için gerekli olan kodları uygulamama dahil ettim.

grid = GridSearchCV(estimator=model, param_grid=param_grid, cv=None)
grid_result = grid.fit(x_train_k, y_train)

En iyi parametre değerlerini değişken içerisinde atadım ve ekrana yazdırdım.

parameter = grid_result.best_params_ 
print("En iyi GradientBoosting %f ile %s" % (grid_result.best_score_, grid_result.best_params_))

Çıktı:

En iyi GradientBoosting 0.896024 ile {'learning_rate': 0.01, 'max_depth': 6, 'n_estimators': 1000}

3.2. GRADIENT BOOSTING REGRESSOR MODELLEMESİ

En iyi parametre sonucumu elde ettim. Diğer parametreler ile elde edilen skorları görüntülemek için gerekli kodları uygulamama dahil ettim.

eans = grid_result.cv_results_['mean_test_score']
stds = grid_result.cv_results_['std_test_score']
params = grid_result.cv_results_['params']
for mean, stdev, param in zip(means, stds, params):
    print("%f (%f) : %r" % (mean, stdev, param))

Çıktı:

0.746375 (0.008246) : {'learning_rate': 0.01, 'max_depth': 4, 'n_estimators': 100}
0.893181 (0.005478) : {'learning_rate': 0.01, 'max_depth': 4, 'n_estimators': 500}
0.894468 (0.005979) : {'learning_rate': 0.01, 'max_depth': 4, 'n_estimators': 1000}
0.763377 (0.009317) : {'learning_rate': 0.01, 'max_depth': 6, 'n_estimators': 100}
0.894708 (0.007509) : {'learning_rate': 0.01, 'max_depth': 6, 'n_estimators': 500}
0.896024 (0.006824) : {'learning_rate': 0.01, 'max_depth': 6, 'n_estimators': 1000}
0.766962 (0.010311) : {'learning_rate': 0.01, 'max_depth': 8, 'n_estimators': 100}
0.892055 (0.006572) : {'learning_rate': 0.01, 'max_depth': 8, 'n_estimators': 500}
0.891872 (0.007298) : {'learning_rate': 0.01, 'max_depth': 8, 'n_estimators': 1000}
0.865966 (0.006261) : {'learning_rate': 0.02, 'max_depth': 4, 'n_estimators': 100}
0.895264 (0.005830) : {'learning_rate': 0.02, 'max_depth': 4, 'n_estimators': 500}
0.894342 (0.005479) : {'learning_rate': 0.02, 'max_depth': 4, 'n_estimators': 1000}
0.874068 (0.008295) : {'learning_rate': 0.02, 'max_depth': 6, 'n_estimators': 100}
0.895356 (0.008382) : {'learning_rate': 0.02, 'max_depth': 6, 'n_estimators': 500}
0.894645 (0.008648) : {'learning_rate': 0.02, 'max_depth': 6, 'n_estimators': 1000}
0.874453 (0.007271) : {'learning_rate': 0.02, 'max_depth': 8, 'n_estimators': 100}
0.891052 (0.007488) : {'learning_rate': 0.02, 'max_depth': 8, 'n_estimators': 500}
0.891294 (0.006860) : {'learning_rate': 0.02, 'max_depth': 8, 'n_estimators': 1000}
0.887539 (0.004514) : {'learning_rate': 0.03, 'max_depth': 4, 'n_estimators': 100}
0.895332 (0.005561) : {'learning_rate': 0.03, 'max_depth': 4, 'n_estimators': 500}

Son olarak en iyi parametre sonucumu kullanarak modelimin yeni tahminleme skorunu ölçeceğim.

model = GradientBoostingRegressor(**parameter)
model.fit(x_train_k, y_train)
tahmin_train = model.predict(x_train_k)
tahmin_test = model.predict(x_test_k)

print("GradientBoostingRegressor: Parametre Optimizasyonu: Test Sonucu:")
print('Gradient Boosting R² (Eğitim): %.4f' % r2_score(y_train, tahmin_train))
print('Gradient Boosting MSE (Eğitim): %.4f' % mean_squared_error(y_train, tahmin_train))
print('Gradient Boosting R² (Test): %.4f' % r2_score(y_test, tahmin_test))
print('Gradient Boosting MSE (Test): %.4f' % mean_squared_error(y_test, tahmin_test))

Çıktı:

Gradient Boosting R² (Eğitim): 0.9844
Gradient Boosting MSE (Eğitim): 70.5342
Gradient Boosting R² (Test): 0.8877
Gradient Boosting MSE (Test): 516.7419

3.3. RANDOM FOREST REGRESSOR MODELLEMESİ

Parametre optimizasyonu işlemini bu kez de Random Forest Regressor için kullanacağım.

model = RandomForestRegressor()

Random Forest için kullanacağım parametre alternatiflerim:

param_grid = {'n_estimators': [100,200,300,400],
              'max_depth': [5,10,15,20]
                 }

Parametre optimizasyonu işlemlerini gerçekleştirecek olan kodları uygulamaya dahil ettim:

grid = GridSearchCV(estimator=model, param_grid=param_grid, cv=None)
grid_result = grid.fit(x_train_k, y_train)

En iyi parametre değerlerini değişken içerisinde atadım.

parameter = grid_result.best_params_
print("En iyi RandomForestRegressor %f ile %s" % (grid_result.best_score_, grid_result.best_params_))

Çıktı:

En iyi RandomForestRegressor 0.896765 ile {'max_depth': 15, 'n_estimators': 400}

En iyi parametre değerlerini elde ettim. Diğer parametre değerleri ile elde ettiğim skorlara göz attım.

means = grid_result.cv_results_['mean_test_score']
stds = grid_result.cv_results_['std_test_score']
params = grid_result.cv_results_['params']

for mean, stdev, param in zip(means, stds, params):
    print("%f (%f) : %r" % (mean, stdev, param))

Çıktı:

0.881170 (0.007204) : {'max_depth': 5, 'n_estimators': 100}
0.881755 (0.007216) : {'max_depth': 5, 'n_estimators': 200}
0.881739 (0.007661) : {'max_depth': 5, 'n_estimators': 300}
0.881476 (0.007668) : {'max_depth': 5, 'n_estimators': 400}
0.896121 (0.004987) : {'max_depth': 10, 'n_estimators': 100}
0.895640 (0.005138) : {'max_depth': 10, 'n_estimators': 200}
0.895850 (0.005978) : {'max_depth': 10, 'n_estimators': 300}
0.896075 (0.005359) : {'max_depth': 10, 'n_estimators': 400}
0.896266 (0.005592) : {'max_depth': 15, 'n_estimators': 100}
0.896354 (0.004997) : {'max_depth': 15, 'n_estimators': 200}
0.896345 (0.005385) : {'max_depth': 15, 'n_estimators': 300}
0.896765 (0.006037) : {'max_depth': 15, 'n_estimators': 400}
0.895089 (0.005261) : {'max_depth': 20, 'n_estimators': 100}
0.895957 (0.005848) : {'max_depth': 20, 'n_estimators': 200}
0.896405 (0.005552) : {'max_depth': 20, 'n_estimators': 300}
0.896261 (0.005450) : {'max_depth': 20, 'n_estimators': 400}

3.3. RANDOM FOREST REGRESSOR MODELLEMESİ

Son olarak parameter isimli değişken içerisinde sakladığım, Random Forest modelim için en iyi parametre değerleri için modelimi tekrardan kuracağım. İşlem sonrasında yeni tahminleme işlemi yapıp skorları görüntüleyeceğim.

model = RandomForestRegressor(**parameter)
model.fit(x_train_k, y_train)

tahmin_train = model.predict(x_train_k)
tahmin_test = model.predict(x_test_k)

Skorlama ve ekrana yazdırma:

print('RandomForestRegressor: Parametre Optimizasyonu: Test Sonucu:')
print('RandomForestRegressor R² (Eğitim): %.4f' % r2_score(y_train, tahmin_train))
print('RandomForestRegressor MSE (Eğitim): %.4f' % mean_squared_error(y_train, tahmin_train))
print('RandomForestRegressor R² (Test): %.4f' % r2_score(y_test, tahmin_test))
print('RandomForestRegressor MSE (Test): %.4f' % mean_squared_error(y_test, tahmin_test))

Çıktı:

RandomForestRegressor R² (Eğitim): 0.9858
RandomForestRegressor MSE (Eğitim): 64.2212
RandomForestRegressor R² (Test): 0.8923
RandomForestRegressor MSE (Test): 495.6972

Elde edilen bu skor ile birlikte, uygulamada elde etmem gereken tüm sonuçları elde ettim. Bu sonuçları kıyaslamak için bir tablo oluşturdum.

SONUÇ TABLOSU

1. HAM R² VE MSE SONUÇLARI

 MSE
K-Neighbor Eğitim0.9298316.5318
K-Neighbor Test0.8825540.5024
Gradient Boost Eğitim0.981981.5374
Gradient Boost Test0.9721128.3062
Random Forest Eğitim0.994525.0243
Random Forest Test0.9584191.3424

2. ÖZNİTELİK SEÇİMİ SONRASI R² VE MSE SONUÇLARI

 MSE
K-Neighbor Eğitim0.9149383.7701
K-Neighbor Test0.8617636.5264
Gradient Boost Eğitim0.9302314.8260
Gradient Boost Test0.8796553.7993
Random Forest Eğitim0.986063.1946
Random Forest Test0.8903504.8358

3. PARAMETRE OPTİMİZASYONU SONRASI R² VE MSE SONUÇLARI

 MSE
K-Neighbor Eğitim0.8924485.5508
K-Neighbor Test0.8681606.9657
Gradient Boost Eğitim0.984470.5342
Gradient Boost Test0.8877516.7419
Random Forest Eğitim0.985864.2212
Random Forest Test0.8923495.6972

SONUÇ

K-Neighbor, Gradient Boosting ve Random Forest modellenmesi ve kullanımı, öznitelik seçimi ve parametre optimizasyonu gibi işlemler başarıyla tamamlanmıştır.

Parametre optimizasyonu kullanımı sonucunda elde edilen skorlara bakıldığında, Gradient Boosting ve Random Forest modellerinin değerleri kıyaslandığında birbirine yakın sonuçlar vermis; K-Neighbor modeli ise diğer algoritmalara göre daha düşük bir skor döndürmüştür. Ancak, yüksek eğitim test skoru döndüren Gradient Boosting ve Random Forest modellerinin eğitim performansı ile yeni tahminler ile elde edilen R² puanı kıyaslandığında bir tutarsızlık söz konusudur.

K-Neighbor ise, birbirine yakın sonuç veren eğitim ve yeni tahmin başarısı ile daha güvenilir bir modellemedir. Bu nedenle, projede model olarak K-Neighbor algoritmasının kullanılması, verisetine eklenecek her yeni gözlemin daha başarılı tahmin edilmesi, dahabaşarılı skorlanması anlamına gelmektedir.

Flutter nedir?

Son yıllarda Android’e duyduğum ilgiyi, Java’nın ötesine taşımak istediğimde, kendime “Gerçekten Kotlin öğrenmeli miyim?” diye sordum. Yaptığım ufak araştırmalar sonucunda Flutter’a başlamanın kendi adıma daha iyi bir fikir olduğuna karar kıldım. Kendisiyle biraz haşır neşir olduktan sonra da böyle bir yazı yazmak istedim.

Flutter Nedir?

Flutter, Google tarafından geliştirilen açık kaynaklı bir çapraz platform mobil uygulama SDK’sıdır. Hem Android hem de iOS uygulamalarını geliştirmek için kullanılır. Flutter, Dart dilinde yazılmıştır. Java, Javascript, Objective C veya Swift gibi Nesne Yönelimli Programlama (OOP) bilenler Dart ile kolayca bağlantı kurabilirler.

Geliştiriciler Flutter’ı tercih ederek, her iki platformda da diledikleri aplikasyonları geliştirebilir, hem maliyetten hem de zamandan tasarruf edebilirler.

Neden Flutter?

  • Platformlar arası geliştirmeyi (cross) desteklediğinden zaman ve maliyet tasarrufu sağlar.
  • Geliştiriciler aynı arayüzü iki kez oluşturmak zorunda kalmaz.
  • Mobil uygulamaların doğasına uygun arayüz ve kullanıcı deneyimi sağlar.
  • 2D efektler ve animasyonlar oluşturmaya olanak tanır.
  • Hot Reloading özelliği sayesinde hızlı çalışır.

Geliştirme Platformları

Flutter geliştiricisi olmak isteyenlerin Android SDK’nın yanı sıra Flutter SDK’ya da sahip olmaları gerekir. Gerekli SDK’lara erişldikten sonra

  • Android Studio,
  • VS Code,
  • Xcode

gibi derleyicilere kurulan Dart ve Flutter eklentileri sayesinde Flutter geliştiricisi olabilirsiniz.
Gerekli SDK’lara buraya tıklayarak erişebilirsiniz.

Flutter Hedefleri

Fast Development

Flutter’ın hızlı bir şekilde yeniden yüklenmesi, (Hot Reload) hızlı ve kolay bir şekilde test yapmanıza, kullanıcı arayüzleri oluşturmanıza, yeni özellikler eklemenize ve hataları daha hızlı bir şekilde gidermenize yardımcı olur. IOS ve Android uygulamalar için emülatör, simülatör ve donanım üzerinde, zaman kaybetmeden hızlı bir şekilde çalışır.

Expressive, Beautiful UIs

Flutter, Android ve iOS’un temel yapı taşlarına göre hazırlanmıştır. Google ve Apple tarafından yayımlanan tasarım ilkelerine göre hazırlanan Flutter sayesinde kullanıcılar kullanılabilirlik ve uyum sorunu yaşamaz.

Modern, Reactive Framework

Flutter’da her şey widget’tan türetilir. Kullanıcı arayüzünüzü kolay bir şekilde, Flutter’in modern ve kullanışlı düzeni ve temel widget seti ile geliştirebilirsiniz. 2D animasyon, efektler ve daha fazlası için güçlü UI zorluklarını güçlü ve esnek API’ler sayesinde aşabilirsiniz.

Tutorial Kaynakları

Özellikle yeni başlayanlar için; karar yapıları döngüler ve OOP temellerini kavrayabilmek adına çok kullanışlı bir dil olan DartLang ile çapraz platformlara aplikasyonlar geliştirmek yeni hobiniz olabilir. Bu konudaki en değerli kaynağınız, Flutter’ın resmi web sitesi olan flutter.dev dökümantasyon sayfaları olacaktır.

Github vs. Gitlab

Git nedir?

Git, Linus Torvalds tarafından tasarlanıp ve geliştirilmiş olup, yazdığımız projeleri veya uygulamaları, bilgisayarımızda ya da harici disklerde değil; internet üzerinde saklamamızı ve yönetmemizi sağlayan bir versiyon kontrol sistemidir.

GitHub nedir?

Github, sürüm kontrol sistemi olarak Git kullanan yazılım projeleri için bir depolama servisidir. Açık kaynak kodlu projeler için ücretsiz sürüm sunan Github, özel depolar için ücretli üyelik seçenekleri sunmakta. Ayrıca Github, öğrenciler için özel üyelik seçeneği “education” ile ücretsiz özel depo hizmeti vermekte:

education.github.com

GitLab nedir?

GitLab, GitHub’ın kullanıcılarına sağladığı işlevlerin tamamını sunan bir Git hizmetidir. Açık kaynak projelerinizi bu servis üzerinde ücretsiz olarak oluşturabilir ve yönetebilirsiniz. GitLab, ücretsiz sürümünde sunduğu, sadece kurum içi kullanıcıların erişebileceği bir GitLab servisi imkanı sunduğundan, daha çok firmalar tarafından tercih edilmektedir.

GitHub vs. GitLab

Felsefe

GitLab, profesyonel bir Git deposu yöneticisidir. Uygulamalarınızı test edebilmeniz adına bir çok araç sunar. Kendi sunucularında barındırabilme hakkı en büyük avantajı.
GitHub ise, daha çok sosyal ağ gibidir. Neredeyse her büyük açık kaynak kütüphanelerini, araç kitlerini ve frameworkleri içerisinde barındırır.

GitLab;

  • Güçlü CI araçları
  • Ücretli ve ücretsiz Docker kayıt defteri
  • Ücretsiz özel depolar gibi özellikler sunmaktadır ama;

Eğer sıfırdan bir proje kodlamaya başlayacaksanız, GitHub sizin için daha iyi bir seçenek olacaktır. GitHub, daha çok bir sosyal ağ gibidir. Bu özelliği sayesinde projeniz üzerinde yardım alabileceğiniz daha çok muhatap bulabilirsiniz.

WooCommerce vs. Shopify

Statista‘nın verilerine göre, 2021’de internetten yapılan satışlar %95 artacak ve 4.7 trilyon dolar’a ulaşacak. Yani kişiler, alışverişlerinin çok daha fazlasını cep telefonundan ve bilgisayardan yapacak.

Her girişimci, böylesine büyüyen bir pazar içerisinde yer alabilmek için araştırmalar yapıyor. Araştırma sonunda ise WooCommerce, Shopify, Opencart, Magento vb. gibi terimlere ulaşılıyor. Biz de, bu altyapı sağlayıcılarının arasında, WooCommerve ve Shopify arasındaki farkları öğrenebilmek adına kıyaslama yapacağız.

WooCommerce nedir?

WooCommerce, ilk kez WordPress için tema geliştiricisi WooThemes tarafından Eylül 2011 tarihinde piyasaya sürülmüştür. Platform olarak açık kaynak kodlu CMS* olan WordPress’i altyapı olarak kullanan WooCommerce, Mayıs 2015 tarihinde -ayrıca WordPress sağlayıcısı da olan- Automattic tarafından satın alınmıştır. WooCommerce, açık kaynak kodlu bir platform üzerine inşa edilmiş, e-ticaret altyapısı sağlayan WordPress eklentisidir.
*İçerik Yönetim Sistemi

WordPress kullanan işletme web siteleri doğal olarak WoCommerce’a eğilimlidir. Uyumluluk ve kullanım kolaylığı nedeniyle, birçok WordPress kullanıcısı WooCommerce tercih etmektedir. Online mağaza kurma ve ürün ekleme işlemleri oldukça basittir. WordPress tabanlı web sitenizde yazı veya resim ekleme sürecinden farkı yoktur.

Shopify nedir?

Shopify, 2004 yılında Kanada’da kullanıma sunulmuş olan bir e-ticaret altyapısı sağlayıcısıdır. Küçük işletmeler için, online mağaza yönetimini basitleştirmek için ortaya çıkmıştır. Ödemeler, nakliye, pazarlama, müşteri katılımına özel paketler sunarak, girişimcilere altyapı sağlamaktadır.

Shopify, 70’ten fazla ödeme ağ geçidini 50’den fazla dilde bulunan check-out’larla entegre eder (Stripe, Paypal vs. gibi) ve bu da dünyanın her yerinden müşterilere ürünlerinizi satmayı kolaylaştırır.

WooCommerce Kullanmanın Avantajları

  1. Kolay kurulum ve kodlamaya gerek olmaması,
    WordPress ve WooCommerce, online mağazanızı, internet ortamına fotoğraf yüklemekten çok daha zor olmayan işlemlerle açmanızı sağlar.
  2. Eklenti imkanının fazla olması
    Güvenlik, veri depolama, otomatik mail işlemleri, spam yönetimi vb. binlerce özellik için ihtiyaçlarınızı karşılayabilecek binlerce eklenti mevcut.
  3. Tema Çeşitliliği ve Kolay Tasarım
    Bir kaç fare tıklaması sonucunda ücretsiz/ücretli temaların kurulumunu gerçekleştirebilirsiniz.
  4. Arama Motorlarına İndekslenmek
    WordPress eklentilerini kullanarak Google, Yandex vb. arama motorlarında üst sıralarda kendinize yer bulabilirsiniz.
  5. Kontrol Paneli Kullanımının Kolay Olması
    WordPress yönetim paneli sayesinde web sitenizde bir düzenlemeye gitmek istediğiniz zaman, sayfaların içerik yönetimi, görsel eklemek, butonların, formların ve çeşitli ayarlamaların düzenlenmesi hızlı bir şekilde gerçekleşebilecek adımlardır.
  6. Ücretsiz Olması
    WooCommerce kullanımı için hiçbir ücret ödemenize gerek yok. Bir çok eklenti, hizmet ve yazılımın ücretli aboneliklerinin olmasının yanı sıra ücretsiz paketler de mevcut.

Shopify Kullanmanın Avantajları

  1. Hızlı kurulum
    WooCommerce’de olduğu gibi, Shopify’da da hiçbir kodlama bilmeden online mağazanızı açabilirsiniz.
  2. Tüm İhtiyaçların Tek Çatıda Toplanmış Olması
    Ödeme yöntemleri, temalar, uygulama vb. online mağazacılık için tüm gerekli şeyler tek çatı altında.
  3. 14 Günlük Deneme Süresi
    Shopify, ücretli abonelik sunmakta. Ancak 14 günlük deneme süresi güzel bir imkan.
  4. Hosting , Sunucu ile Uğraşmanıza Gerek Yok
    Shopify, sunduğu abonelik paketi içerisinde bu tür işlemleri halletmekte.
  5. Güvenilirliğini Kanıtlamış Olması
    2017 başında 5 Milyar Dolar şirket değerine ulaşan Shopify, dünyanın en büyük bulut tabanlı e-ticaret altyapı sağlayıcısıdır.
  6. Yeni Satış Kanalları
    Dilerseniz Facebook mağazanızı Facebook sayfanızda açıp satış yapabilir, dilerseniz mevcut blog’unuza “satın al” düğmesi ekleyerek direkt satış imkanı kazanabilirsiniz.

WooCommerce Kullanmanın Dezavantajları

  1. İşlemciyi Fazla Yoruyor Olması
    Hostinginizi almadan önce dikkat etmeniz gereken en önemli unsurda bu yüzden hostinginizin CPU kullanımıdır. Olabildiğince yüksek CPU kullanım alanı bulunan hostingleri tercih etmenizi tavsiye ederim.
  2. Verilerinizin Gizliliği
    Son zamanlarda WordPress güvenlik sorunlarını yaptığı güvenlik sorunlarıyla çözüyor gibi gözükse de; adı çıkmış dokuza, inmez sekize.
  3. E-Ticaret
    WordPress alanının bir numarası olabilir. Ancak e-ticaret konusunda kesinlikle akla gelen başka isimler var.

Shopify Kullanmanın Dezavantajları

  1. Ücretli Abonelik
    Shopify, sunduğu hizmetler karşılığında satış yaptığınız ürünler üzerinden komisyon ve aylık $29 ücret talep etmektedir.
  2. Tema Problemi
    Bir çok tema seçeneği var gibi gözükse de, ücretli temaların ücretinin yanı sıra ücretsiz temaların azlığı problem yaratmakta.
  3. Sınırlı Raporlama Seçeneği
    Basit bir örnek: Ödemesini gerçekleştirmemiş bir müşteri, müşteri olarak sayılmayabiliyor. Bu 3. taraf uygulamalar tarafından yaptığınız analizlerin tutarlılığını etkileyebilir.

Sonuç

WooCommerce ve Shopify ile yaptığımız karşılaştırmayı bir sonuca bağlayacak olursak;

  • Kurulum kolaylığı açısından, hosting, sunucu bakımı, SSL gibi terimlerle uğraşmadan kurulum yapmanızı sağlayan Shopify’ın tercih edilmesi,
  • Maliyet açısından, hiçbir ücret ödemeden online mağazanızı açabilme imkanı sunan WooCommerce’un tercih edilmesi,
  • Kullanım Kolaylığı açısından incelendiğinde, WooCommerce’de ihtiyaçlarınızı karşılayacak eklentileri bulmakla uğraşmak yerine sürükle bırak kullanım arayüzü sayesinde Shopify’ın tercih edilmesi,
  • Ödeme Yöntemleri açısından yaklaştığımızda, her ne kadar komisyon alıyor olsa da, 70’ten fazla dilde sunduğu ödeme yöntemi ile Shopify tercihi,
  • Eklentiler ve Entegrasyonlar tarafında ise, 55.000’den daha fazla eklentiyi içerisinde barındıran WordPress tabanlı WooCommerce’nin tercih edilmesi doğru karar olacaktır.

Kişisel tercihim ise, daha çok özgürlükçü platformda, dilediğim hizmetleri kullanarak herhangi bir komisyon ödemeden online mağazamı açabileceğim WooCommerce’yi tercih etmek olacaktır.

WooCommerce, SSL, Hosting, sunucu yönetimi, optimizasyon, ödeme yöntemlerine başvurmak gibi süreçlerden geçerek online mağazanın açılmasına olanak sağlamaktadır. Eğer bunlara harcayacak bir zamanım ve teknik bilgim olmasaydı, bu hizmetlerin karşılığı olarak abonelik ücretini ödeyerek Shopify kullanmayı tercih ederdim.

PHP öğrenmek hala değerli mi?

Bu yazı, Dev.to platformunda Mike Oram tarafından yayınlanan “Is PHP relevant?” adlı yazının Türkçe çevirisidir.

 

Bana akademisyenler, iş başvurusu yapanlar hatta diğer geliştiriciler tarafından en sık sorulan sorulardan biri “Neden PHP öğretiyorsun?” hatta bazen “PHP ölü bir dil değil mi?” sorusudur.

En başından bu konuda gayet açık olayım. PHP ÖLÜ BİR DİL DEĞİLDİR.

2017 yılının aralık ayında, PHP internette server tarafında kullanılan programlama dillerinin %83’ünü oluşturuyordu. Bu sonucu PHP tabanlı WordPress gibi içerik yönetim sistemlerine borçluyuz, ama bu hazır içerik yönetim sistemlerini bu orandan çıkarsak dahi, PHP web’in %54’ünden fazlasına güç vermekte. Zaten, aşağıdaki grafiğe bakarsanız, PHP’nin market payı 2017 yılında gayet tutarlıymış hatta artış göstermiş.

Eylül ayında ‘IT endüstrisi için uygun’ yetenek ve teknolojiler hakkında bir blog yazısı yazmıştım. O blog yazısını yazdığım sırada, PHP ve Javascript iş pazarında açık ara aranan özelliklerdendi. Hala da böyledir. Tesadüfi olmayan bir şekilde, akademide en çok vakit ayırdığımız iki dil de PHP ve JavaScript’tir.

Şimdi, yeni programcılara en çok öğretilen diğer dillerin kullanım istatistiklerine bir göz atalım. Üniversiteler Java veya C’ye odaklanmaya yatkınlar. Bunun sonucunda bir çok girişim uygulamalarını bu dilleri kullanarak geliştirmekte. Tüm bu çabaların sonucunda Java server tarafındaki uygulamaların %2.5’una güç veriyor. C hiç bir yerde görülmemekle beraber, bazen üniversitelerde öğretilen ASP.NET %14.2 oranına ulaşabilmiş. Programlama workshoplarında en popüler olan Python, Ruby veya sunucu tabanlı JavaScript dillerinin hepsini birden topladığımıza, internettin sadece %1.2’sine güç verebilmişler. Sunucu taraflı JavaScript (node.js) en hızlı artan sunucu taraflı teknoloji olmasına rağmen halen az kullanılıyor ancak tabiiki öğrenilmeli. (Biz akademik takvimimizde Node.js öğretmeye 1 hafta ayırıyoruz)

Peki neden bir sürü kişi PHP’nin uygunsuz ya da ölü bir dil olduğunu iddia ediyor?

Bunun, benim gördüğüm iki ana nedeni var. Birincisi, bu geliştiriciler arasında dolaşıp duran bir söylentiden ibaret. PHP, ilk zamanlarında görece olarak yavaş, nereye gittiği belli olmayan ve bir sürü tutarsızlığa sahip bir dildi. Yıllar boyunca PHP bayağı bir gelişti, hatta 2009 yılında 5.3 sürümünün yayınlanmasından beri, eski şikayetlerin çoğu düzeltildi. En son sürüm (7.1) son derece hızlı, modern ve güçlü bir şekilde nesne yönelimliliğe odaklanan bir dildir.

İkincisi, PHP esnek ve “loosely typed” yani kesin tür belirtilmemiş bir dildir. Bu, metin editörünü açıp hızlı yazılım geliştirmeye olanak verse de, kolayca kötü kod üretmeye de yol açmakta. Yani kendi başarısının bir kurbanı olduğunu söyleyebilirsiniz. Ancak, düzgün bir şekil yazıldığında, ve DRY, SOLID ve MVC gibi (bizim de akademide öğrettiğimiz) metodolojiler kullanıldığında, PHP gerçekten güçlü, iyi bir çeşitliliğe sahip, bir çok şeyleri sunabilecek hızlı bir dildir.

Yani, PHP ölmedi. Her dilde olduğu gibi kendine özgü kusurları olsa da, bırakalım istatistikler bizim yerimize bu iddialara cevap versin.

 

Orjinal dilinde yayımlanmış hali:  https://dev.to/mporam/is-php-relevant–1np
Çeviri sahibi:  mynameismidori.com

Gamification (Oyunlaştırma) neden bu kadar yaygınlaştı?

Gamification, (Oyunlaştırma) insanların, davranışlarını ve eğilimlerini değiştirmek için oyun mekanizmalarının, aslında oyun olmayan aktivitelere uygulanmasına denir. Bu yöntemin en büyük amacı, oyunlaştırmanın uygulandığı, aplikasyonda, web sitesinde, anket&form işlemlerinde vb. sistemlerde katılımı ve teşviki artırmaktır.

Swarm’da check-in yaparak mekanın mayor’ü olmaya çalışıyor, Yemek Sepeti’nde yemek siparişleri vererek bulunduğumuz konumun muhtarı olmaya uğraşıyor ve bazen de George Clooney* gibi 5 milyon uçuş mili toplamak için çabalıyoruz. İşte bunun sebebi, kullandığımız servislerde, gamification ile servisin içine gömülmüş olan oyun dinamiğinden** kaynaklanmaktadır.

*George Clooney’nin başrolünde oynadığı Up in the Air (Aklı Havada) filmindeki iş adamı, beş milyon uçuş mili toplamaya çalışmaktadır.
**Bir etkinliği oyun yapan eylemlere, aktivitelere ve kontrol mekanizmalarına oyun mekanikleri; bu mekaniklerin yarattığı motivasyon, elde etme arzusu duygularına oyun dinamiği denir. Ödül, seviye, rekabet, başarı gibi kavramlar oyun dinamğini oluşturur.

Gamification kavramının popüler olmasına en büyük katkıyı sağlayan 2 olay vardır. Bunlar;

  1. Rajat Paharia’nın, 2005 yılında Bunchball* adlı şirketi kurarak, müşterinin sadakat artışını sağlamak için geliştirdiği oyunlaştırma platformu,
  2. Pennsylvania Üniversitesi‘nde Hukuk Çalışmaları ve İş Etiği Profesörü olan Kevin Werbach’nın 2012 yılında Coursera yüklediği eğitim hatftalık eğitim programı.

Banchball* Tanıtım Videosu

Oyunlaştırma mekanizmasının en sık kullanıldığı alanlardan birisi de eğitimdir. 2. maddede bahsettiğim Prof. Kevin Werbach gibi eğitim alanında da oyunlaştırma mekanizması kullanılmaktadır. Bu sayede, öğrenme süreci, pasif ve sıradan olmaktan ziyade, keşfedilebilir bilgiler ile aktif ve öğrencilerin dikkatinin dağılmadığı bir öğrenim süreci elde edilebilmektedir. Eğitim alanında kullanılan gamification mekanizmasının en büyük örneği Scratch‘dir.

Scratch, ABD’de bulunan MIT’nin geliştirdiği, 8-16 yaş arası çocukların kullanımına göre tasarlanmış oldukça basit bir arayüze sahip programlama ortamıdır. Çocuklar, Scratch sayesinde, kodlama ekranından ziyadeoyunlaştırılmış bir arayüz kullanarak programlama mantığını öğrenmektedir.

Gamification, müşterilerin/kullanıcıların, kayıt olup, kişisel hesap oluşturabildiği her sisteme uygulanabilir. Basit bir forum sitesinde insanların sorunlarına cevap vererek kazandığınız puanlar da, bir e-Ticaret sitesinde alışveriş sonunda kazandığınız indirim kuponları da gamification mekanizması için örnek olarak sayılabilir. Bu tür kullanımlarda var olan müşteriyi/kullanıcıyı kaybetmeme amacı vardır.

Gamification için, firmalara proje oluşturan ve profesyonel destek veren oluşumlar Türkiye’de de yaygınlaşmaya başlamıştır. Bir çok öğrenci, GamificationAcademy gibi eğitimler sayesinde bu yönde istihdam sağlamaktadır.

Son olarak; bu yıl, Maarten Molenaar, Sylvester Arnab, An Coppens gibi isimlerin katılacağı Gamification Turkey Zirvesi‘nden bahsetmek istiyorum. Bir sonraki zirve, 15 Kasım 2018 tarihinde İstanbul’da gerçekleştirilecek. Detaylı bilgiyi gamificationturkey.org sitesinden öğrenebilirsiniz.