R probably started out "beautiful" and "thought out" but has lost that edge with years of community driven development. It's also what make it so damn useful -- you can pretty much find anything on CRAN, often multiple implementations of it.
R is really nice for some things, because of how easy it is to work with table-like data, and operations like applying a function over a vector or a matrix, etc are cake. However, it's also slow, leaky (and sometime seems to use more memory after gc()), and often difficult to debug because so many things fail silently. If it wasn't for so many standard bioinformatics packages being available only for R, I'd probably be using Python. Once we stop running microarrays, I'll probably be using R much less.
For instance, one of the questions I give in phone screens is for the candidate to write a program to count the number of occurrences of unique words in a text file. The "after writing one or two Python scripts" approach is something like this:
counts = {}
f = open('test.txt')
lines = f.read().split('\n')
for line in lines:
for word in line.split(' '):
if word:
word = word.lower()
if word in counts.keys():
counts[word] += 1
else:
counts[word] = 1
f.close()
count_items = [(count, word) for word, count in counts.items()]
count_items.sort()
for count, word in reversed(count_items):
print word, count
Whereas the "highly proficient" (and much simpler and more Pythonic) approach might look something like this: import collections
counts = collections.Counter()
with open('test.txt') as f:
for line in f:
for word in line.lower().split():
counts[word] += 1
for word, count in counts.most_common():
print word, count lines = [line for line in open("bible.txt")]
words = [word for line in lines for word in line.split()]
counts = {word:0 for word in words}
for word in words:
counts[word] += 1
No imports needed. Linear time. A bit inefficient in the dictionary comprehension, but easy to read.The "lines=" and "words=" can be compressed into one line, but I figure this is a bit easier to read for people who aren't familiar with nested list comprehensions.
lines = [line for line in open("bible.txt")]
words = [word.lower() for line in lines for word in line.split()]
counts = {word:0 for word in words}
for word in words:
counts[word] += 1If you allow one import to sneak in, I think a `defaultdict` would do nicely. :-)
for word, count in Counter(open('test.txt').read().lower().split()).most_common():
print word, count words=open('test.txt').read().lower().split()
for word, count in Counter(words).most_common():
print word, count
(edited per child comment)One nitpick: it's Pythonic (I think) to just name the list of words "words" rather than "word_list".
Personally I always read entire files into memory first unless I have reason to believe memory will be an issue or need to program defensively against malicious/careless input. The code is always much cleaner and easier to read and if you need to do a second pass on the data you don't need to re-read it from disk.
def get_file_words(filename):
file = open(filename, 'rU')
words = {}
for line in file:
for word in [word.lower() for word in line.split()]:
if not word in words.keys():
words[word] = 1
else: words[word]+=1
return words
def print_words(filename):
wordcount = get_file_words(filename)
for word in wordcount:
print word, wordcount[word]open(con <- file('text.txt'))
text = readLines(con, n= -1L) # n is number of lines to read, -1L means read all of it
words = strsplit(text,split = " ")
counts = table(unlist(words))
I put this in because the good thing about R is that it provides functions for many such mathematical operations. And along with this, I'll say something any self-respecting pythoner will know- Less is better than more.
def read_words(words_file):
return [word for line in open(words_file, 'r') for word in line.split()]
len(set(read_words('test.txt')))length(unique(text)) comes from the thought "How many(length()) unique (unique()) words are there in this text(text)?"
length(unique(scan('test.txt',character(),sep=" ")))table(scan('test.txt',character(),sep=" "))
def read_words(words_file):
with open(words_file, 'r') as f:
return {word for line in f for word in line.split()} words = open("test.txt").read().split()
[(k, len(list(g))) for k, g in groupby(words, lambda x: x)] words = open("test.txt").read().split()
[(k, len(list(g))) for k, g in groupby(words, lambda x: x)][1]: Note; I am not insinuating that Lisp is bad, I like it personally. Just relaying the response you will get from most practicing scientists who are not trained as programmers.
Is it that hard of a gap to cross? I was a math major who took a couple CS courses, and wouldn't say I was "trained as a programmer". I found Scheme pretty easy to get from the go.
I tend to think that anyone who can get a PhD in the physical sciences can become a half-decent programmer-- if the desire is there, the intelligence and work ethic being established (one hopes, at least) by the degree.
One language I'm really excited about for scientific computing though is Julia. From a language-design perspective, it's beautiful. It was actually thought out rather than kludged together. I've been trying to gradually use it more and more for my research, but the only problem I've found so far is the large mental context-switch I make going from my usual languages to Julia. It's hard to tell what Julia code will be the most performant because there's many ways of doing the same task. I saw someone in the comments on this page mention that you can hand-tune the LLVM generated output within the REPL itself. I imagine this would be very useful if I can get around to learning it (anyone know a good tutorial?)