Soccer World Cup 2018 Average Face by Team

World Cup 2018 Average Player Face by Team
World Cup 2018 Average Player Face by Team


We all know that Spain is going to win this world cup 😉, but watching the games is still entertaining. I thought today while watching England vs. Tunisia that soccer players look very much alike, especially within a team, and so I thought I could compute the average face by soccer team for this world cup.


Getting the data, in this case, images for each player in the current soccer world cup is arguably critical. The images need to have a plain background, similar ilumation, and ideally, equal size. Luckly, the FIFA has done all of this, and the images are available online:

If you inspect one of the player’s elemtents, you can access the URL for the player’s image, which can then be downloaded programmatically

Inspecting the player element on the browser console shows the URL.

I downloaded the page’s source code, and downloaded the 736 300 pixels squared images.

Averaging a face

There is a naive way of averaging an image, using numpy, and it only takes a few lines:

import numpy as np
import imageio

countries = ['argentina', 'belgium', 'colombia', 'croatia', ...]

for country in countries:
    images_stacked = np.zeros((23, 300, 300, 3))

    for i in range(0, 23):
        images_stacked[i] = imageio.imread('images-input/%s/%s_%s.jpg' % (country, country, i))

    results = np.zeros((300, 300, 3))

    for color in range(0, images_stacked.shape[3]):
        for x_pos in range(0, images_stacked.shape[1]):
            for y_pos in range(0, images_stacked.shape[2]):
                results[x_pos, y_pos, color] = np.mean(images_stacked[:, x_pos, y_pos, color])

    imageio.imwrite('images-output/%s_naive_averager.png' % country, results)

There is room for improving on this code, but I am not interested in that, rather on the actual results. And here is how they show:

Sweden Naive Average Face

Let’s look at another example:

Brazil Naive Average Face

Even though images are standard, averaging the pixel values doesn’t build a compeling image; sure, we can distinguish some features such as color of the t-shirt or hair, but we can’t put a face to it.

Face morpher

Luckily, there are better ways. Meet Face Morpher (FM from now own). FM works in a different way to find the average face, instead of naively averaging the pixel values, it builds a geometry of the face by identifying elements on it such as the eyes. It then proceeds to average those sections across images.

The results are much more compeling, and we are definitely able to put a face to this teams now.

Sweden FM Average Face

Or in the case of Brazil:

It looks great! I love it! And yet, aren’t we missing important and distinctive elements, such as the hair, ears, or even team t-shirt?

Face art

I decided to them combine the two images: naive + FM for a more compeling result. I blended the images using Sketch, used the naive image as background, and overlayed a semi-transparent FM face.

Quite happy with the result:

World Cup 2018 Average Player Face by Team
World Cup 2018 Average Player Face by Team


How does the average face look across all countries then?

Average face across all countries
Average face across all countries

PSN We’ve encountered an error while processing your order

PSN Error message

For about two years, any attempt to purchase anything on the PSN website failed with the message:

“We’ve encountered an error while processing your order. Please try again later”

PSN Error Message
PSN Error Message

It turns out I am not the only one getting this error:

I contacted support, and they said that the billing address from my credit card does not match my profile’s address You’ve heard correctly, Sony thinks that your address should be the same as your billing address in all cases, otherwise, purchases will fail with that cryptic error.

To fix it, head to, and check the box “Use my billing address” under Account -> Account Details -> Location.


PSN Update Address
PSN Update Address

Phone Insights API

This API can parse, format and find phone numbers in text documents for any phone number worldwide.

These are the endpoints:

Endpoint /parse

Given a phone number with an international prefix, or if you prefer by specifying the ISO country code, and the national phone number instead, we can tell you:

  • number_type: One of the following: FIXED, MOBILE, VOIP, SHORT_NUMBER, PREMIUM, TOLL_FREE, PAGER, etc.
  • location: Where available by prefix.
  • phone_number_e164: Phone number in E164. Perfect to be saved in a database in a standard way.
  • carrier: In countries where blocks of numbers are assigned to carriers. Note that if portability is available in the country, we will return the original carrier.
  • is_valid_number: Using the length, format and prefixes against our 100Mbytes+ database of phone numbers we can asses the validity of the phones. E.g. we can detect fake US numbers if the prefixes don’t exist.
  • country_code_iso: Two letter ISO country code for the phone number.
  • We also give other auxiliary fields such as: number_of_leading_zeros, national_number, etc. If you need more information returned, contact us!

