Saturday, 4 August 2018

Python Generator and Iterator

Iterator

Iterator is an object which allows to traverse through all the elements of a collection.

In Python, an iterator is an object which implements the iterator protocol.

The iterator protocol consists of two methods.

  1. The iter() method, which must return the iterator object,
  2. the next() method, which returns the next element from a sequence.

Iterators have several advantages:

  • can work with infinite sequences
  • save resources

Python has several built-in objects examples lists, tuples, strings, dictionaries or files, which implement the iterator protocol.

## Define Custom Iterator using Iterator Protocol.

class krange(object):
    
    def __init__(self, n):
        self.i = 0
        self.n = n
        
    def __iter__(self):
        return self
    
    def next(self):
        if self.i < self.n:
            i = self.i
            self.i += 1
            return i
        else:
            raise StopIteration()
k = krange(5)
print(k.next())
print(k.next())
print(k.next())
print(k.next())
0
1
2
3

Example: Write an iterator class reverse_iter, that takes a list and iterates it from the reverse direction.

class reverse_iter(object):
    
    def __init__(self, alist):
        self.alist = alist
        self.n = len(alist) -1 
        
    def __iter__(self):
        return self
    
    def next(self):
        if self.n > 0:
            item = self.alist[self.n]
            self.n -= 1
            return item
        else:
            raise StopIteration()
        
abc = reverse_iter([1,2,3,4,5])
print(abc.next())
print(abc.next())
print(abc.next())
print(abc.next())
5
4
3
2

Python Generators

Generator is a special routine that can be used to control the iteration behavior of a loop. A generator is similar to a function returning an array.
Generators are defined similar to a function and have the same properties like has parameters, can be called. But unlike functions, which return a whole array, a generator yields one value at a time. This requires less memory.

Generators in Python:

  • Use the yield keyword and it May use several yield keywords
  • Return an iterator

Generators simplifies creation of iterators. A generator is a function that produces a sequence of results instead of a single value.

So a generator is also an iterator. Here You don’t have to worry about the iterator protocol.

def gen(alist):
   for k in alist:
    yield k

g = gen([1,2,3,4,5])
print(next(g))
print(next(g))
print(g.__next__()) # g.next() in python2
print(g.__next__())
1
2
3
4

Can you think about how it is working internally?

When a generator function is called, it returns a generator object without even beginning execution of the function. When next method is called for the first time, the function starts executing until it reaches yield statement. The yielded value is returned by the next call.

Let’s check an example:

def foo():
    print("start Foo")
    for i in range(5):
        print(f"Before Yield i -- {i}")
        yield i
        print(f"After Yield i -- {i}")
    print("End Foo")
f = foo()
print(f.__next__())
start Foo
Before Yield i -- 0
0
print(f.__next__())
After Yield i -- 0
Before Yield i -- 1
1
print(f.__next__())
After Yield i -- 1
Before Yield i -- 2
2
print(f.__next__())
After Yield i -- 2
Before Yield i -- 3
3
print(f.__next__())
After Yield i -- 3
Before Yield i -- 4
4
print(f.__next__())
After Yield i -- 4
End Foo



---------------------------------------------------------------------------

StopIteration                             Traceback (most recent call last)

<ipython-input-72-2f4b6c2bfb23> in <module>
----> 1 print(f.__next__())


StopIteration: 

Python generator expression

A generator expression is created with round brackets.

aa = (a for a in range(1000)) # this return a generator
ab = [b for b in range(100)] # this return a list
print(next(aa))
print(next(aa))
print(next(aa))
print(next(aa))

print(next(aa))
print(next(aa))
print(next(aa))
print(next(aa))
0
1
2
3
4
5
6
7

Further Reading: http://www.dabeaz.com/generators-uk/

Tuesday, 17 January 2017

Dealing With Missing Values Using Sklearn and Pandas

Real world data sets contain missing values. These values encoded as blanks, NaNs or other placeholders. we are used to skip that entire row/columns containing missing value while describing a machine learning model. However this comes at the price of losing data.

A good strategy is to fill out the missing values using known part of the data. The Imputer class provides basic strategies for imputing missing values, either using the mean, the median or the most frequent value of the row or column in which the missing values are located. Let's Check

first import our module and grab our data 

import pandas as pd

data= pd.read_csv(r'titanic.csv')

Let's Grab some column data which has some missing values. Have a look.

 

 

data['Age'].values[1:30]

Output :

array([ 38.,  26.,  35.,  35.,  nan,  54.,   2.,  27.,  14.,   4.,  58.,
        20.,  39.,  14.,  55.,   2.,  nan,  31.,  nan,  35.,  34.,  15.,
        28.,   8.,  38.,  nan,  19.,  nan,  nan])

