Wednesday, April 23, 2025

First Day at Work: Orienting Yourself by Matching Abstract Actions to Concrete Tools

On October 2024 I entered a Software Engineering position on a new job. This was one of the first things I jotted on the yellow spiral bound pad I brought along with me.

 

To orient myself, I made a sort of table with all the common stuff I do in my day-to-day, and what they're using in the new place as opposed to my former workplace.

Putting this data into a table, you would see something like this:

Action

Old Workplace

New Workplace

Messaging

SlackZoom

Video Meetings

Google Meet, sometimes ZoomZoom (even though Google Meet is available!)

Email

GmailGmail

Documents

Google DocsMicrosoft Office over Dropbox

Wiki

Started with a self-hosted mediawiki (with a git backend!), eventually migrated to Notion.Wiki built into Redmine

Tickets

JiraRedmine + Trello

CI/CD

Proprietary solution on top of Jenkins, started moving to Argo CD around the time I left.Jenkins

CVS

Started with a self-hosted GitLab, moved to GitHub.com .SVN built into Redmine!
I kid you NOT!

Tooling differences between workplaces reveal something.

On one hand, work can feel pretty much the same across workplaces. "Here I am, sending messages in a public Slack channel about my Python code... again!".

On the other hand, each place has its own way of doing things ("this is how we do it here").

Bad places stick to titles: "We are practicing Agile and work in Sprints."

Good places keep the Abstract and Concrete separate: "Here is how we do Agile here - perhaps differently from how you did it in your previous place; you know how it is...."

Moreover, tools are not interchangeable, and you need to learn the differences in order to become effective.

An obvious example is SVN vs. git: if you're used to one of them, switching to the other is going to be annyoing.

A less obvious example is Zoom vs. Slack; I did not imagine Zoom will be missing so many basic features, such as inline code in a message's body (`foo` automatically becoming foo was only added on the December 16, 2024 release).

Sometimes an available feature isn't used. My team used to write Markdown documents rendered to HTML with **asteriks for headlines**. Using ## hashes for headlines prompts the rendering to include an anchor, making it easier to post a link to a specific section of the document. Slowly but surely, by setting an example rather than forcing it, I'm making the usage of headers more common.

For fun and profit, one should:

  • Identify the abstract Actions you would like to perform, drilled-down as deep (or shallow) as you choose:
    • "I want to work with git, and nothing but git."
    • "I want my messaging platform to support code blocks."
    • "I want my automatic anchor rendering for my documents' headlines."
  • Identify which of these Actions can be concretely performed by the tools available in the organization right now...
  • ...and try to close or at least note the gaps, ideally bringing in the new tools if possible (considering licensing, budget, security, legal, etc.).
  • It goes both ways, of course: identifying the actions the organization is routinely performing, finding or generating an official description for them and learning them is one of your major goals as a team member.
Sometimes the organization itself isn't too worried about everyone being on the same page about what's being done. And that, I believe, is where trouble slowly but surely enter your work-life.


Sunday, August 13, 2023

Expectations from an SRE

(In the words of my interviewer - I didn't pass)

Generally for an SRE-type of engineer I expect a T-shaped like skillset.

So there is a minimum baseline that has to be met across these categories:

  1. Linux fundamentals
  2. Software Engineering skills
  3. Networking
  4. Security best practices
  5. Containers & container orchestration
  6. Troubleshooting various scenarios related to above
  7. Distributed systems & scalability & reliability
  8. Understanding of cloud-based providers
  9. Infrastructure-as-code
So that's the baseline.

Beyond that, at higher levels, folks will be expected to have a much more in-depth skillset in either one of the above categories (like Cloud or IaC or very large scale, etc) and/or depth in a particular domain like build systems, platform building on k8s or the like, etc.

(so perhaps this blog would pivot towards exploring these concepts as I'd try to learn those? Who knows!)

Tuesday, December 20, 2022

lambda functions (=anonymous functions)

You can't import them,

so you cannot unit-test them,

so you cannot reason about them.

Perhaps they are small and inconsequential.

But perhaps they are not.

#polkadot

Monday, April 4, 2016

GraphLab Create via PyCharm

People who want to start using GraphLab Create usually ask two questions:

1) Where do I get GraphLab from?
2) What IDE should I use in my Python projects?

The first answer is to use the Dato Launcher. This is a bundle of Python, GraphLab Create and IPython Notebook. IPython Notebook is the best IDE for data scientists, and is also what we use in the Coursera Machine Learning Foundations course.

The second answer is JetBrains' PyCharm, which is becoming the de-facto standard Python IDE. This is what we use in big projects which also require lots of debugging.

To make PyCharm use the Python bundled in the Dato Launcher, follow the following instructions, which are detailed and include lots of screenshots.

You can download this PDF version of the instructions, which is included in a single file, or the HTML version which is zipped. Unzip all files to see the pictures as well.

This guide was originally written in MarkDown using the Mou editor.


Saturday, October 3, 2015

7 Links To Convince You That Big Data Isn't Your Problem

tl;dr - scroll to the bottom for a list of 7 links you should can read instead of this entire post.

I'll spare you the big bulk of my words and get to the point:

