2013-10-08 3 views
1

У меня есть 2d словаря в следующем формате:Написать 2d словарь в dataframe или текстового файла, используя Python

myDict = {('a','b'):10, ('a','c'):20, ('a','d'):30, ('b','c'):40, ('b','d'):50,('c','d'):60} 

Как я могу написать это в табуляцией файл, так что файл содержит следующее. При заполнении кортежа (x, y) будут заполняться два местоположения: (x, y) и (y, x). (Х, х) всегда 0.

Выход будет:

a b c d 
a 0 10 20 30 
b 10 0 40 50 
c 20 40 0 60 
d 30 50 60 0 

PS: Если как-то словарь может быть преобразован в dataframe (используя панда), то его можно легко записать в файл используя панды функционировать

ответ

4
In [49]: data = map(list, zip(*myDict.keys())) + [myDict.values()] 

In [50]: df = DataFrame(zip(*data)).set_index([0, 1])[2].unstack() 

In [52]: df.combine_first(df.T).fillna(0) 
Out[52]: 
    a b c d 
a 0 10 20 30 
b 10 0 40 50 
c 20 40 0 60 
d 30 50 60 0 

Для потомков: Если вы только настраивались, проверить ответ Phillip Клауда ниже для аккуратного способа построения df.

+0

Ну, я думаю, что волна повернулась для вас :) –

+0

Ha! Спасибо за помощь, приятель. –

1

не так элегантно, как хотелось бы (и не использовать панд), но пока вы не найдете что-то лучше:

adj = dict() 
for ((u, v), w) in myDict.items(): 
    if u not in adj: adj[u] = dict() 
    if v not in adj: adj[v] = dict() 
    adj[u][v] = adj[v][u] = w 
keys = adj.keys() 

print '\t' + '\t'.join(keys) 
for u in keys: 
    def f(v): 
    try: 
     return str(adj[u][v]) 
    except KeyError: 
     return "0" 
    print u + '\t' + '\t'.join(f(v) for v in keys) 

или что то же самое (если вы не хотите, чтобы построить adjace матрица NCY):

k = dict() 
for ((u, v), w) in myDict.items(): 
    k[u] = k[v] = True 
keys = k.keys() 

print '\t' + '\t'.join(keys) 
for u in keys: 
    def f(v): 
    if (u, v) in myDict: 
     return str(myDict[(u, v)]) 
    elif (v, u) in myDict: 
     return str(myDict[(v, u)]) 
    else: 
     return "0" 
    print u + '\t' + '\t'.join(f(v) for v in keys) 
5

Вы можете сделать это с менее известной align метода и немного unstack магии:

In [122]: s = Series(myDict, index=MultiIndex.from_tuples(myDict)) 

In [123]: df = s.unstack() 

In [124]: lhs, rhs = df.align(df.T) 

In [125]: res = lhs.add(rhs, fill_value=0).fillna(0) 

In [126]: res 
Out[126]: 
    a b c d 
a 0 10 20 30 
b 10 0 40 50 
c 20 40 0 60 
d 30 50 60 0 

Наконец, чтобы написать это в файл CSV, используйте to_csv метод:

In [128]: res.to_csv('res.csv', sep='\t') 

In [129]: !cat res.csv 
     a  b  c  d 
a  0.0  10.0 20.0 30.0 
b  10.0 0.0  40.0 50.0 
c  20.0 40.0 0.0  60.0 
d  30.0 50.0 60.0 0.0 

Если вы хотите сохранить что-то целым числом, то используйте DataFrame.astype(), например:

In [137]: res.astype(int).to_csv('res.csv', sep='\t') 

In [138]: !cat res.csv 
     a  b  c  d 
a  0  10  20  30 
b  10  0  40  50 
c  20  40  0  60 
d  30  50  60  0 

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

ответ @Dan Аллана с помощью combine_first приятно:

In [130]: df.combine_first(df.T).fillna(0) 
Out[130]: 
    a b c d 
a 0 10 20 30 
b 10 0 40 50 
c 20 40 0 60 
d 30 50 60 0 

Сроки:

In [134]: timeit df.combine_first(df.T).fillna(0) 
100 loops, best of 3: 2.01 ms per loop 

In [135]: timeit lhs, rhs = df.align(df.T); res = lhs.add(rhs, fill_value=0).fillna(0) 
1000 loops, best of 3: 1.27 ms per loop 

Эти тайминги, вероятно, немного загрязнены из-за затрат на строительство, так что же все выглядит с помощью некоторых действительно огромных кадров?

In [143]: df = DataFrame({i: randn(1e7) for i in range(1, 11)}) 

In [144]: df2 = DataFrame({i: randn(1e7) for i in range(10)}) 

In [145]: timeit lhs, rhs = df.align(df2); res = lhs.add(rhs, fill_value=0).fillna(0) 
1 loops, best of 3: 4.41 s per loop 

In [146]: timeit df.combine_first(df2).fillna(0) 
1 loops, best of 3: 2.95 s per loop 

DataFrame.combine_first() быстрее для больших рамок.

+0

Хороший анализ. Я удалил свой ответ в стыде, когда увидел вашу превосходную конструкцию, настолько чище, чем все мои молнии. Приятно видеть также, что используется выровненный.Я верну его для '' comb_first'' .... –

+1

Очевидно, вы еще не видели мой грязный er ... 'zip'ping [здесь] (http://stackoverflow.com/a/18581763/564538) , :) –

-2

Получил его, используя pandas.

#Find all column names 
z = [] 
[z.extend(x) for x in myDict.keys()] 
colnames = sorted(set(z)) 

#Create an empty DataFrame using pandas 
myDF = DataFrame(index= colnames, columns = colnames) 
myDF = myDF.fillna(0) #Initialize with zeros 
#Fill each item one by one 
for val in myDict: 
    myDF[val[0]][val[1]] = myDict[val] 
    myDF[val[1]][val[0]] = myDict[val] 

#Write to a file 
outfilename = "matrixCooccurence.txt" 
myDF.to_csv(outfilename, sep="\t", index=True, header=True, index_label = "features") 
+0

Непонятно, что это делает то, что вы изначально задавали. Это также излишне низкоуровневое для панд. 'pandas' спроектирован таким образом, чтобы с легкостью выполнять простые задачи, подобные этим. Например, почему вы создаете нано-кадр только для того, чтобы сразу заполнить его нулями, когда вы можете просто создать одну строку с помощью 'np.zeros'? Вы также используете прикованное присваивание, которое сильно осуждается из-за различий в просмотре vs-copy. Наконец, цикл над значениями в dict wil будет медленнее, чем бросать его в 'Series', который поддерживается массивами' numpy'. –

Смежные вопросы