2015-03-20 2 views
0

Представьте себе, что существует 10 домов, где может быть одно бесконечное количество людей. Каждый из этих лиц отправляет несколько сообщений, содержащих их идентификатор пользователя и номер дома. Это может быть от 1 до бесконечного количества сообщений. Я хочу знать среднее количество сообщений, которые отправляются каждым человеком, для каждого дома, для последующего заговора, в котором дом получил наибольшее количество средних сообщений.Получите плотность сообщений на разных ID

Теперь, что я объяснил концептуально, дома не дома, а широты, от f.ex -90 до -89 и т. Д. И что человек может отправлять сообщения из разных домов.

Итак, у меня есть база данных с широтой и senderID. Я хочу, чтобы построить плотность широты рг уникальный SenderId:

Number of rows/Number of unique userids на каждой широте в течение интервала

Это входной пример:

lat = [-83.76, -44.88, -38.36, -35.50, -33.99, -31.91, -27.56, -22.95, 
     40.72, 47.59, 54.42, 63.84, 76.77, 77.43, 78.54] 

userid= [5, 7, 6, 6, 6, 6, 5, 2, 
     2, 2, 1, 5, 10, 9 ,8] 

Вот соответствующие плотности:

-80 to -90: 1 
-40 to -50: 1 
-30 to -40: 4 
-20 to -30: 1 
    40 to 50: 2 
    50 to 60: 1 
    60 to 70: 1 
    70 to 80: 1 

Другой вход:

lat = [70,70,70,70,70,80,80,80] 
userid = [1,2,3,4,5,1,1,2] 

Плотность для широты 70 равна 1, а плотность для широты 80 - 1,5.

Если бы я сделал это с помощью запросов к базе данных/псевдокод я хотел бы сделать что-то вроде:

SELECT count(latitude) FROM messages WHERE latitude < 79 AND latitude > 69 
SELECT count(distinct userid) FROM messages WHERE latitude < 79 AND latitude > 69 

Плотность затем будет count(latitude)/count(distinct userid) - также быть истолковано как totalmessagesFromCertainLatitude/distinctUserIds. Это будет повторяться с интервалами от -90 до 90, то есть -90<latitude<-89 до 89<latitude<90

Чтобы получить помощь в этом, возможно, далеко, но я просто не могу организовать свои мысли, чтобы сделать это, пока я уверен, что есть нет ошибок. Я был бы рад за что угодно. Извините, если я не понял.

+0

Если вам нужно решение в Python, а затем удалить тег sqlite. –

+1

Интересный вопрос. Я не уверен, что смогу выполнить ваш вопрос или код. Плотность, о которой вы говорите, будет определяться на широту или диапазон широты. Но гистограмма, которую вы планируете, забивается на плотность. Что вы на самом деле хотите увидеть?Я думаю, может быть, плотность на каждый лот, но я не уверен. Можешь подтвердить? У меня есть решение для этого, если это то, что вы хотите. –

+0

@ j-richard-snape Спасибо за ваш интерес. Я хочу построить количество сообщений pr уникального идентификатора пользователя по всем широтам. Это было яснее? Я верю, что плотность плотности. Если вы посмотрите на мой пример кода SQLite, я хочу, чтобы для каждой широты. – bjornasm

ответ

1

Я не уверен на 100%. Я понял результат, который вы хотите, но это даст ступенчатый, совокупный график, подобный гистограмме, с осью x, которая является широтой (binned), а ось y равна плотности, которую вы определите выше.

Из вашего образца кода у вас уже установлено numpy и с удовольствием его используем. Подход, который я хотел бы сделать, состоит в том, чтобы получить два набора данных, как то, что будет возвращено вашим образцом SQL, а затем использовать их для получения плотности, а затем заговора.Использование существующего формата широта/идентификатор данных - это может выглядеть примерно так

EDIT: Удалены первую версию кода здесь и некоторые замечания, которые были лишними следующие разъяснения и вопрос редактирует от OP


следующие замечания и О.П. уточнение - я думаю, что это то, что требуется:

import numpy as np 
import matplotlib.pyplot as plt 
from itertools import groupby 

import numpy as np 
import matplotlib.pyplot as plt 
from itertools import groupby 