From my personal experience with big data projects, I'll conclude that it's a big (data) fat lie (or a big fat [data] lie if you'd like). Most people don't have big data to begin with, and they use big data technologies for the saying 'we use big data in our product' to acquire more sales. Below is the story in a set of links: read through this links, while bearing in mind the key points I attached to each one of them. I hope that by the time you finish reading this, you'll become more convinced in this point.

And yes, big data technologies still have a place in today's world: buzzwords have always been a part of business, and somebody has to purchase all of these disks and RAM and CPU (packed nicely into "commodity servers"). But if you are a penniless start-up worrying about how will you close the gap and get in the game, rest assured. The game is mostly imaginary - as always!

I dedicate this post to my friend Rony, an economist who asked me for tips on how to incorporate "big data" into her research. A short set of questions proved to me that she was not aware of what big data really is, and that it wasn't relevant for her case. She would use computers, programming and databases - but she should not worry that she is missing any knowledge. So for people like her, whose fears get fed by the same legends big data salespeople spread about it to get more cash, I dedicate this post.

The Beginning: Google Gets There First (as always!)

I mark the beginning of the era of 'big data' with two research papers published their Google.

The first paper described the Google File System (GFS) and you can (and should!) read it here:
http://static.googleusercontent.com/media/research.google.com/en//archive/gfs-sosp2003.pdf
This is a file system that specializes in scalability, in appending incoming data in mass volumes into existing files, and in making high-availability systems (due to redundancy of data). These were all valid concerns relevant for Google's business - but not relevant for just any company.

The second paper described the MapReduce paradigm:
Basically that means that if I have a book and I want to count how many times each word appears in it - I can split it to several chunk, count the words separately in each chunk, and then combine my results. The split+count part is called MAP, the combine part is called REDUCE. This is the nuttiest of all nutshells explanations, but is enough to get some few points across. First, this paradigm fits for batch processing of rather homogenous data. Second, it doesn't fit non-aggregative batch processing tasks. An example is Graph Algorithms - looking at a node in a graph, and retrieving its friends, its friends' friends... etc - is something you don't normally do using MapReduce.

These two concepts are combined into a single system like this: the data is stored using a GFS-like filesystem, then processed using the MapReduce paradigm (the more general term is 'lambda architectures'). Google published these papers but did not release any code. But Doug Cutting and Mika Cafarella, two engineers from Yahoo, decided to implement such a system as part of their work in Yahoo. This became an open-source Apache project named Hadoop. So - Hadoop - open-source GFS+MapReduce. Hadoop became the generic name for a big data processing system and is used widely in academia and industry. But enough about Hadoop at this point.

You might notice that these papers are from 2003 and 2004. Google itself has already got past that point and is using some other technologies which don't have open sources implementations out yet. You can read more about how Hadoop is no longer the cutting-edge big data framework here:


The Disillusionment: Where Big Data Fails in General, and Hadoop fails Specifically

Instead of presenting my own opinion, please look at this KD Nuggets poll:
When asked what is the largest dataset analyzed, data scientists (the people working on analyzing data) respond that a laptop is usually enough for their needs.

(I used to link the previous year's poll when I noticed a new version came out. Still, here it is, you can compare each year's results to see the stability:
http://www.kdnuggets.com/polls/2014/largest-dataset-analyzed-data-mined-2014.html
)

Why is that? For two reasons. The first one is that big data is not always necessary to develop a good data-driven application. Let's say we are trying to create personalized messages for e-commerce websites. Is the entire history of all users necessary? Maybe just the last 3 months? How much do they weight? Is a sample of the data enough for building a good model (rather than the entire corpus)? Is the entire corpus tagged (labelled) so that we can use it for building our model? This is not always the situation. Quantity can not always cover for quality.

The second reason is that most big data systems are aimed at being scalable. That means that such a system deployed on two machines (working together on the computations) will perform better than the same system deployed on a single machine (and it goes on: 3 machines are better than 2, 4 are better than 3... etc). The more machines also allow for robustness - if one of the machines die in a Hadoop cluster, the others can pick up from where it left and finish the job. So big data systems are usually scalability-oriented. Nice!

But do they perform better than a non-scalability-aimed, single-thread based system?

Frank McSherry et. al checked this subject and returned with surprising results. Read their paper here:
Frank McSherry, Michael Isard, Derek G. Murray
Scalability! But at what COST?
http://www.frankmcsherry.org/assets/COST.pdf

A shorter reading would be McSherry's blog-post about this paper:
http://blog.acolyer.org/2015/06/05/scalability-but-at-what-cost/

And also his follow-up post about testing the single laptop on even bigger data:
http://www.frankmcsherry.org/graph/scalability/cost/2015/02/04/COST2.html
For me, this set of publications is what finally took the hot air out of the big data balloon. Most people don't have big data and won't benefit (performance-wise, not business-sexiness-wise) from using those systems. That's what I see when I read the links posted above.

The Truth: Where is Big Data Used Successfully

So is it completely useless to analyze large amounts of data? As I mentioned above - that depends on your goals. Here is an opposite opinion - a positive use-case for using big data:

http://www.ft.com/cms/s/0/304b983e-5a44-11e5-a28b-50226830d644.html
Charles Clover, China’s Baidu searches for AI edge, Financial Times (September 14, 2015 4:24 am)

Here is the (probably copyrighted - sorry about that!) most important part in my opinion:
The company has an advantage in deep-learning algorithms for speech recognition in that most video and audio in China is accompanied by text — nearly all news clips, television shows and films are close-captioned and almost all are available to Baidu and Iqiyi, its video affiliate. 
While a typical academic project uses 2,000 hours of audio data to train voice recognition, says Mr Ng, the troves of data available to China’s version of Google mean he is able to use 100,000 hours. 
He declines to specify just how much the extra 98,000 hours improves the accuracy of his project, but insists it is vital. 
“A lot of people underestimate the difference between 95 per cent and 99 per cent accuracy. It’s not an ‘incremental’ improvement of 4 per cent; it’s the difference between using it occasionally versus using it all the time,” he says.
So in this domain (speech recognition), using this algorithm (deep learning), big data of this high quality (closed captions) is successfully used. This also works well with image recognition done in neural networks over large corpora of tagged images. Google and Facebook have such datasets and they do it successfully. But not every company is Google or Facebook. Are you?

Conclusion

Before you get confused by big-data, please focus on the following points:

Big Data Systems are usually a combination of scalable file systems and scalable processing architectures. The household name in this field is Hadoop, which is based on these two Google papers:
(although Google don't use these technologies nowadays:

Most real data scientists admit they don't really use big data...

... and new evidence shows that these systems are also not very efficient:
Paper by McSherry:
4) http://www.frankmcsherry.org/assets/COST.pdf
Two blog posts he made about this paper:

Big data is sometimes useful - here's an example. Understand whether you are in such a situation:
If you are not - don't feel bad. You're in a good company :)