Example request:

curl -X POST --include '' \
  -H 'X-Mashape-Key: {my-mashape-key}' \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json' \
  --data-binary '{"phone_number":"+34626475849","country_code":"ES"}'

That returns:

  "country_code": 34,
  "country_code_iso": "ES",
  "location": "Spain",
  "location_latitude": 40.0028028,
  "location_longitude": -4.003104,
  "national_number": 626475849,
  "number_of_leading_zeros": null,
  "number_type": "MOBILE",
  "is_valid_number": true,
  "carrier": "Movistar",
  "phone_number_e164": "+34626475849"


Endpoint /format

Given a phone number with an international prefix, or if you prefer by specifying the ISO country code, and the national phone number instead, we can tell you:

  • national: phone number in national format, e.g. (415) 498-8739
  • international: phone number in international format, e.g. +1 (415) 498-8739
  • E164: phone number in E164 format, e.g. +14154988739
  • RFC3966: phone number in E164 format, e.g. tel:+14154988739

Example request:

curl -X POST --include '' \
  -H 'X-Mashape-Key: {my-mashape-key}' \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json' \
  --data-binary '{"phone_number":"+34626475849","country_code":"ES"}'


  "national": "626 47 58 49",
  "international": "+34 626 47 58 49",
  "E164": "+34626475849",
  "RFC3966": "tel:+34-626-47-58-49"

Endpoint /find-numbers-in-text Endpoint

Given a text document (max. 512 characters), we can find all phone numbers in it. E.g. for the text: Hey, the office's phone number is (510) 765-9845, my personal one is 4157653478. We will return the begin and end character position for both phone numbers, along with a E164 formatted version of each. We can find phone numbers, even if the format used in the document differs.

