YouTube Videos

A Simple Neural Network
KotlinConf 2018 - Mathematical Modeling
Creating a Sudoku Solver from Scratch
Traveling Salesman Problem
Text Categorization w/ Naive Bayes
Monty Hall Problem
Solving World's Hardest Sudoku

Friday, October 28, 2016

Kotlin for Data Science

Can Kotlin be an Effective Alternative for Python and Scala?

As I started diving formally into data science, I cannot help but notice there is a large gap between data science and software engineering. It is good to come up with prototypes, ideas, and models out of data analysis. However, executing those ideas is another animal. You can outsource the execution to a software engineering team, and that can go well or badly depending on a number of factors. In my experience, it is often helpful to do the execution yourself or at least offer assistance by modeling towards production.

Although Python can be used to build production software, I find its lack of static typing causes difficulty in scaling. It does not easily plug in with large corporate infrastructures built on the Java platform either. Scala, although an undeniably powerful JVM language, is somewhat esoteric and does not click with everyone, especially those who do not have a software engineering background or love of expressing code with mathematical flair. But Kotlin, a new JVM language by JetBrains (the creator of Intellij IDEA, PyCharm, and dozens of other developer tools), has an active community, rapid growth and adoption, and might serve as a pragmatic alternative to Scala. Although Kotlin is unlikely to replace Python's numerical efficiency and data science libraries, it might make a practical addition to your toolbelt especially since it works with Spark out-of-the-box. as shown below.

import org.apache.spark.SparkConf
import org.apache.spark.api.java.JavaSparkContext
import kotlin.reflect.KClass
 