The Fine Print

I currently work for Dato, the creator of GraphLab Create. Our product is famous for analyzing massive datasets over a single laptop or a single machine. Nevertheless, these opinions do not represent Dato. Also, I got these opinions from experimenting with GraphLab Create, and seeing for myself that good data science can be done over less than petabytes, and in a single machine.

Monday, July 27, 2015

Neural Networks and Deep Learning by Michael Nielsen

1) The best deep learning tutorial I've came across in the last few months is here:

Neural Networks and Deep Learning by Michael Nielsen
http://neuralnetworksanddeeplearning.com/index.html
Read it now! This post is just an attempt to add one more link in the web to this great book.


2) The best deep learning Python (what else??) library I came across is Keras .

http://keras.io/


3) Everyone's favourite deep learning post of the moment is Karpathy's RNN:
http://karpathy.github.io/2015/05/21/rnn-effectiveness/

But imho you should read Yoav Goldberg's response:
http://nbviewer.ipython.org/gist/yoavg/d76121dfde2618422139


And that's it for today.

Tuesday, June 2, 2015

python2.7 + graphlab create on centos

CentOS is notorious for using yum as its package manager (the equivalent of Ubuntu's 'apt-get'). Yum is dependent on the Python version that ships with CentOS, so upgrading to python 2.7 simply kills yum and breaks the system updates. Here's how to solve this.


Long story short: You can follow the instructions here:

https://github.com/h2oai/h2o-2/wiki/Installing-python-2.7-on-centos-6.3.-Follow-this-sequence-exactly-for-centos-machine-only

Or simply use my gist based on this post and my previous post:

https://gist.githubusercontent.com/guy4261/0e9f4081f1c6b078b436/raw/bfded5dd1c53284a83872bc61444511e02d99212/python2.7.9_centos_installation.sh

Monday, May 18, 2015

Learning Torch7 - Part 2: The Basic Tutorial

From this part of the page:
https://github.com/torch/torch7/wiki/Cheatsheet#newbies

I got to the tutorials part:
https://github.com/torch/torch7/wiki/Cheatsheet#tutorials-demos-by-category

And to the first tutorial:
http://code.madbits.com/wiki/doku.php

And to its first part, about the basics:
http://code.madbits.com/wiki/doku.php?id=tutorial_basics

The tutorial is straightforward, but ends with a request to normalize the mnist train data.

This is the code I came up with after a very painful realization.

The realization is what you see on line 4:

train.data = train.data:type(torch.getdefaulttensortype())


It appears that the data is loaded by torch not in tables or torch tensors but in something called "userdata". This is the type assigned to things coming from the dark depths of C / C++ interops with Lua. This userdata had a particular interesting feature: its contents were forced (through wrapping) to be in the range [0, 256) . So storing -26, for instance, would result in the value 256 - 26 = 230 to appear in the data. So casting was the first step to gain back my sanity in this case.

After casting back to torch tensors can you use :mean(), :std() and other tensor methods which make this code short and quick.

By the way - this normalization is called "z-score": http://en.wikipedia.org/wiki/Standard_score
I didn't quite catch the right way to describe the whole act in proper English, but that's what we are doing here (subtracting the mean and dividing by the standard deviation).

More useful things learned along the way:

for k,v in pairs(o) do -- or pairs(getmetatable(o))
   print(k, v)
end

is the equivalent of Python's dir(o) .

I also started working with this IDE: http://studio.zerobrane.com/

Moving on with the tutorials, the supervised learning will be my next post's subject.

Learning Torch7. Part 1

(part 1, aka: "The First Circle")

My entry point is this:
https://github.com/torch/torch7/wiki/Cheatsheet#newbies

I've read this page cover-to-cover and obtained a machine on which torch was installed. Installing it sounds like a nightmare and I wonder if the performance over a docker container would be the same, making the installation seem like a bad dream. So - read is step 1, installation is step 2.

Step 3 is according to the Newbies training course is to Learn Lua in 15 Minutes:
http://tylerneylon.com/a/learn-lua/

Notable comments:
  • nil is the local None/null/void/undefined.
  • do/end wrap blocks (just like { and } would in other languages)
  • There is no ++, += operators, so  n = n+1  is the way to go.
  • == , ~= for equal/nonequal test.
  • .. is string communication
  • Anything undefined evaluates to nil. (So you can type 'lkgjadsgjdas' into the interpreter without getting an error).
  • Only nil and false are considered false. 0 is true!
How to create tables:
-- Literal notation for any (non-nil) value as key: t = {key1 = 'value1', key2 = false}
u = {['@!#'] = 'qbert', [{}] = 1729, [6.28] = 'tau'} print(u[6.28]) -- prints "tau"
Matching keys within tables: -- Key matching is basically by value for numbers -- and strings, but by identity for tables. a = u['@!#'] -- Now a = 'qbert'. b = u[{}] -- We might expect 1729, but it's nil: -- b = nil since the lookup fails. It fails -- because the key we used is not the same object -- as the one used to store the original value. So -- strings & numbers are more portable keys.

Python dir() equivalent
print(_G) ~~ dir()

Using tables as lists/arrays:
-- List literals implicitly set up int keys:
v = {'value1', 'value2', 1.21, 'gigawatts'} for i = 1, #v do -- #v is the size of v for lists. print(v[i]) -- Indices start at 1 !! SO CRAZY! end -- A 'list' is not a real type. v is just a table -- with consecutive integer keys, treated as a list.

The other meaningful parts, to be read thoroughly in the tutorial, are the metatables parts.

Wednesday, May 13, 2015

pyspark + ipython = tab-completing python shell for spark

Update (2015-06-21): The new version of Spark (1.4.0) resolved the naming issue for environment variables, so that environment variables in Windows and Linux have the same names. Big thank-you to Aviad "Jo" Cohen for pointing this out to me!

<tldr>
How to run pyspark via IPython/IPython Notebook in Windows/Mac/Linux, and how to turn off its log messages.
</tldr>

Big-data is a lie - that we all know. To make it more believable, a shift was made from disk-based Map-Reduce (aka: Map-Reduce) to RAM-based Map-Reduce (aka: Spark). The immediate benefit is that Pig, which uses disk-based Map-Reduce, now has a fork called Spork (Pig over Spark). Just say that: spork!!! The joy is boundless.

Spark is fun if you like writing in Scala. (I am of course ignoring Java completely, just like everyone else should.) In case you are like me - trapped in the belly of Python, with no intention to ever leave these cozy intestines, you'll be working with the Python port of spark, dubbed PySpark. I call this a 'port' rather than a version or a driver since some parts available in the Scala/Java versions of Spark are not available in Python (for instance, their graph toolkit, GraphX). However, it's still useful for many.

When you downloaded a pre-built binary version of Spark (from here), the versions > 1.3 support integration with ipython. This is based on having ipython in your PATH and adding some environment variables. Here's how to achieve it:

0) Make sure python is installed: from the Windows cmd or Linux shell, type in:
$ python
(you should get a Python shell, exit() the shell.)
$ pip
(you should see that pip is installed.)