def draw_hist(latitudes,userids): 
    min_lat = -90 
    max_lat = 90 
    binwidth = 1 

    bin_range = np.arange(min_lat,max_lat,binwidth) 

    all_rows = zip(latitudes,userids) 
    binned_latitudes = np.digitize(latitudes,bin_range) 
    all_in_bins = zip(binned_latitudes,userids) 
    unique_in_bins = list(set(all_in_bins)) 
    all_in_bins.sort() 
    unique_in_bins.sort() 

    bin_count_all = [] 
    for bin, group in groupby(all_in_bins, lambda x: x[0]): 
     bin_count_all += [(bin, len([k for k in group]))] 

    bin_count_unique = [] 
    for bin, group in groupby(unique_in_bins, lambda x: x[0]): 
     bin_count_unique += [(bin, len([ k for k in group]))]   

    # bin_count_all and bin_count_unique now contain the data 
    # corresponding to the SQL/pseudocode in your question 
    # for each latitude bin 

    bin_density = [(bin_range[b-1],a*1.0/u) for ((b,a),(_,u)) in zip(bin_count_all, bin_count_unique)] 

    bin_density = np.array(bin_density).transpose() 

    # plot as standard bar - note you can put uneven widths in 
    # as an array-like here if necessary 
    # the * simply unpacks the x and y values from the density 
    plt.bar(*bin_density, width=binwidth) 
    plt.show() 
    # can save away plot here if desired 


latitudes = [-70.5, 5.3, 70.32, 70.43, 5, 32, 80, 80, 87.3] 
userids = [1,1,2,2,4,5,1,1,2] 

draw_hist(latitudes,userids) 

Пример выходных данных с различными бинов ширин на OP наборе

Output with bin widths 0.1, 1 and 10

+0

Еще раз спасибо за помощь. Я получить справедливую долю предупреждений, когда я запускаю код: RuntimeWarning: деление на ноль встречается в разделяй bin_density = all_in_bins * 1.0/unique_in_bins RuntimeWarning: недействительное значение встречается в разделяй bin_density = all_in_bins * 1.0/unique_in_bins – bjornasm

+0

благодарственное вы снова, но я не совсем понимаю, что вы имели в виду с предупреждением, потому что мои программы разбиваются, и я получаю различные ошибки: http://pastebin.com/ynAH1dK7 Список широт до тех пор, пока пользовательские идентификаторы, поэтому всегда должен быть подключен идентификатор пользователя с широтой, если я правильно понял? – bjornasm

+1

Что-то я не ожидал, потому что 'numpy.unique' обрабатывал список кортежей по-разному на версии, которую я установил здесь, и на работе. Очевидно, это не надежное решение для вас - так что возвращайтесь к чертежной доске для меня. Я, вероятно, отвечу на этот ответ, если я не смогу отладить его сегодня вечером - не хочу оставлять сломанный раствор. –

0

Я думаю, что это решает случай, Allthough он не эффективен на всех:

con = lite.connect(databasepath) 
binwidth = 1 
latitudes = [] 
userids = [] 
info = [] 
densities = [] 
with con: 
    cur = con.cursor() 
    cur.execute('SELECT latitude, userid FROM dynamicMessage') 
    con.commit() 
    print "executed" 
    while True: 
     tmp = cur.fetchone() 
     if tmp != None: 
      info.append([float(tmp[0]),float(tmp[1])]) 
     else: 
      break 
    info = sorted(info, key=itemgetter(0)) 
    for x in info: 
     latitudes.append(x[0]) 
     userids.append(x[1]) 
    x = 0 
    latitudecount = 0 
    for b in range(int(min(latitudes)),int(max(latitudes))+1): 
     numlatitudes = sum(i<b for i in latitudes) 
     if numlatitudes > 1: 
      tempdensities = latitudes[0:numlatitudes] 
      latitudes = latitudes[numlatitudes:] 
      tempuserids = userids[0:numlatitudes] 
      userids = userids[numlatitudes:] 
      density = numlatitudes/len(list(set(tempuserids))) 
      if density>1: 
       tempdensities = [b]*int(density) 
       densities.extend(tempdensities) 
    plt.hist(densities, bins=len(list(set(densities)))) 
    plt.savefig('latlongstats'+'t'+str(time.strftime("%H:%M:%S")), format='png') 
2

Поскольку это пакеты так аккуратно в pandas ', это, вероятно, быстро в пандах для больших наборов данных.