curl -X POST --include '' \
  -H 'X-Mashape-Key: {my-mashape-key}' \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json' \
  --data-binary '{"text":"Hello, call me at 4154785647 or at (510) 675 8976 if it's after 11PM","country_code":"US"}'


  "matches": [
      "start": 18,
      "end": 28,
      "phone_number": "+14154785647"
      "start": 35,
      "end": 49,
      "phone_number": "+15106758976"

If you want to try it, head to mashape: The API is completely free!

Tennis API

I published a tennis API on

This API has methods to query all tennis matches on the ATP since 1968,  along with players and qualifications. Dynamic filtering, sorting and pagination of the results are allowed.

Example request to get matches played on Clay that lasted over 350 minutes (almost 6 hours!):

curl --get --include '{"minutes":">350", "surface": "Clay"}' \
  -H 'X-Mashape-Key: {mashape_key}' \
  -H 'Accept: application/json
  "_items": [
      "id": 125145,
      "tourney_id": "2004-520",
      "tourney_name": "Roland Garros",
      "surface": "Clay",
      "draw_size": 128,
      "tourney_level": "G",
      "tourney_date": "2004-05-24",
      "match_num": 61,
      "winner_id": 102148,
      "winner_seed": 0,
      "winner_entry": "",
      "winner_name": "Fabrice Santoro",
      "winner_hand": "R",
      "winner_ht": 178,
      "winner_ioc": "FRA",
      "winner_age": 31.4552,
      "winner_rank": 58,
      "winner_rank_points": 662,
      "loser_id": 103096,
      "loser_seed": 32,
      "loser_entry": "",
      "loser_name": "Arnaud Clement",
      "loser_hand": "R",
      "loser_ht": 173,
      "loser_ioc": "FRA",
      "loser_age": 26.4339,
      "loser_rank": 33,
      "loser_rank_points": 1005,
      "score": "6-4 6-3 6-7(5) 3-6 16-14",
      "best_of": 5,
      "round": "R128",
      "minutes": 393,
      "winner_ace": "22",
      "w_df": 2,
      "w_svpt": 229,
      "w_1stIn": 108,
      "w_1stWon": 77,
      "w_2ndWon": 60,
      "w_SvGms": 36,
      "w_bpSaved": 13,
      "w_bpFaced": 22,
      "l_ace": 10,
      "l_df": 9,
      "l_svpt": 234,
      "l_1stIn": 121,
      "l_1stWon": 84,
      "l_2ndWon": 52,
      "l_SvGms": 34,
      "l_bpSaved": 17,
      "l_bpFaced": 27,
      "_created": "Thu, 01 Jan 1970 00:00:00 GMT",
      "_updated": "Thu, 01 Jan 1970 00:00:00 GMT",
      "_etag": "d6e3dcd73f0a27191ce602f0d537e697a022e690",
      "_links": {
        "self": {
          "title": "Atp_match",
          "href": "atp_match/125145"
      "id": 164617,
      "tourney_id": "2017-0308",
      "tourney_name": "Munich",
      "surface": "Clay",
      "draw_size": 32,
      "tourney_level": "A",
      "tourney_date": "2017-05-01",
      "match_num": 297,
      "winner_id": 111202,
      "winner_seed": 0,
      "winner_entry": "",
      "winner_name": "Hyeon Chung",
      "winner_hand": "R",
      "winner_ht": 0,
      "winner_ioc": "KOR",
      "winner_age": 20.95,
      "winner_rank": 78,
      "winner_rank_points": 659,
      "loser_id": 105373,
      "loser_seed": 0,
      "loser_entry": "",
      "loser_name": "Martin Klizan",
      "loser_hand": "L",
      "loser_ht": 190,
      "loser_ioc": "SVK",
      "loser_age": 27.8056,
      "loser_rank": 53,
      "loser_rank_points": 880,
      "score": "6-4 3-6 6-2",
      "best_of": 3,
      "round": "QF",
      "minutes": 987,
      "winner_ace": "4",
      "w_df": 2,
      "w_svpt": 81,
      "w_1stIn": 47,
      "w_1stWon": 36,
      "w_2ndWon": 15,
      "w_SvGms": 13,
      "w_bpSaved": 4,
      "w_bpFaced": 6,
      "l_ace": 8,
      "l_df": 4,
      "l_svpt": 95,
      "l_1stIn": 49,
      "l_1stWon": 28,
      "l_2ndWon": 27,
      "l_SvGms": 14,
      "l_bpSaved": 8,
      "l_bpFaced": 12,
      "_created": "Thu, 01 Jan 1970 00:00:00 GMT",
      "_updated": "Thu, 01 Jan 1970 00:00:00 GMT",
      "_etag": "3e4d5985b601dad5b0229b881b6a0fe52aa06af0",
      "_links": {
        "self": {
          "title": "Atp_match",
          "href": "atp_match/164617"


Tablifyme API

I created an API that creates a text table from a JSON array. Useful to send tables of data over email, Slack or Markdown.

The API is available on Mashape:


curl -X POST --include '' \
-H 'X-Mashape-Key: My-Secret-key' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
--data-binary '{"body":[[1,2,3],[4,"test",6]],"header":["Y","A","B"]}'


|  Y  |  A   |  B  |
|  1  |  2   |  3  |
|  4  | test |  6  |



Choropleth: percentage of foreigners by US county

Building maps is very easy with Folium. I got my hands on some data from the US Census, specifically, the foreign born population and total population per US county.

To plot it, first download the CSV from the  2011-2015 American Community Survey 5-Year Estimates.

import os
import folium
import pandas as pd

# Load data
data = pd.read_csv('data.csv')

# Add column with ratio of foreign born vs total pop.
data['ratio_foreign_born_vs_total_population'] = 100 * data['Estimate; Foreign born:'] / data['Estimate; Total:']

# Create map
map_1 = folium.Map(location=[39, -96], zoom_start=4)
high_res_county_geo = os.path.relpath('gz_2010_us_050_00_500k.json') # from

# Add choropleth layer
 columns=['Id', 'ratio_foreign_born_vs_total_population'],
 legend_name='Percentage Foreigners(%)',
 threshold_scale=[0, 5, 10, 15, 20, 25]


# Save as index.html'index.html')

The result:

Saïgon on iPhone 6 iOS 10.2.1

Saigon’s Jailbreak is now on beta 2r1, and it promises to jailbreak iPod Touch 6 (10.2.1), iPhone SE (10.2.1), iPhone 6/Plus (10.2.1), iPhone 6s/Plus (10.2.1), iPad Mini 4 (10.2.1) (Not tested), iPad Air 2 (10.2.1). I have an iPhone 6 on 10.2.1 and decided to give it a try.

Download Cydia impactor from

Download Saigon.ipa from

Login into your and create an app specific password.

Open Cydia Impactor and Drag and drop the Saigon.ipa into it. You will be prompted to enter your email (the same one you used at appleid) and your password, for this one, use the one time password you just create it.

Saigon should now be installed on your phone. Open it and you should see a Jailbreak button. Note that because this is a beta 2, and because the vulnerability that is based on is random, the jailbreak will probably fail a few times before it succeeds.

In my case, despite having a compatible device iPhone 6 on 10.2.1, after installing Saigon I was getting a message with Device Not Compatible. To solve this, I cloned the repo with Saigon’s code and compiled the .ipa myself:

~/D/Code ❯❯❯ git clone
~/D/Code ❯❯❯ ./ was failing with some cryptic errors, I found that xcrun –sdk iphoneos –show-sdk-path was using the SDK for iOS 11, instead, I manually run the commands:

~/D/Code ❯❯❯ $(which xcodebuild) clean build CODE_SIGNING_REQUIRED=NO CODE_SIGN_IDENTITY="" -sdk /Applications/ -arch arm64

~/D/Code ❯❯❯ mv build/Release-iphoneos/ saï
~/D/Code ❯❯❯ zip -r9 Saïgon.ipa Payload/saï

I then impacted this IPA on my device, and magically my device was now supported! 🙂 For your convenience, this is the .ipa I created.


Everything that’s wrong with the new iPhone 8

Although a heavy Mac Book user and lover, I had been away from Apple phones, I saw them as more restricted and expensive versions of their Android counterparts but that changed recently when my company gave me a corporate iPhone 8 Plus 256Gb.

I’ve used the phone for roughly two weeks and I would like to comment on everything that I came to realize it’s wrong with it.

Lightning Port

Why in the hell did Apple not substitute their proprietary 1-star lightning cable for the superior USB-C?  This is extremely annoying, the whole industry is moving towards USB-C as a standard, it’s de facto for new Android phones, and contradictory enough, for the new Mac Books. Even legislators such as the European Union are looking into forcing manufacturers that want to sell phones inside the EU to adopt a common charger, to reduce e-waste. But Apple has different plans…

Yep, that’s a solid 1-star average.

Having a lighting cable means that I need to carry two chargers now, one for my Mac (USB-C) and one for my iPhone (lightning), before, with my Android, my Mac charger would suffice, as I could charge both with it.

Also, connecting my Mac and iPhone out of the box is now impossible. You are forced to buy a USB-C to USB extension for your Mac.

Hey, at least this one is 2-stars!


I was actually excited before receiving the iPhone 8, I thought I’ll have some of those Bluetooth earpods that everybody seems to love, or at least they seem to carry around with impunity. So there I am, opening the iPhone 8 Plus, a 1000$+ phone in California, when I discover the uncomfortable truth.

Apple has not included the Bluetooth earpods, instead, they have included wired earbuds. But it get’s even worse, because of the lack of a jack connector, the earbuds are lighting too, which means that you can only use them on your phone. Now, if you travel with your laptop and phone, you have to bring two sets of earpods. And yes, to add to the inconsistency, the Mac Book Pro latest model does have a jack connector.

Those will not work anywhere else, not even on your Mac.

At least two things that they could have done to remedy the situation, first, if I am paying more than 1K$ for a phone that doesn’t have a jack, don’t nickel and dime me for extra Bluetooth earbuds, include them “for free”. Otherwise, put a USB-C connector on the iPhone, and include wired earbuds, that at least work on my computer and phone.

The Design

Supposedly Apple was at least good at this one, but I am starting the hesitate. The iPhone 8 design is almost an exact copy of it’s much older brother iPhone 6, and time is starting to show, those huge bezels at the top and bottom are not cool anymore, the home button, even if maintained, could have been squeezed as some Androids have done to increase screen real estate, or even placed behind.

The design is starting to show it’s age.

The glass back is cool but is a fingerprint magnet, and dropping it is a liability. Haptic feedback and force touch are in my opinion underutilized features, I barely use them, and including them makes for a thicker phone.

Overall, it feels like Apple didn’t put much time or effort into this one. They were focused on the iPhone X that got all the media attention, and for the iPhone 8 they basically took the 7 and put in a slightly better processor and a higher price tag. For the foreseeable future, I will stick to Android.





[MAC] Install matplotlib basemap on virtualenv

I am using Python3.6 and MacOS 10.13.2(Beta) but should work with similar setups.

First create and activate your virtual environment, by default mine is created on top of Python3.6.

~ ❯❯❯ mkvirtualenv basemap-virtualenv
Installing setuptools, pip, wheel...done.
(basemap-virtualenv) ~ ❯❯❯ cd Desktop
(basemap-virtualenv) ~/Desktop ❯❯❯ curl -o basemap-v1.1.0.tar.gz

Note that I am using the latest version 1.1.0 as of the time of writing this, but you should use the latest available on

Let’s start the installation

(basemap-virtualenv) ~/Desktop ❯❯❯ tar -xvf basemap-v1.1.0.tar.gz
(basemap-virtualenv) ~/Desktop ❯❯❯ cd basemap-1.1.0/geos-3.3.3/
(basemap-virtualenv) ~/D/b/geos-3.3.3 ❯❯❯ export GEOS_DIR=/usr/local
(basemap-virtualenv) ~/D/b/geos-3.3.3 ❯❯❯ ./configure --prefix=$GEOS_DIR
(basemap-virtualenv) ~/D/b/geos-3.3.3 ❯❯❯ make; make install

After this geos library is installed, let’s jump onto installing basemap.

(basemap-virtualenv) ~/D/basemap-1.1.0 ❯❯❯ pip install numpy
(basemap-virtualenv) ~/D/basemap-1.1.0 ❯❯❯ pip install pyproj
(basemap-virtualenv) ~/D/basemap-1.1.0 ❯❯❯ pip install pyshp

(basemap-virtualenv) ~/D/basemap-1.1.0 ❯❯❯ python install
customize UnixCCompiler
customize UnixCCompiler using build_ext
building '_geoslib' extension
compiling C sources
C compiler: clang -Wno-unused-result -Wsign-compare -Wunreachable-code -fno-common -dynamic -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes

compile options: '-I/usr/local/include -I['/Users/myself/.virtualenvs/basemap-virtualenv/lib/python3.6/site-packages/numpy/core/include'] -I/Users/myself/.virtualenvs/basemap-virtualenv/lib/python3.6/site-packages/numpy/core/include -I/usr/local/Cellar/python3/3.6.2/Frameworks/Python.framework/Versions/3.6/include/python3.6m -c'
clang: src/_geoslib.c
zsh:1: no matches found: -I[/Users/myself/.virtualenvs/basemap-virtualenv/lib/python3.6/site-packages/numpy/core/include]
zsh:1: no matches found: -I[/Users/myself/.virtualenvs/basemap-virtualenv/lib/python3.6/site-packages/numpy/core/include]
error: Command "clang -Wno-unused-result -Wsign-compare -Wunreachable-code -fno-common -dynamic -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -I/usr/local/include -I['/Users/myself/.virtualenvs/basemap-virtualenv/lib/python3.6/site-packages/numpy/core/include'] -I/Users/myself/.virtualenvs/basemap-virtualenv/lib/python3.6/site-packages/numpy/core/include -I/usr/local/Cellar/python3/3.6.2/Frameworks/Python.framework/Versions/3.6/include/python3.6m -c src/_geoslib.c -o build/temp.macosx-10.12-x86_64-3.6/src/_geoslib.o -MMD -MF build/temp.macosx-10.12-x86_64-3.6/src/_geoslib.o.d" failed with exit status 1

As we can see, the installation out of the box is failing. More specifically, the command

clang -Wno-unused-result -Wsign-compare -Wunreachable-code -fno-common -dynamic -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes compile options: '-I/usr/local/include -I['/Users/myself/.virtualenvs/basemap-virtualenv/lib/python3.6/site-packages/numpy/core/include'] ...

The reason lies in a bug on  If you read the error, you will see how we are trying to include (-I) a path, but instead, we are passing a python array with a single string inside.  To fix this go to this line:

geos_include_dirs = [os.path.join(GEOS_dir,'include'),inc_dirs]

and change it to:

geos_include_dirs = [os.path.join(GEOS_dir,'include'),inc_dirs[0]]

After that python install succeeds. Great affordable WordPress Hosting

Web development stack

I’ve built quite a few websites for clients, friends, family and for myself too in the last few years, some examples are:, or this very blog. Over time, I’ve polished the tool and techniques, to speed up development, and to increase the quality. At this point my default and preferred architecture that I recommend to most clients is WordPress + Enfold theme. There are powerful reasons behind this choice:

  • WordPress is great. Used by ~27% of the top 10M websites, with a very rich plugin ecosystem, and a visual, intuitive, web based builder. It’s built on technologies that have passed the test of time: PHP and MySQL. The web interface allows non technical customers to make tweaks and additions if they are needed after the project is finished.
  • Enfold by Kriesi is one of the most popular WordPress themes out there, and rightfully so. Their intuitive UI makes it straightforward to create visually compelling websites. It comes with dozens of UI elements, including contact forms, google maps, and image sliders, and they are all personalizable.

I think the quality of the examples above prove the point. The clients were extremely happy with the result, and after 2-3 years, the websites still look sleek and responsive.



This is where the headaches begin. I started recommending my first clients to go with GoDaddy, the had made a name for themselves, their support was always available, and their cPanel web management, although far from perfect, got the job done. Finally, about the price, they weren’t so bad at the beginning, and they included some freebies such as Google Ads credit.

GoDaddy has however let me down in multiple occasions, so I have progressively pushed them down the list of recommended hosting solutions. My first problem with them were their continuous and unwarned price rises, we were paying no more than 20$ for hosting on the first year, but by the third, the bill was over 100$. They also have a very aggressive up-sale and cross-sale model, constantly and annoyingly suggesting you to buy their SSL certificate for 70$+ or to get a faster hosting (read next paragraph) for another 100$.

Secondly comes the speed, in case you don’t know, speed loading in a website is critical in this world of instant gratification, visitors don’t like waits; according to Kissmetrics, every second that passes before the web loads leads to a 7% decrease in conversions.  GoDaddy, in an attempt to cut costs, makes you share a server with hundreds if not thousands of other websites, reducing speed and responsiveness.

Lastly, they recently changed their support policy, you can’t email and are forced to call them. Their support team is helpful, but unfortunately they’ve been trained to try and sell you other services…

Getflywheel and WPEngine

These guys understand WordPress hosting: great infrastructure, free SSL certificates (they are free for them, so that makes sense), free backups and flawless user experience. I have tried both services, and really, they are good.

They have only one inconvenient: they are both alarmingly expensive. Their basic plans can set you back at 250$ per year, and it only goes up from there. Unfortunately, most people can’t justify that expense for a website with some dozen visitors per day.


This is where comes into the game. I’ve developed Fendera using the latest cloud computing technologies: Docker + Kubernetes. Kubernetes is legendary; created by Google as a way to ensure services such as YouTube or Gmail were available 99.99999% of the time and later released to the community. It’s not by chance that it integrates very nicely with Google Cloud, arguably one of the best cloud computing provider.

I wanted to break apart from how GoDaddy did things, and I wanted to stay close to Getflywheel or WPEngine but without forgetting about affordability. Our current plan sets you back at only 34$ per year, yet it comes with free SSL certificates, free weekly backups,  6Gb of SSD and a lot of horse power under Google Cloud’s hood. Moreover, thanks to Kubernetes we can guarantee great up times SLAs and ensure a safe and reliable service.

If you want to give it a try, please register at Questions or feedback are welcome!