fun main(args: Array<String>) {
 
    class MyItem(val id: Int, val value: String)
 
    val conf = SparkConf()
            .setMaster("local")
            .setAppName("Line Counter")
 
    conf.registerKryoClasses(MyItem::class)
 
    val sc = JavaSparkContext(conf)
 
    val input = sc.parallelize(listOf(MyItem(1,"Alpha"),MyItem(2,"Beta")))
 
    val letters = input.flatMap { it.value.split(Regex("(?<=.)")) }
            .map { it.toUpperCase() }
            .filter { it.matches(Regex("[A-Z]")) }
    println(letters.collect())
}
/*extension function to register Kotlin classes*/
fun SparkConf.registerKryoClasses(vararg args: KClass<*>) = 
    registerKryoClasses(args.map { it.java }.toTypedArray()

I'll cover Spark with Kotlin another time, but you can look at this simple GitHub project if you like. Apache Spark is definitely a step in the right direction to close the gap between data science and software engineering, or more specifically, turning an idea into immediate execution. You can use SparkR and PySpark to interface R and Python with Spark. But if you want to use a production-grade JVM language, the only mainstream options seem to be Scala and Java. But as stated Kotlin works with Spark too, as it is 100% interoperable with all Java libraries.

A Comparison Between Python and Kotlin

Let's take a look at a somehwat simple data analysis case study. For now, we will leave Scala and Spark out to only compare Kotlin with Python. What I want to highlight is how Kotlin has the tactical conciseness of Python, and maybe in some ways brings a little more to the table as a language for data analysis. Granted, there are not a lot of mainstream JVM data science libraries other than Apache Spark. But they do exist and perhaps there is room for growth, and maybe the language may be worth keeping your eye on (and even exploring).

This comparison was inspired by the social media example from the first chapter of Data Science from Scratch (Grus, O'Reilly). Let's start with declaring two sets of data, users and friendships. Using simply dicts, lists, and tuples without any classes, this is how it could be done in Python.

Python

users = [ 
    { "id" : 0, "name" : "Hero" }, 
    { "id" : 1, "name" : "Dunn" }, 
    { "id" : 2, "name" : "Sue" }, 
    { "id" : 3, "name" : "Chi" }, 
    { "id" : 4, "name" : "Thor" }, 
    { "id" : 5, "name" : "Clive" }, 
    { "id" : 6, "name" : "Hicks" }, 
    { "id" : 7, "name" : "Devin" }, 
    { "id" : 8, "name" : "Kate" }, 
    { "id" : 9, "name" : "Klein" }, 
]
 
friendships = [ 
    (0,1), 
    (0,2), 
    (1,2), 
    (1,3), 
    (2,3), 
    (3,4), 
    (4,5), 
    (5,6), 
    (5,7), 
    (6,8), 
    (7,8), 
    (8,9)
]

The users is a List of dict items, and the friendships are a List of Tuple items. A feature of dynamic typing is you can be fast-and-loose creating data structures that maintain a raw data-like nature. There is no enforcement to uses classes or explicit types.

The equivalent to doing this in Kotlin would look like this:

Kotlin

val users = listOf(
    mapOf("id" to 0, "name" to "Hero"),
    mapOf("id" to 1, "name" to "Dunn"),
    mapOf("id" to 2, "name" to "Sue"),
    mapOf("id" to 3, "name" to "Chi"),
    mapOf("id" to 4, "name" to "Thor"),
    mapOf("id" to 5, "name" to "Clive"),
    mapOf("id" to 6, "name" to "Hicks"),
    mapOf("id" to 7, "name" to "Devin"),
    mapOf("id" to 8, "name" to "Kate"),
    mapOf("id" to 9, "name" to "Klein")
    )
 
 val friendships = listOf(
        listOf(0,1), listOf(0,2), listOf(1,2), listOf(1,3), listOf(2,3), listOf(3,4),
        listOf(4,5), listOf(5,6), listOf(5,7), listOf(6,8), listOf(7,8), listOf(8,9)
 )

For the friendships, we can actually create Pair<Int,Int> items. Kotlin does not really encourage Tuples (or any collection with differing types) and we will see what it offers instead later with the data class. But let's use Pairs in this example instead using the to operator.

val friendships = listOf(
        0 to 1, 0 to 2, 1 to 2, 1 to 3, 2 to 3, 3 to 4,
        4 to 5, 5 to 6, 5 to 7, 6 to 8, 7 to 8, 8 to 9
)

This may look effective at first glance, and Kotlin is statically typed. It is inferring the type for users and friendships as List<Map<String,Any>> and List<List<Int> respectively. Notice how friendships is a List containing Map<String,Any> items, meaning each item has a String for a key and an Any for the value. The reason for the Any is some values are String and others are Int (due to the "id" and "name"), and because the type is not consistent it cast them back down to Any. If we want to use Hero's "id", we need to cast it back up to an Int for it to be treated like an Int rather than a raw Any.

val herosId = users[0]["id"] as Int

Of course, if an "id" value is slipped in as a String accidentally this would throw an error. You can check if it is an Int, but at this point we are just fighting the statically-typed nature of Kotlin (just like Java and Scala). In Kotlin, we are much better off creating a class and doing things the statically-typed way. While this may make dynamic-typing advocates moan, check this out. Kotlin has a concise, readable way of declaring a class quickly and easily, even exceeding Python's standards

Python

class User(Any):
    def __init__(self, id, name):
        self.id = id
        self.name = name
 
    def __str__(self):
        return "{0}-{1}".format(self.idself.name)
 
users = [ 
    User(0,"Hero"), 
    User(1,"Dunn"), 
    User(2,"Sue"), 
    User(3,"Chi"), 
    User(4,"Thor"), 
    User(5,"Clive"), 
    User(6,"Hicks"), 
    User(7,"Devin"), 
    User(8,"Kate"), 
    User(9,"Klein"), 
]

Kotlin

data class User(val id: Int, val name: String)
 
val users = listOf(
      User(0, "Hero"),
      User(1, "Dunn"),
      User(2, "Sue"),
      User(3, "Chi"),
      User(4, "Thor"),
      User(5, "Clive"),
      User(6, "Hicks"),
      User(7, "Devin"),
      User(8, "Kate"),
      User(9, "Klein")
    )

Not too shabby, right? Technically, we did less typing (as in keyboard typing) than Python (76 characters less to be exact, excluding spaces). And we achieved static typing in the process.

Kotlin is certainly a progressive language compared to Java, and it even has practical features like data classes. We made our User a data class, which will automatically implement functionality typically used for classes holding plain data. It will implement toString() and hashcode()/equals() using the properties, as well as a nifty "copy-and-modify" builder by using a copy() function. (This helps aid flexibility while maintaining immutability, which is valued in software engineering).

Kotlin

data class User(val id: Int, val name: String)
 
val user = User(10,"Tom")
val changedUser = user.copy(name = "Thomas")
 
println("Old user: $user")
println("New user: $changedUser")

OUTPUT:

Old user: User(id=11, name=Tom)
New user: User(id=11, name=Thomas)

NOTE: In Kotlin, val precedes the declaration of an immutable variable. var precedes a mutable one.

Data classes are a valuable tool especially for working with data. And yes, Kotlin supports named arguments for constructors and functions as shown in the copy() function above.

Let's return back to our example. Say we wanted to find the mutal friends between two Users. Traditionally in Python, you would create a series of helper functions to assist in this task.

Python

class User(object):
    def __init__(self, id, name):
        self.id = id
        self.name = name
 
    def __str__(self):
        return "{0}-{1}".format(self.idself.name)
 
users = [ 
    User(0,"Hero"), 
    User(1,"Dunn"), 
    User(2,"Sue"), 
    User(3,"Chi"), 
    User(4,"Thor"), 
    User(5,"Clive"), 
    User(6,"Hicks"), 
    User(7,"Devin"), 
    User(8,"Kate"), 
    User(9,"Klein"), 
]
 
friendships = [ 
    (0,1), 
    (0,2), 
    (1,2), 
    (1,3), 
    (2,3), 
    (3,4), 
    (4,5), 
    (5,6), 
    (5,7), 
    (6,8), 
    (7,8), 
    (8,9)
]
 
def user_for_id(user_id):
    for user in users:
        if user.id == user_id:
            return user
 
 
 
def friends_of(user):
    for friendship in friendships:
        if friendship[0] == user.id or friendship[1] == user.id:
            for other_user_id in friendship:
                if other_user_id != user.id:
                    yield user_for_id(other_user_id)
 
def mutual_friends_of(user, otherUser):
    for friend in friends_of(user):
        for other_friend in friends_of(otherUser):
            if (friend.id == other_friend.id):
                yield friend
 
 
# print mutual friends between Hero and Chi 
 
for friend in mutual_friends_of(users[0],users[3]):
    print(friend)

OUTPUT:

1-Dunn
2-Sue

But we can do something similar in Kotlin. This is our first pass, so I'll show a better way in a moment.

Kotlin

fun main(args: Array<String>)  {
 
    data class User(val id: Int, val name: String)
 
    val users = listOf(
        User(0,"Hero"),
        User(1, "Dunn"),
        User(2, "Sue"),
        User(3, "Chi"),
        User(4, "Thor"),
        User(5, "Clive"),
        User(6, "Hicks"),
        User(7, "Devin"),
        User(8, "Kate"),
        User(9, "Klein")
        )
 
    
    val friendships = listOf(
            0 to 1, 0 to 2, 1 to 2, 1 to 3, 2 to 3, 3 to 4,
            4 to 5, 5 to 6, 5 to 7, 6 to 8, 7 to 8, 8 to 9
    )
 
    fun userForId(id: Int): User {
        for (user in users)
            if (user.id == id)
                return user
        throw Exception("User not found!")
    }
 
    fun friendsOf(user: User): List<User> {
        val list = mutableListOf<User>()
        for (friendship in friendships) {
            if (friendship.first == user.id)
                list += userForId(friendship.second)
            if (friendship.second == user.id)
                list += userForId(friendship.first)
        }
        return list
    }
 
    fun mutualFriendsOf(user: User, otherUser: User): List<User> {
        val list = mutableListOf<User>()
        for (friend in friendsOf(user))
            for (otherFriend in friendsOf(otherUser))
                if (friend.id == otherFriend.id)
                    list += friend
 
        return list
    }
 
    for (friend in mutualFriendsOf(users[0],users[3]))
        println(friend)
}

OUTPUT:

User(id=1, name=Dunn)
User(id=2, name=Sue)

Although Kotlin seems to have lost in this example by being wordier and resorting to Lists, hold on. Kotlin has no direct concept of generators and yield keywords. However, we can accomplish something that fulfills the same purpose (and is arguably stylistically better) through Sequence.

fun main(args: Array<String>)  {
 
    data class User(val id: Int, val name: String)
 
    val users = listOf(
        User(0,"Hero"),
        User(1, "Dunn"),
        User(2, "Sue"),
        User(3, "Chi"),
        User(4, "Thor"),
        User(5, "Clive"),
        User(6, "Hicks"),
        User(7, "Devin"),
        User(8, "Kate"),
        User(9, "Klein")
        )
 
    
    val friendships = listOf(
            0 to 1, 0 to 2, 1 to 2, 1 to 3, 2 to 3, 3 to 4,
            4 to 5, 5 to 6, 5 to 7, 6 to 8, 7 to 8, 8 to 9
    )
 
    fun userForId(id: Int) = users.asSequence().filter { it.id == id }.first()
 
    fun friendsOf(user: User) = friendships.asSequence()
            .filter { it.first == user.id || it.second == user.id }
            .flatMap { sequenceOf(it.first,it.second) }
            .filter { it != user.id }
            .map { userForId(it) }
 
    fun mutualFriendsOf(user: User, otherUser: User) = friendsOf(user).flatMap { friend ->
        friendsOf(otherUser).filter { otherFriend -> otherFriend.id == friend.id }
    }
 
    mutualFriendsOf(users[0],users[3]).forEach { println(it) }
}

OUTPUT:

User(id=1, name=Dunn)
User(id=2, name=Sue)

We can use the Sequence to compose a series of operators as a chain, like filter(), map(), flatMap(), and many others. This style of functional programming has been getting a lot of traction over the years thanks to LINQ, primarily because it easily breaks up logic into simple pieces and increases maintainability. 99.99% of the time, I am never using for loops but rather using a Kotlin Sequence, a Java 8 Stream, or an RxKotlin/RxJava Observable. This chain-operator syntax is becoming less alien in Python as well (look at PySpark and RxPy). What is great about this style of programming is you can read what is happening left-to-right, top-to-bottom rather than jumping through several loops and helper functions.

Conclusions

In the coming months, I am going to blog about my experiences using Kotlin for data science, and I will continue to share what I learn. I may throw in an article occasionally covering ReactiveX for data science as well (for both Kotlin and Python). I acknowledge that the Java JVM platform, which Kotlin runs on, does not handle numbers as efficiently as Python or R (maybe Project Valhalla will change that?). But successful models inevitably need to turn into execution, and the Java platform increasingly seems to be the place that happens.

Kotlin merely provides a pragmatic abstraction layer that provides a tactical and concise syntax that seems excellent not just for data analysis, but also executing software. Outside of data science, Kotlin has spurred many successful open-source libraries even before a year after its release. One library, TornadoFX, allows rapid turnaround of complex business user interfaces using Kotlin (As a disclaimer, I have helped with that project). The Kotlin community is active, growing, and engaged on the Slack channel. It continues to be adopted on Android as well as backends, and JetBrains is using Kotlin to build all their tools (including PyCharm and Intellij IDEA). It is also replacing Groovy as the official language for Gradle. Because of these facts, I do not see Kotlin's momentum slowing down anytime soon.

I believe Kotlin could make a great first JVM language, more so than Java or Scala (I struggle to make Jython count). If you are already happy with Scala or Java you will likely not need Kotlin. But for folks wanting to break into JVM languages, there is a new O'Reilly video series that covers Kotlin from scatch. Its instructor Hadi Hariri (one of the JetBrains folks behind Kotlin) believes Pythonistas should be able to follow along. He said anybody familiar with classes, functions, properties, etc should be able to learn Kotlin in a day with this video series. Unfortunately, the existing Kotlin documentation and books assume prior Java knowledge, and hopefully more resources other than the video pop up in the future.

There is a lot of exciting features I have not covered about Kotlin in this article. Features like nullable types, extension properties and functions, and boilerplate-free delegates make the language pleasant to use and productive. So check out Kotlin if you are using Python for data science and wanting to learn a JVM language. Again, this is not a proposal to drop your current tools, but rather consider exploring an additional one that may help you tackle new problems. I will continue blogging about my experiences with Kotlin, and showcase it being used in deeper data science topics as well as Spark.

66 comments:

  1. > Scala, although an undeniably powerful JVM language, is rather esoteric and inaccessible for those who do not have a software engineering background

    Just plain false

    Your title is misleading, you don't actually compare Scala

    ReplyDelete
    Replies
    1. I'll grant I have not been explicit who I was writing this article for: Python developers who struggled to adopt Scala or Java. That's why I was comparing the syntax to Python. If you are a Scala developer, you have plenty of expertise to research Kotlin. And if you like Scala, you have little need to read this article.

      Personally I tried to learn Scala about a year ago and I just couldn't "get it", even as a Java developer. It's too math-driven for me and a lot of Kotlin developers have similar sentiments. But a good platform like the JVM has something for everyone, right? :)

      Delete
    2. Comparison of Scala to Kotlin: https://kotlinlang.org/docs/reference/comparison-to-scala.html

      Delete
    3. Your observation that Pythonistas might find JVM style typing cumbersome is pretty fascinating to me since I came from the reverse direction, moving from C/C++ to Java to Python, which I tend to love precisely for its loose typing. It DOES support type wrapping, which can keep most unexplained behavior in check (especially around any math operations or string manipulation).

      There are plenty of other reasons to use JVM variants for enterprise development, mostly for their approach to private functions and robust threading support - how does Kotlin compare there?

      Delete
    4. Hey Eric. Yes you are right on all points. I like Python and am using it weekly with RxPy. Python is currently the right tool for many tasks due to its data science libraries. But there are other benefits though to JVM and Kotlin in the enterprise. Kotlin has aptly been described as "Scala for dummies", meaning it has a pragmatic syntax and set of language features designed for industry, but removes a lot of what people find to be academic and esoteric. It has full concurrency support since it runs on the JVM, and (although I use Rx for concurrency) there will be support for async/await/yield operations soon.

      https://blog.jetbrains.com/kotlin/2016/04/kotlin-post-1-0-roadmap/

      I see you sent a message to me. I'll follow up later today and get back.

      Delete
  2. Why are you comparing character counts? If that matters to you, use Perl and single char varnames.

    But, in the example when you do it for Python, since it's a criteria of yours: you may want to consider a named tuple.

    >>> Person = namedtuple("Person", "fname lname age")
    >>> Person("Fishy", "McFishface", 34)
    Person(fname='Fish', sname='McFishface', age=34)

    ReplyDelete
  3. I'm afraid I don't follow your first question regarding counts. And yeah, you can use named tuples for sure. As a Java dev they just feel odd since I can create a class, although I see your point in that it yields less boilerplate.

    ReplyDelete
  4. Really very happy to say,your post is very interesting to read.I never stop myself to say something about it.You’re doing a great job.Keep it up.data analytics courses in pune
    data science course in pune

    ReplyDelete
  5. Thank you for your post. This is superb information. It is amazing and great to visit your site.
    Big Data Training in Noida

    Big Data Course in Noida

    Big Data Training institute in Noida

    ReplyDelete
  6. Thank you for your post. This is excellent information. It is amazing and wonderful to visit your site.
    mobile application training in hyd
    Apple iOS Training Institutes in Hyderabad

    ReplyDelete
  7. Happy to peruse your useful post, continue sharing profitable data! Anticipating seeing your notes posted.
    Data Science Course In Pune

    ReplyDelete
  8. Such an ideal piece of blog. It’s quite interesting to read content like this. I appreciate your blog
    Data Science online Training

    ReplyDelete
  9. If you are looking for Self learning, Distance Education Courses are becoming increasingly popular as a mode of education and are being utilized by professionals and students from around the world, there are numerous possibilities for distance learning courses. Just go through this link:-

    Distance Education Courses ,

    ReplyDelete
  10. Are you looking for Distance Learning Courses in India most of the students choose and apply, Talentedgenex there are many popular courses which attract the students for having distance education. For more info visit this site:- Distance learning courses in India ,

    ReplyDelete
  11. Talentedgenext Way of Online Learning, Distance Education, is an increasing number of becoming popular all over the world due as it has many benefits. For further details visit in this site:- Distance Education Website,

    ReplyDelete
  12. Are you looking to apply for Online BBA course in India as distance education courses awareness has been rising in India? Most the students choose online learning because of digitization and easy availability of the internet hence, students have moved to distance courses.
    Online BBA course in India,

    ReplyDelete
  13. This is an awesome post.Really very informative and creative contents. These concept is a good way to enhance the knowledge.I like it and help me to development very well.Thank you for this brief explanation and very nice information.Well, got a good knowledge.
    msbi online training

    ReplyDelete
  14. Thanks for this information. This information is really given incredible knowledge. Search Engine Optimization generating create organic traffic for your website. If anyone searching for Best SEO Services Dubai. Visit my profile

    ReplyDelete
  15. This is the perfect blog for everyone who really wants to find out about this topic. Thanks for sharing such good information.If anyone searching for the best washing machine repair service in Dubai. visit my profile.

    ReplyDelete
  16. Nice post. By reading your blog, i get inspired and this provides some useful information. Thank you for posting this exclusive post for our vision. 

    ReactJS Online Training

    ReplyDelete
  17. It is amazing and wonderful to visit your site.Thanks for sharing this information,this is useful to me...

    Tableau online training

    ReplyDelete
  18. TV Repair Dubai
    If you are looking for Tv repair we provide best Tv repair service with customer satisfaction .We have a good team of Experience technician. Our main objective is to provide good technical support and quality service as per our commitment. We also provide services in Dubai as well.For more Details you can visit our website.

    ReplyDelete
  19. DNS Server Not Responding
    How to fix the not responding error on Word? Method 1: Go to start menu Click on Start, point to All Programs, and then point to Microsoft Office. Then press and hold the Ctrl key, and click on the Microsoft Office Word program.

    ReplyDelete
  20. A universal message I suppose, not giving up is the formula for success I think. Some things take longer than others to accomplish, so people must understand that they should have their eyes on the goal, and that should keep them motivated to see it out till the end.
    Sql server dba online training

    ReplyDelete
  21. Good Post! Thank you so much for sharing this pretty post, it was so good to read and useful to improve my knowledge as updated one, keep blogging..
    Oracle DBA Online Training

    ReplyDelete
  22. Really It is very useful information for us. thanks for sharing.
    Machine Learning Training in Hyderabad

    ReplyDelete
  23. This comment has been removed by the author.

    ReplyDelete
  24. But as stated Kotlin works with Spark too, as it is 100% interoperable with all Java libraries!!!

    ReplyDelete
  25. Thanks for the sharing information, it's great to see some of the nice new features but also some resolution to the pain points both within Spring and Kotlin

    ReplyDelete
  26. Excellent post.Thanks for sharing the fantastic post.

    ReplyDelete
  27. Thanks for provide great informatic and looking beautiful blog, really nice required information & the things i never imagined and i would request, wright more blog and blog post like that for us. Thanks you once agianMarriage certificate in delhi
    Marriage certificate in ghaziabad
    Marriage registration in gurgaon
    Marriage registration in noida
    special marriage act
    Marriage certificate online
    Marriage certificate in mumbai
    Marriage certificate in faridabad
    Marriage certificate in bangalore
    Marriage certificate in hyderabad thanks once again to all.

    ReplyDelete
  28. Thanks for provide great informatic and looking beautiful blog, really nice required information & the things i never imagined and i would request, wright more blog and blog post like that for us. Thanks you once agian

    special marriage act
    name add in birth certificate
    passport agent
    court marriage in delhi
    name change
    marriage registration
    birth certificate in gurgaon
    birth certificate in noida
    birth certificate in ghaziabad
    birth certificate in delhi

    ReplyDelete
  29. Thanks for these valuable things put in this blog. Visit OGEN Infosystem for Website Designing Company and SEO Service in Delhi.
    Web Designing Company

    ReplyDelete
  30. Hello dear,
    It is amazing and very interesting to visit your blog.Thanks for sharing this information.

    ReplyDelete
  31. Hello!
    Data science could be a multi-disciplinary field that uses scientific ways, processes, algorithms, and system to extract data and insights from structured and unstructured information.

    ReplyDelete
  32. Thanks you so much for your efforts blog, If your are looking for a creative mobile app development in mumbai, Appslure provides one of the best and professional app development company in mumbai.
    Mobile app development company in mumbai

    ReplyDelete
  33. Really very nice blog information for this one and more technical skills are improve,i like that kind of post.
    Best Data science training in Vijayawada

    ReplyDelete

  34. Nuvigil smart drug is the trade name for Armodafinil smart drug and is the enantiopure compound of Modafinil. There are a number of health benefits that the use of Nuvigil smart drug has to offer to its users. So, you can buy Nuvigil online. The use of the smart drug is safe and has no health issues attached to its usage. In the year 2006, the FDA approved the use of generic Nuvigil smart drug to be used to treat a number of mental issues. Buy Nuvigil online as the use of Nuvigil smart drug has long effect when compared to other smart drugs. This is mainly because Armodafinil smart drug has a longer half-life than the other smart drugs. Therefore more and more people are depending on Nuvigil smart drug to carry on their daily routine smoothly.
    Buy Nuvigil online

    ReplyDelete
  35. Really very nice blog information for this one and more technical skills are improve,
    Best Data science training in Vijayawada

    ReplyDelete
  36. Thank you for providing such an awesome article and it is very useful blog for others to read.
    Big Data Training In Delhi

    ReplyDelete
  37. Very Good Post! Thank you so much for sharing this nice post, it was so good to read and useful to improve my Technical knowledge as updated one, keep blogging.
    Scala and Spark Training in Electronic city

    ReplyDelete
  38. Nice information you have given here.It is useful to us.You are doing a great job.Keep it up.
    Data Science Course in Mumbai

    ReplyDelete
  39. I like your post very much. It is very much useful for my research. I hope you to share more info about this. Keep posting Apache Spark Certification

    ReplyDelete
  40. Great! Nice article Thanks for giving this good information...
    Tableau Training In Hyderabad

    ReplyDelete
  41. It is very good blog with full information, we are looking forward to connect with you in future. Best home tutors are provided by TheTuitionTeacher in Delhi and Lucknow.
    Home Tutors in Delhi | Home Tuition Services

    ReplyDelete
  42. I am a constant reader of blogs. I have read this whole blog and it is an amazing blog for developers who are dealing daily with the new challenges and tasks.
    Thank You for sharing such a valuable, informative and useful thoughts for users in your blog. app development company in bhopal
    Top It companies in Bhopal
    Companies in MPNagar Bhopal

    ReplyDelete