Windows users: to make the best out of pyspark you should probably have numpy installed (since it is used by MLlib). It's is a pain to install this on vanilla Python, so my advice is to download Anaconda Python, a distribution of python - which means Python + tons of packages already installed (numpy, scipy, pandas, networkx and much more). This is also good for installing 64-bit Python (rather than the usual 32-bit version). Get Anaconda from here:
http://continuum.io/downloads


1) Install ipython, pyradline:

$ pip install ipython
$ pip install pyreadline


2) Make sure that the ipython executable is in your path (if you can run pip with no problems, that should be ok as well).


3) Set the proper environment variables:
On Windows:


$ set PYSPARK_DRIVER_PYTHON="ipython"
(or set it permanently in the control panel)

On Mac/Linux:
$ declare -x PYSPARK_DRIVER_PYTHON="ipython"
(or add it to your ~/.bash_profile or ~/.bashrc file)

On Windows + Spark < 1.4.0:


$ set IPYTHON=1


4) Run pyspark, and you'll get it in IPython with auto completion working! Now you'll never mistake spelling sc.paralelize sc.parrallelize sc.parallelize again!


Bonus rounds:

5) If you want to use pyspark and IPython Notebook, you can install IPython Notebook, and then make pyspark use it:
On Windows:
$ set PYSPARK_DRIVER_PYTHON_OPTS=notebook
(or set it permanently in the control panel)

On Mac/Linux:
$ declare -x PYSPARK_DRIVER_PYTHON_OPTS="notebook"
(or add it to your ~/.bash_profile or ~/.bashrc file)

On Windows + Spark < 1.4.0:


$ set IPYTHON_OPTS=notebook

In the next IPython Notebook, the sc object will be available right from the start (no need to define it) as well as the pyspark module.


6) Last but not least, like any good Java program Spark makes a big drama out of its execution by printing out lots and lots of lengthy log messages. You can turn them off by following this StackOverflow answer:
http://stackoverflow.com/questions/25193488/how-to-turn-off-info-logging-in-pyspark


Enjoy Sparking!

Sunday, May 3, 2015

How to install IPython Notebook + matplotlib + GraphLab Create

tl;dr: these instructions *work*. I'm publishing them here mainly to make them quickly accessible for my own use.

