In [606]:
from grab import Grab
from lxml.html import fromstring
from pandas import DataFrame
from pylab import *
import brewer2mpl
from matplotlib import rcParams
import matplotlib.pyplot as plt
In [2]:
g = Grab()
g.go('http://tjournal.ru/users')
people = g.doc.select('//a[@class="line"]')

К сожалению, из-за того что в ipython как-то странно обрабатываются строки, мне пришлось использовать Beautiful Soup, чтобы достать нужные значения из таблички, что убивает прелесть использования Граба

In [3]:
from bs4 import BeautifulSoup
people = g.doc.select('//a[@class="line"]')

name = []
link = []
comments=[]
karma = []


for item in people:
    soup = BeautifulSoup(item.html())
    link.append(soup.a['href'])
    name.append(soup.a.i.get_text())
    comments.append( int(soup.find_all('div')[1].b.text) )
    karma.append( int(soup.find_all('div')[2].b.text.replace(" ", "")))
    
/users/7541

In [5]:
#colorbrewer2 Dark2 qualitative color table
dark2_colors = brewer2mpl.get_map('Dark2', 'Qualitative', 7).mpl_colors

rcParams['figure.figsize'] = (10, 6)
rcParams['figure.dpi'] = 150
rcParams['axes.color_cycle'] = dark2_colors
rcParams['lines.linewidth'] = 2
rcParams['axes.facecolor'] = 'white'
rcParams['font.size'] = 14
rcParams['patch.edgecolor'] = 'white'
rcParams['patch.facecolor'] = dark2_colors[0]
rcParams['font.family'] = 'StixGeneral'
проводятся исследования сейчас, и как этот процесс можно улучшить. Для начала ты находишь интересный источник данных. Затем ты эти данные собираешь, 
def remove_border(axes=None, top=False, right=False, left=True, bottom=True):
    """
    Minimize chartjunk by stripping out unnecesasry plot borders and axis ticks
    
    The top/right/left/bottom keywords toggle whether the corresponding plot border is drawn
    """
    ax = axes or plt.gca()
    ax.spines['top'].set_visible(top)
    ax.spines['right'].set_visible(right)
    ax.spines['left'].set_visible(left)
    ax.spines['bottom'].set_visible(bottom)
    
    #turn off all ticks
    ax.yaxis.set_ticks_position('none')
    ax.xaxis.set_ticks_position('none')
    
    #now re-enable visibles
    if top:
        ax.xaxis.tick_top()
    if bottom:
        ax.xaxis.tick_bottom()
    if left:
        ax.yaxis.tick_left()
    if right:
        ax.yaxis.tick_right()
In [583]:
tj = DataFrame({'name' : name, 'karma' : karma, 'comments' : comments})
In [584]:
tj.head()
Out[584]:
comments karma name
0 177 1684 Вязаный ТвитерХранитель TJ
1 448 1583 Никита ГолоденкоРезидент
2 590 1413 it-trendРезидент
3 346 1300 Влад ЧиковРезидент
4 413 1114 King Of MagicРезидент

Что приходит на ум, когда мы видим эту табличку? Естественно, что хочется узнать, насколько много комментариев собирает каждый человек. Поделим карму на количество комментариев, и обзовём этот параметр "крутостью":

In [585]:
tj['awesomeness'] = tj.karma/tj.comments

А теперь уже посмотрим на описательные статистики, среднее значение, квантили и дисперсию:

In [567]:
tj.describe()
Out[567]:
comments karma awesomeness
count 300.000000 300.000000 300.000000
mean 42.466667 134.606667 5.863333
std 66.455121 204.105403 11.254341
min 1.000000 35.000000 0.000000
25% 11.000000 46.000000 2.000000
50% 23.000000 71.000000 3.000000
75% 42.000000 129.750000 6.000000
max 590.000000 1684.000000 145.000000

Это как раз тот случай, когда активные комментаторы, попавшие в рейтинги могут заставить нас подумать, что большинство людей из списка оставляет в среднем комментария, но это не так. На самом деле, медиана показывает, что большинство людей, чтобы попасть в список оставило 23 комментария.

Тоже самое оказывается верным и для кармы. Есть ребята, которые получили значительно больше кармы в среднем, чем остальные, что даёт нам 134 очка кармы для наших топовых 300 комментаторов, тогда как большинство всё-таки получило около 71 очков кармы.

Опять-таки из-за наличия в выборке людей, которые "срывают куш", мы снова смотрим на медиану крутости и понимаем, что в основном комментарии на сайте собирают около 3х плюсиков.

Также по этой табличке мы видим, что кто-то был настолько активен, что оставил аж 590 комментариев. Посмотрим, кто это был.

In [568]:
tj[tj.comments==590]
Out[568]:
comments karma name awesomeness
2 590 1413 it-trendРезидент 2

Кстати, индекс у нас построился на основе того, как были сначала отсортированны данные (что заставило изобрести небольшие костыли, чтобы работать с данными чуть ниже, но не суть), поэтому мы видим, что it-trend-у его активность принесла 3 место в рейтинге.

Ещё интеренсо посмотреть, кто же (постил больше всех смешных кратинок) наиболее удачно комментирует посты на протяжении достаточно долгого времени.

