Randomly split the rows of an SFrame into two SFrames. The first SFrame
        contains *M* rows, sampled uniformly (without replacement) from the
        original SFrame. *M* is approximately the fraction times the original
        number of rows. The second SFrame contains the remaining rows of the
        original SFrame. 
        
        An exact fraction partition can be optionally obtained by setting 
        exact=True.

        Parameters
        ----------
        fraction : float
            Fraction of the rows to fetch. Must be between 0 and 1.
            if exact is False (default), the number of rows returned is
            approximately the fraction times the number of rows.

        seed : int, optional
            Seed for the random number generator used to split.

        exact: bool, optional
            Defaults to False. If exact=True, an exact fraction is returned, 
            but at a performance penalty.

        Returns
        -------
        out : tuple [SFrame]
            Two new SFrames.

        Examples
        --------
        Suppose we have an SFrame with 1,024 rows and we want to randomly split
        it into training and testing datasets with about a 90%/10% split.

        >>> sf = turicreate.SFrame({'id': range(1024)})
        >>> sf_train, sf_test = sf.random_split(.9, seed=5)
        >>> print(len(sf_train), len(sf_test))
        922 102

def random_split(self, fraction, seed=None, exact=False):
        """
        Randomly split the rows of an SFrame into two SFrames. The first SFrame
        contains *M* rows, sampled uniformly (without replacement) from the
        original SFrame. *M* is approximately the fraction times the original
        number of rows. The second SFrame contains the remaining rows of the
        original SFrame. 
        
        An exact fraction partition can be optionally obtained by setting 
        exact=True.

        Parameters
        ----------
        fraction : float
            Fraction of the rows to fetch. Must be between 0 and 1.
            if exact is False (default), the number of rows returned is
            approximately the fraction times the number of rows.

        seed : int, optional
            Seed for the random number generator used to split.

        exact: bool, optional
            Defaults to False. If exact=True, an exact fraction is returned, 
            but at a performance penalty.

        Returns
        -------
        out : tuple [SFrame]
            Two new SFrames.

        Examples
        --------
        Suppose we have an SFrame with 1,024 rows and we want to randomly split
        it into training and testing datasets with about a 90%/10% split.

        >>> sf = turicreate.SFrame({'id': range(1024)})
        >>> sf_train, sf_test = sf.random_split(.9, seed=5)
        >>> print(len(sf_train), len(sf_test))
        922 102
        """
        if (fraction > 1 or fraction < 0):
            raise ValueError('Invalid sampling rate: ' + str(fraction))
        if (self.num_rows() == 0 or self.num_columns() == 0):
            return (SFrame(), SFrame())

        if seed is None:
            # Include the nanosecond component as well.
            seed = abs(hash("%0.20f" % time.time())) % (2 ** 31)

        # The server side requires this to be an int, so cast if we can
        try:
            seed = int(seed)
        except ValueError:
            raise ValueError('The \'seed\' parameter must be of type int.')


        with cython_context():
            proxy_pair = self.__proxy__.random_split(fraction, seed, exact)
            return (SFrame(data=[], _proxy=proxy_pair[0]), SFrame(data=[], _proxy=proxy_pair[1]))