lat = [-83.76, -44.88, -38.36, -35.50, -33.99, -31.91, -27.56, -22.95, 
     40.72, 47.59, 54.42, 63.84, 76.77, 77.43, 78.54] 

userid= [5, 7, 6, 6, 6, 6, 5, 2, 
     2, 2, 1, 5, 10, 9 ,8] 
import pandas as pd 
import matplotlib.pyplot as plt 
from matplotlib.patches import Rectangle 
from matplotlib.collections import PatchCollection 
from math import floor 

df = pd.DataFrame(zip(userid,lat), columns = ['userid','lat'] 
) 
df['zone'] = map(lambda x: floor(x) * 10,df.lat/10) # for ten-degree zones 
zonewidth=10 
#df['zone'] = map(floor, df.lat) # for one-degree zones 
#zonewidth=1 # ditto 

dfz = df.groupby('zone') #returns a dict of dataframes 

#for k, v in dfz: # useful for exploring the GroupBy object 
# print(k, v.userid.values, float(len(v.userid.values))/len(set(v.userid.values))) 

p = [(k, float(len(v.userid.values))/len(set(v.userid.values))) for k, v in dfz] 

# plotting could be tightened up -- PatchCollection? 
R = [Rectangle((x, 0), zonewidth, y, facecolor='red', edgecolor='black',fill=True) for x, y in p] 
fig, ax = plt.subplots() 
for r in R: 
    ax.add_patch(r) 
plt.xlim((-90, 90)) 
tall = max([r.get_height() for r in R]) 
plt.ylim((0, tall + 0.5)) 
plt.show() 

Для первого набора тестовых данных:

enter image description here

+0

Интересно - не могли бы вы объяснить, что делает каждый шаг? первый набор df, а также tf ['zone']? Получается ли это, как объясняется, плотность? – bjornasm

+1

Ну, я добавил комментарии к коду, но (смотря строго над очками), вы должны были начать с публикации правильного вывода для ваших данных образца, чтобы мы все знали, вычисляем ли вы то, что вы просили. – cphlewis

+0

Я очень ценю это, и я обновил свой первый вопрос двумя входами вместе с правильным выходом. – bjornasm

0

То, что следует не является полным решением с точки зрения построения требуемой гистограммы, но я думаю, что это все-таки достоин сообщается

  1. Основная часть решения, мы сканируем массив кортежей, чтобы выбрать их в требуемом диапазоне, и мы рассчитываем

    • количество выбранных кортежей
    • уникальных идентификаторов, используя трюк, заключающийся в создании набора (отбрасывается автоматически дубликаты) и вычисления его множественность

    в конечном счете мы возвращать требуемое соотношение или ноль если количество различных идентификаторов равен нулю

    def ratio(d, mn, mx): 
        tmp = [(lat, uid) for lat, uid in d if mn <= lat < mx] 
        nlats, nduids = len(tmp), len({t[1] for t in tmp}) 
        return 1.0*nlats/nduids if nduids>0 else 0 
    
  2. данные вводятся и назначаются через zip, в список кортежей

    lat = [-83.76, -44.88, -38.36, -35.50, -33.99, -31.91, -27.56, -22.95, 
         -19.00, -12.32, -6.14, -1.11, 4.40, 10.23, 19.40, 31.18, 
         40.72, 47.59, 54.42, 63.84, 76.77] 
    userid= [52500.0, 70100.0, 35310.0, 47776.0, 70100.0, 30991.0, 37328.0, 25575.0, 
         37232.0, 6360.0, 52908.0, 52908.0, 52908.0, 77500.0, 345.0, 6360.0, 
          3670.0, 36690.0, 3720.0, 2510.0, 2730.0] 
    data = zip(lat,userid) 
    
  3. подготовка бункеров

    extremes = range(-90,91,10) 
    intervals = zip(extremes[:-1],extremes[1:]) 
    
  4. фактического вычисления, результат представляет собой список float с, которые могут быть переданы в соответствующие pyplot функции

    ratios = [ratio(data,*i) for i in intervals] 
    print ratios 
    # [1.0, 0, 0, 0, 1.0, 1.0, 1.0, 1.0, 2.0, 1.0, 1.0, 0, 1.0, 1.0, 1.0, 1.0, 1.0, 0] 
    
Смежные вопросы