In [571]:
tj.sort(['awesomeness'],ascending=False).head()
Out[571]:
comments karma name awesomeness
66 1 145 LambdaDelta 145
140 1 77 Кельпи 77
173 1 61 Nikita Shulaev 61
250 1 43 Buck Fen 43
290 1 37 Роман Спрятан 37

Мы видим вполне ожидаемую ситуацию, при которой несколько человек, запостив 1 комментарий получили кучу плюсиков и попали в этот топ. Всё-таки интереснее знать, чьи комментарии реально стоит читать.

Для этого возьмём в качестве критического значения 11 комментариев, потому что 75% участников списка оставили больше этого числа (25% перцентиль распределения комментариев).

In [578]:
tj[tj.comments>11].sort(['awesomeness'],ascending=False)[:20]
Out[578]:
comments karma name awesomeness
22 19 306 Георгий ЛобушкинВКонтакте 16
27 23 283 Уборщица «Ленты.Ру» 12
72 12 137 Андрей Коняев 11
16 38 397 RIP Новости 10
0 177 1684 Вязаный ТвитерХранитель TJ 9
31 28 266 Влад 9
47 19 187 Александр ПегановВладелец бутика 9
74 14 135 Oleg ب_ب 9
26 32 285 Генс Йонбирг 8
84 15 117 Эрнест Макаронин 7
18 53 377 Сергей Д 7
17 55 394 Лось 7
29 36 280 Ольга Ефимова 7
78 20 124 Timur Kalandarov 6
19 58 354 Андрей Жаровин 6
54 26 165 Кирилл Соколов 6
59 24 159 Открыто 6
10 81 502 Михаил КафановIronyProduction 6
102 15 100 Udjin Onblack 6
23 59 295 ВьетКафе 5

Мы же так и не узнали пока 20 топ-комментариев. Ну точнее, мы видели эту статистику на офицальном сайте, но мы же посчитали наш параметр "крутости":

In [587]:
tj.sort(['karma','awesomeness'],ascending=False)[:20]
Out[587]:
comments karma name awesomeness
0 177 1684 Вязаный ТвитерХранитель TJ 9
1 448 1583 Никита ГолоденкоРезидент 3
2 590 1413 it-trendРезидент 2
3 346 1300 Влад ЧиковРезидент 3
4 413 1114 King Of MagicРезидент 2
5 151 789 Евгений Кривых 5
6 203 752 Andrey Stavitskiy 3
7 228 656 хрустРезидент 2
8 278 594 Kokoulin Nikolay 2
9 265 543 Kult Morkovki 2
10 81 502 Михаил КафановIronyProduction 6
11 132 477 Дмитрий Усольцев 3
12 236 474 Вечерние чтения 2
13 130 406 Дмитрий Ашихмин 3
14 82 401 Вадим ЕлистратовTJournal 4
15 67 400 Tony Montana 5
16 38 397 RIP Новости 10
17 55 394 Лось 7
18 53 377 Сергей Д 7
19 58 354 Андрей Жаровин 6

А теперь посмотрим на тех, кто оставил больше всего комментариев:

In [590]:
tj.sort(['comments','awesomeness'],ascending=False)[:20]                                                    
Out[590]:
comments karma name awesomeness
2 590 1413 it-trendРезидент 2
1 448 1583 Никита ГолоденкоРезидент 3
4 413 1114 King Of MagicРезидент 2
3 346 1300 Влад ЧиковРезидент 3
34 296 252 Пасютин Макс 0
8 278 594 Kokoulin Nikolay 2
9 265 543 Kult Morkovki 2
12 236 474 Вечерние чтения 2
7 228 656 хрустРезидент 2
6 203 752 Andrey Stavitskiy 3
38 200 232 Mike Kosulin 1
49 200 183 Anisim 0
68 188 140 Ich bin Klaus 0
0 177 1684 Вязаный ТвитерХранитель TJ 9
24 156 294 Loki Depressed 1
5 151 789 Евгений Кривых 5
61 133 153 Маврикий 1
11 132 477 Дмитрий Усольцев 3
13 130 406 Дмитрий Ашихмин 3
48 122 185 Ryzhov_S 1

Поскольку выборка у нас очень маленькая, да и TJ молодой ресурс, мы даже не можем сказать, что эти люди особо флудили, чтобы попасть в рейтинг, потому что медиана "крутости" получилась около 3.

Теперь посмотрим на то, что в итоге отняло у меня больше всего времени. Графики

In [611]:
plt.figure(figsize=(10, 150))

tj_sorted = tj.sort('karma',ascending=False)
change = tj_sorted.karma
city = tj_sorted.name
pos = numpy.arange(len(tj.karma))
plt.barh(pos,change)

for p, c, ch in zip(pos, city, change):
    plt.annotate(str(ch), xy=(ch + 1, p + .5), va='center')
    
#cutomize ticks
ticks = plt.yticks(pos + .5, city)
xt = plt.xticks()[0]
plt.xticks(xt, [' '] * len(xt))

#minimize chartjunk
remove_border(left=False, bottom=False)
plt.grid(axis = 'x', color ='white', linestyle='-')