Here one can see missing values are represented as 'nan' (nan in Numpy array and NaN in pandas dataframe).Below are some method by which missing value can be handled.

  1. Using sklearn.preprocessing Imputer function

  2. Using Pandas fillna() method

1. Using Sklearn Imputer Function

import imputer class from sklearn.preprocessing and define our imputer. here we will describe missing_value placeholder, strategy used to fill out the value and axis.

from sklearn.preprocessing import Imputer
imp = Imputer(missing_values='NaN', strategy='mean', axis=0)
print imp

Output

Imputer(axis=0, copy=True, missing_values='NaN', strategy='mean', verbose=0)

Here some additional variables axis, verbose are set to default. Now let's fit our data to our Imputer.

imp.fit(data['Age'].values.reshape(-1,1))

Output:

Imputer(axis=0, copy=True, missing_values='NaN', strategy='mean', verbose=0)

Now transform our data replacing Nan with appropriate mean value.

age_reformed= imp.transform(data['Age'].values.reshape(-1,1))

Now Check the transformed data.

print(age_reformed[1:10])

Output:

array([[ 38.        ],
       [ 26.        ],
       [ 35.        ],
       [ 35.        ],
       [ 29.69911765],
       [ 54.        ],
       [  2.        ],
       [ 27.        ],
       [ 14.        ]])

Now these "nan" values has been transformed into mean value(29.69911765). The data after transformation can be used to machine learning model as it doesn't have missing value.

2. Using Pandas fillna()

Python's pandas library provide a direct way to deal with missing value.

First create a new data frame with column value as age.

df= pd.DataFrame(columns=['age'])
df['age']= data['Age'].values

We have missing values in newly created Dataframe.

print(df.head(10))
# output
 	age
0 	22.0
1 	38.0
2 	26.0
3 	35.0
4 	35.0
5 	NaN
6 	54.0
7 	2.0
8 	27.0
9 	14.0

Pandas fillna method is applied to dataframe as

DataFrame.fillna(value=None, method=None, axis=None, inplace=False, limit=None, downcast=None, **kwargs)

Now apply this to our data and put a value equal to 25 where nan occurs.

filled_value=df.fillna(25)
print(fill_value.head(10))

# output
 	age
0 	22.0
1 	38.0
2 	26.0
3 	35.0
4 	35.0
5 	25.0
6 	54.0
7 	2.0
8 	27.0
9 	14.0

Here value in 6th row(5th indexed) has been changed from 'NaN' to 25.0. More information about the default variable can be checked from docs.

Pandas's DataFrame fillan:-

https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.fillna.html

Pandas's Series fillna:-

 https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.Series.fillna.html

Comment and let me know what are you using.

Tuesday, 3 January 2017

Domain name configuration from Github to digital-Ocean

Github is a good place to starting out things. Writing some basic code and putting it on github and checking that on a default URL is pretty cool. The default URL can be changed into a domain name.
When I started my blog, I used Github as a hosting provider. you know you can use it for free using public repository or if you are a student. But Letter I moved to some other hosting provider. Now the major issue was how to configure the domain name setting from github to digital ocean (I chose that). There was a lot of article about how to setup a domain name with digital ocean but none of then describe what to do if you’re moving from github.

Now Let’s check what are the issues and how I solved them. First when you configure a domain name with github, you have to do some configuration like, we have to enter two ip addresses into A record and a CNAME file has to be added which have value equal to your github profile url.
 
I have a apex domain(no www, sometime called root domain or naked domain) so have to configure an ALIAS and A record with my DNS provider.

A record for github :-
While create an A record for github, We have to specify a single IP address that looks like this:-









some time you have to put 2 IP addresses where additional IP address would be 192.30.252.154.

This is explained here on github-help tips-for-configuring-an-a-record-with-your-dns-provider
  and A CNAME(alias) record that looks like this …
This is my godaddy account’s DNS setting for github pages. Now I move from github to Digital ocean. So I have to reconfigure my DNS settings. At first I was terrified that how I would configure this all again. Their are many question are going into my head like setting up a A record, Changing alias record(how?). I have an IP address but don’t know how to setup them plus the guideline to configure DNS setting on digital ocean is bit confusing if you are moving from github. In this case I used the below approach that helped me. I hope it would be helpful for you.
First I erase all the record from DNS setting. It was empty as for new domain. it looks like below:-


Now find the fields called “Domain Name Server”.



Point your name servers to digitalOcean and fill in three domain Name Server fields. Once done, save your changes and exit.
The DigitalOcean Domain servers are:-
After setting up above values it looks like below :


Now go back to digital ocean account, select a droplet for which you want to add your domain name and click on “add a domain” as below.
A new page will opened put your domain name and droplet IP and click on “create record”. And you are done.

These change may take some time to be effective. you can test your new domain by pinging it.

ping website_name

it response would be like this :

pinging website_name [IP Address(Droplet)] with 32 byte of data.