Install the following to get a fully working IPython Notebook:

$ sudo apt-get install python-dev
$ sudo apt-get install libzmq3-dev
$ sudo pip install pyzmq
$ sudo pip install jinja2
$ sudo pip install pygments
$ sudo pip install tornado
$ sudo pip install jsonschema
$ sudo pip install ipython
$ sudo pip install "ipython[notebook]"


If you want to use this machine as a server, follow these instructions:

These instructions including creating an SSL key pair so that you can access the IPython server via https from a remote machine.


Additionally, install this to get a fully working matplotlib:

$ sudo apt-get install libpng-dev
$ sudo apt-get install libfreetype6
$ sudo apt-get install libfreetype6-dev
$ sudo apt-get install g++
$ sudo pip install matplotlib


And finally, install GraphLab Create:

$ pip install graphlab-create

Note: for GraphLab Create to work, your Python instance should be built with the UCS4 flag. To check if that was the case:

(Taken from: http://stackoverflow.com/questions/1446347/how-to-find-out-if-python-is-compiled-with-ucs-2-or-ucs-4 )

When built with --enable-unicode=ucs4:
>>> import sys
>>> print sys.maxunicode
1114111
When built with --enable-unicode=ucs2:
>>> import sys
>>> print sys.maxunicode
65535



GraphLab Create Product Key Setup

After GraphLab is installed, you need to get a product key from dato.com (for free, after a simple and short registration). When you register, you receive the product key within a script for installing it into the appropriate directory. In case you lost the script and only have the product key, you can also set it via GraphLab itself.

$ python
>>> import graphlab as gl
>>> gl.product_key.set_product_key("PRODUCT KEY OBTAINED FROM DATO AFTER REGISTRATION")

Tuesday, March 10, 2015

Resizing a VirtualBox vm's disk using gparted - by Derek Molloy

Had to place a link somwhere to this awsome post by Derek Molloy, about how to resize the disk for a VirtualBox VM:
http://derekmolloy.ie/resize-a-virtualbox-disk/

If you install Ubuntu Desktop (like I did), reserve more than 8GB for disk space... This process is a bit lengthy and annoying, but Derek's post makes it a bit better with it's clear explanations.

Tuesday, January 27, 2015

Compiling GCC 4.9.2 from scratch on CentOS 6.5

I required GCC 4.8+ to compile the GraphLab Create SDK:
https://github.com/graphlab-code/GraphLab-Create-SDK

The machine runs CentOS 6.5 (for which yum does not yet offer the latest version of GCC), so I had to compile it on my own. Luckily, I have root access on this machine.

To compile it from scratch, GMP, MPFR and MPC should be installed:

yum install gmp-devel mpfr-devel libmpc-devel

Afterwards, the official installation instructions may be followed:
https://gcc.gnu.org/wiki/InstallingGCC

Monday, December 29, 2014

Installing python-igraph 0.7 (when cannot compile against libxml2.a)

Scientific Linux 6.5 (SL6.5), trying to install python-igraph, but getting error documented here:
https://github.com/igraph/igraph/issues/640. When trying to pip install python-igraph, version 0.7 yields the following error:

/usr/lib64/gcc/x86_64-suse-linux/4.3/../../../../x86_64-suse-linux/bin/ld: /usr/lib64/libxml2.a(entities.o): relocation R_X86_64_32 against `.text' can not be used when making a shared object; recompile with -fPIC

/usr/lib64/libxml2.a: could not read symbols: Bad value

collect2: ld returned 1 exit status
The server is not mine, so I am not a sudoer or anything.

The solution is mentioned in the github issue:


I modified the setup.py as follows:
I changed the line
variants = ["lib{0}.a", "{0}.a", "{0}.lib", "lib{0}.lib"]
to
variants = ["lib{0}.so", "{0}.so"]
I downloaded Python 2.7.9's source code and compiled it on a local directory. I then added it to my PATH so that my newly compiled version would be the one I use by default. I logged out, logged in, downloaded the python-igraph source code, and modified the variants in the setup.py and used Python to install igraph:

python setup.py install

I then tried to import igraph from Python, but got an ImportError:

ImportError: libigraph.so.0: cannot open shared object file: No such file or directory

So I looked it up and found that in the igraph source code directory (where it was built) and found the libigraph.so.0 file. On the GitHub link, they say you should copy it to the /lib folder under your Python installation. That did not work for me (maybe because I don't use Anaconda like they do, but who knows). So I added that

setenv LD_LIBRARY_PATH ${LD_LIBRARY_PATH}:/path/to/build/source/of/python-igraph-0.7/igraphcore/lib

and now igraph could be imported from my own Python installation.


I am writing this post because:

  1. I almost resorted to using the crappy C API of igraph; such desperation is surely worth of a post.
  2. The answer supplied by the igraph team here: http://lists.nongnu.org/archive/html/igraph-help/2014-09/msg00041.html
    is misleading and did not help me, so I thought it'd be worth to upvote the GitHub issue answer by a link.

That's it.

Thursday, February 27, 2014

Getting logs for jobs / tasks in Hadoop 2

tl;dr

mapred job -list all | grep <username> #get your job-id
mapred job -events <job-id> # print attempts
mapred job -logs <job-id> [<attempt-id>] #all/just for attempt

That's it. I'm not going to spend another word discussing this. Grab the shell terminal and try it yourself, and everything will be clear.

tl;dr+

#here's the same example and some outputs with:

  • <username> = reuts
  • <job-id> = job_1392731301770_0015
  • <attempt-id> = attempt_1392731301770_0015_m_000001_0

[root@hadoopb home]# mapred job -list all | grep reuts #all the 
14/02/27 17:20:00 INFO service.AbstractService: Service:org.apache.hadoop.yarn.client.YarnClientImpl is inited.
14/02/27 17:20:00 INFO service.AbstractService: Service:org.apache.hadoop.yarn.client.YarnClientImpl is started.
 job_1392731301770_0022     FAILED       1393505972362         reuts         default        NORMAL                    0               0       0M              0M                0M      hadoopb-w02:8088/proxy/application_1392731301770_0022/jobhistory/job/job_1392731301770_0022
 job_1392731301770_0021     FAILED       1393504180159         reuts         default        NORMAL                    0               0       0M              0M                0M      hadoopb-w02:8088/proxy/application_1392731301770_0021/jobhistory/job/job_1392731301770_0021
 job_1392731301770_0016     FAILED       1393455977860         reuts         default        NORMAL                    0               0       0M              0M                0M      hadoopb-w02:8088/proxy/application_1392731301770_0016/jobhistory/job/job_1392731301770_0016

#... many more failures after that! I picked one of the last which is not shown here - job_1392731301770_0015

[root@hadoopb home]# mapred job -events job_1392731301770_0015 #the high-level events.
Usage: CLI [-events <job-id> <from-event-#> <#-of-events>]. Event #s start from 1.
[root@hadoopb home]# mapred job -events job_1392731301770_0015 1 100
14/02/27 17:19:44 INFO service.AbstractService: Service:org.apache.hadoop.yarn.client.YarnClientImpl is inited.
14/02/27 17:19:44 INFO service.AbstractService: Service:org.apache.hadoop.yarn.client.YarnClientImpl is started.
14/02/27 17:19:45 INFO mapred.ClientServiceDelegate: Application state is completed. FinalApplicationStatus=FAILED. Redirecting to job history server
Task completion events for job_1392731301770_0015
Number of events (from 1) are: 6
FAILED attempt_1392731301770_0015_m_000001_0 hadoopb-w07:59031/tasklog?plaintext=true&attemptid=attempt_1392731301770_0015_m_000001_0
FAILED attempt_1392731301770_0015_m_000001_1 hadoopb-w06:45315/tasklog?plaintext=true&attemptid=attempt_1392731301770_0015_m_000001_1
FAILED attempt_1392731301770_0015_m_000000_1 hadoopb-w05:48979/tasklog?plaintext=true&attemptid=attempt_1392731301770_0015_m_000000_1
FAILED attempt_1392731301770_0015_m_000001_2 hadoopb-w07:59031/tasklog?plaintext=true&attemptid=attempt_1392731301770_0015_m_000001_2
FAILED attempt_1392731301770_0015_m_000000_2 hadoopb-w06:45315/tasklog?plaintext=true&attemptid=attempt_1392731301770_0015_m_000000_2
FAILED attempt_1392731301770_0015_m_000001_3 hadoopb-w06:45315/tasklog?plaintext=true&attemptid=attempt_1392731301770_0015_m_000001_3

[root@hadoopb home]# mapred job -logs job_1392731301770_0015 #that'd spit all the logs, so lets specify an attemptID.
[root@hadoopb home]# mapred job -logs job_1392731301770_0015 attempt_1392731301770_0015_m_000001_0
14/02/27 17:21:18 INFO service.AbstractService: Service:org.apache.hadoop.yarn.client.YarnClientImpl is inited.
14/02/27 17:21:18 INFO service.AbstractService: Service:org.apache.hadoop.yarn.client.YarnClientImpl is started.
14/02/27 17:21:19 INFO mapred.ClientServiceDelegate: Application state is completed. FinalApplicationStatus=FAILED. Redirecting to job history server

LogType:stderr

LogLength:250
Log Contents:
Traceback (most recent call last):
  File "/mnt/disk2/hadoop/yarn/usercache/reuts/appcache/application_1392731301770_0015/container_1392731301770_0015_01_000003/./mapper.py", line 11, in <module>
    import config
ImportError: No module named config

LogType:stdout

LogLength:0
Log Contents:

LogType:syslog

LogLength:11461
Log Contents:
2014-02-27 01:01:50,943 WARN [main] org.apache.hadoop.conf.Configuration: job.xml:an attempt to override final parameter: mapreduce.job.end-notification.max.retry.interval;  Ignoring.
2014-02-27 01:01:50,944 WARN [main] org.apache.hadoop.conf.Configuration: job.xml:an attempt to override final parameter: mapreduce.job.end-notification.max.attempts;  Ignoring.
2014-02-27 01:01:51,062 INFO [main] org.apache.hadoop.metrics2.impl.MetricsConfig: loaded properties from hadoop-metrics2.properties

Friday, February 14, 2014

To Unicode and Back in Python 2.xx

(note)

Python syntax highlighting for this post was done using lusever's online syntax highlighter, to which I arrived after testing many other online highlighters and concluded that it's the best!)

tl;dr:

Read these:
http://nedbatchelder.com/text/unipain.html
http://farmdev.com/talks/unicode/

Always turn incoming (read from file, user or web) strings into unicode objects, and outgoing unicode objects (written to file, printed to screen, used for opening a url) back into strings:
# -*- coding: utf-8 -*-
"""Unicode sandwiching methods."""
def welcome_string(obj, encoding='utf-8'):
    """For objects entering the system. Returns utf-8 unicode objects."""
    if isinstance(obj, basestring):
        if not isinstance(obj, unicode):
            obj = unicode(obj, encoding)
    return obj
    
def goodbye_string(obj, encoding = 'utf-8'):
    """For objects leaving the system. Returns an str instance."""
    if isinstance(obj, basestring):
        if isinstance(obj, unicode):
            obj.encode(encoding, 'ignore')
    return obj
    
def printable_string(obj):
    """For unicode objects which should be printed to the screen."""
    if isinstance(obj,unicode):
        obj = obj.encode('ascii','ignore')
    return obj

def get_html(url):
    """Url is assumed to be a string in utf-8 encoding."""
    import urllib2
    u = urllib2.urlopen(goodbye_string(url), timeout = 60)
    html = u.read()
    html = welcome_string(html)
    return html

url = "http://jltfpw.jimdo.com/alle-informationen-zur-gesetzesänderung-der-petition-sternenkinder/faq/"
print "Note how Python stores the bytes for the ae symbol in the url:"
print url
print

url = u"http://jltfpw.jimdo.com/alle-informationen-zur-gesetzesänderung-der-petition-sternenkinder/faq/"
try:
    get_html(url) # will fail: Python will break the 'ä' and try to open a bad url.
except:
    print "urllib2.open requires a string - a sequence of bytes!"
    print

url = u"http://jltfpw.jimdo.com/alle-informationen-zur-gesetzesänderung-der-petition-sternenkinder/faq/".encode('utf-8')
html = get_html(url)
print printable_string(html)[:100],"..."

Unicode Hell: An Introduction

I'm currently working on a project involving texts in German. I tested my code on small inputs, was satisfied with the results, then left it to work on a large dataset for the night. I woke up in the morning and logged in to find this:

UnicodeEncodeError: 'ascii' codec can't encode character u'\xb7' in position 13: ordinal not in range(128)

The traceback gave out the line numbers, so I wrapped it with try-except and ignored the records with these errors. Ran my code again, and got this error in a different line.

I tried horsing around:

"string".encode('utf-8')
"string".decode('utf-8')
"string".encode('ascii', 'ignore')
... and all sorts of other games. I even wrote a function replacing each umlauted character (there's a finite number of these in German) with some sort of English equivalent. That implied data loss (as "ä" is not exactly the same as "ae"), but I didn't care. Until that un-umlauting function starting throwing UnicodeEncodeErrors as well!

So I did some digging and here are my results. I've also listed a few recommended readings, and what I'll try to do in this post is to give the tl;dr versions of their words and also supply some code examples. On the way I'll point some surprising facts (at least to me) I've found about Python 2.xx.

I used to think that strings are ascii-strings, and unicode objects are some utf-8 versions of strings, or something like that. That's nonsense, and that has to go out first.

This is how your application should be like (according to Ned Batchelder's "Pragmatic Unicode" which I'll go on quoting again and again in this post:

Read Ned's slides about unicode and save yourself hours of suffering!!!

First steps towards understanding unicod

Python string objects really are sequences of bytes. When you check the length of "string", you can 6 (naturally). But check this out:

>>> "ö"
'\xc3\xb6'
>>> len("ö")
2

Python has to represent "ö" using 2 hex bytes. That's very nice of him. Unfortunatqely, that can cause all sorts of errors, even causing errors in a line such as 'print "ö"' on the Windows cmd!

So let's do what I used to think is the solution, and turn this weird umlauted o to unicode:

>>> unicode("ö")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position 0: ordinal not in range(128)

Look, ma! Decode!!!

What's ascii got to do with that? Isn't unicode utf-8? Appearently, not!

Here's the right solution. Note the length of the generated unicode object:

>>> unicode("ö",'utf-8')
u'\xf6'
>>> len(unicode("ö",'utf-8'))
1

Hey, utf-8 can be seen again! Guess that solved it all, right?

Not exactly.

Unicode is a concept, meaning there's a unified numeric code for each symbol. "ö" is represented by the (hex) number f6 (246). Note that's larger than 127 - with [0,127] being the numeric values of ascii symbols. Each symbol has a numeric value. Nice!

Files, on the other hands, are sequences of bytes. To store unicode objects (text in unicode; "strings" as we used to call them) we need some way to represent these as byte sequences. How do we do that?

There are several methods, with utf-8 being the king. Python uses utf-8 by default when handling unicode objects and making them play with strings (u'a' + 'b', u'c'.join(['def']) , etc). Let's turn our unicode object back to string (a sequence of bytes) and see that we get the same values as we originally got for it as a string:

>>> str(unicode("ö",'utf-8'))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
UnicodeEncodeError: 'ascii' codec can't encode character u'\xf6' in position 0: ordinal not in range(128)
>>> unicode("ö",'utf-8').encode('utf-8')
'\xc3\xb6'

Look, ma! Encode!

To make a long story short: when dealing with strings which might not only contain ascii characters, you'd better use unicode objects instead of strings. And when you deal with data from the internet - that becomes not a suggestion but a requirement.

So what's demanded of you? Again:

"Unicode Sandwich - Bytes on the outside, Unicode on the inside."

- Ned Batchelder

What's the outside? Files you read, html you read via urllib2.urlopen(...).read(), user input, etc. And just as well - anything you're intending to write to a file or pickle, and the URLs you're trying to open.
What's the inside? Anything you store. Dictionary keys (and values!), assignments to your objects' properties, etc.

And how do you make the switch between outside and inside and outside again?

To quote again:
You can't decode a unicode, and you can't encode a str. Try doing it the other way around.
Or in my words (which are written on a sticky-note placed right above my screen until I'll know it by heart):

Decode - str to unicode
Encode - unicode to str

You de-string a string object into a unicode object, and en-string a unicode object into a sequence of bytes (string). Or whatever other mnemonic that works for you.

Solutions

An incoming string s has to be treated as soon as possible, like this:
s.decode('utf-8','ignore')

An outgoing unicode object u has to be treated as late as possible, like this:
u.encode('utf-8','ignore')

Unless you're printing it - the Windows cmd is working with the cp-666 (or something) encoding, and when Python will try to encode (to str!) your text for the printing it'd fail with a UnicodeEncodeError - so you should do something like this:
u.encode('ascii','ignore')

Just as well, files you are writing should be told to expect utf-8 byte streams, and so:

import codecs #the Python STL codecs library
with codecs.open("output_filename", 'w', 'utf-8', 'ignore') as f:
    f.write(unicode_object_decoded_in_utf8)

Now I know that sounds like a lot of responsibility, but it really isn't. When I converted my code unicode-safe, I thought I'll have tons of modifications to do. I was wrong. I didn't really read files from as many places as I've thought (which is where I placed my str → unicode conversions), and I didn't write them in so many places (which is where I placed my unicode → str conversions, or used the codecs shortcut for opening a file for writing out utf-8 . And after I did this, my code stopped crashing due to UnicodeWhateverErrors.

Here's the code I used, based on Kumar McMillan's "to_unicode_or_bust" function in "Unicode in Python, Completely Demystified":

"""Unicode sandwiching methods."""
def welcome_string(obj, encoding='utf-8'):
    """For objects entering the system. Returns utf-8 unicode objects."""
    if isinstance(obj, basestring):
        if not isinstance(obj, unicode):
            obj = unicode(obj, encoding)
    return obj
    
def goodbye_string(obj, encoding = 'utf-8'):
    """For objects leaving the system. Returns an str instance."""
    if isinstance(obj, basestring):
        if isinstance(obj, unicode):
            obj.encode(encoding, 'ignore')
    return obj
    
def printable_string(obj):
    """For unicode objects which should be printed to the screen."""
    if isinstance(obj,unicode):
        obj = obj.encode('ascii','ignore')
    return obj

Here is a usage example based on importing the functions above:

# -*- coding: utf-8 -*-
from unicode_sandwiching_methods import welcome_string, goodbye_string, printable_string
def get_html(url):
    """Url is assumed to be a string in utf-8 encoding."""
    import urllib2
    u = urllib2.urlopen(goodbye_string(url), timeout = 60)
    html = u.read()
    html = welcome_string(html)
    return html

url = "http://jltfpw.jimdo.com/alle-informationen-zur-gesetzesänderung-der-petition-sternenkinder/faq/"
print "Note how Python stores the bytes for the ae symbol in the url:"
print url
print

url = u"http://jltfpw.jimdo.com/alle-informationen-zur-gesetzesänderung-der-petition-sternenkinder/faq/"
try:
    get_html(url) # will fail: Python will break the 'ä' and try to open a bad url.
except:
    print "urllib2.open requires a string - a sequence of bytes!"
    print

url = u"http://jltfpw.jimdo.com/alle-informationen-zur-gesetzesänderung-der-petition-sternenkinder/faq/".encode('utf-8')
html = get_html(url)
print printable_string(html)[:100],"..."

Note that if you're going to construct some toy code examples including unicode texts, nclude the "magic string" stating your source code file's encoding as the top of your script (after the #! if you have one):
# -*- coding: utf-8 -*-

Further reading:

Ned Batchelder: Pragmatic Unicode (2012)
http://nedbatchelder.com/text/unipain.html

(The "Unicode Sandwich" slide:
http://nedbatchelder.com/text/unipain/unipain.html#35 )

Kumar McMillan, Unicode in Python, Completely Demystified (2008)
http://farmdev.com/talks/unicode/

Joel Spolsky, The Absolute Minimum Every Software Developer Absolutely, Positively Must Know
About Unicode and Character Sets (No Excuses!) (2003!)
http://www.joelonsoftware.com/articles/Unicode.html

encool - quickly generate crazy unicode strings for your tests:
http://fsymbols.com/generators/encool/
Ian Albert - a dude who printed out all the unicode symbols of his time, and, well, that's about that.

Notes:

German texts usually contains umläuts - which can be stylized back to English using the following transliteration dictionary (kinda like Russian and volapuk):

german_letters = {"Ä" : "AE",
"ä" : "ae",
"æ" : "ae",
"Ö" : "O",
"ö" : "o",
"Ü" : "U",
"ü" : "u",
"ß" : "ss"}

Note 2: People get confused due to the C char / string stuff that is used to teach C programming - uppercasing/lowercasing etc... More on that later.