Округление в Python NumPy при добавлении узлов в Networkx

Откуда я могу получить конечный 0 или 9? На каждом шаге я проверял, не возникают ли проблемы с округлением, и получил правильные результаты. Однако, когда я добавляю эти числа к графику, возникают проблемы с округлением.

Мой полный код следующий:

from __future__ import division
from math import sqrt
import networkx as nx
import numpy as np
from decimal import Decimal

n=4   #n is the nummber of steps in the graph.
a = np.array([ 1.1656,  1.0125,  0.8594])

g=nx.DiGraph() #I initiate the graph

#f2 checks for equal nodes and removes them
def f2(seq): 
    checked = []
    for e in seq:
        if (e not in checked):
            checked.append(e)
    return np.asarray(checked)

root = np.array([1])
existing_nodes = np.array([1])
previous_step_nodes = np.array([1])
nodes_to_add =np.empty(0)
clean = np.array([1])

for step in range(1,n):
    nodes_to_add=np.empty(0)
    for values in previous_step_nodes:
        nodes_to_add = np.append(nodes_to_add,values*a)

    print "--------"
    print "*****nodes to add ****" + str(f2(np.round(nodes_to_add,4)))
    print "clean = " + str(clean) + "\n"
    #Up to here, the code generates the nodes I will need 

    # This for loop makes the edges and adds the nodes.
    for node in clean:
        for next_node in np.round(node*a,4):
            print str(node ) + "     "  + str( next_node)
            g.add_edge(np.round(node,4), np.round(next_node,4))
#            g.add_edge(Decimal(np.round(node,4)).quantize(Decimal('1.0000')), Decimal(np.round(next_node,4)).quantize(Decimal('1.0000')))

    previous_step_nodes = f2(nodes_to_add)
    clean = f2(np.round(previous_step_nodes,4))
#    g.add_nodes_from(clean)

    print "\n step" + str(step) + " \n"
    print " Current Step :" + "Number of nodes = " + str(len(f2(np.round(previous_step_nodes,4))))
    print clean

print "How many nodes are there ? " +str(len(g.nodes()))

Этот код работает и выводит очень четкое описание графика, что мне и нужно. Однако, когда я печатаю список узлов, чтобы убедиться, что граф содержит только то количество узлов, которое мне нужно, я получаю:

How many nodes are there ? 22
[1, 0.88109999999999999, 1.0143, 1.038, 0.74780000000000002, 
1.1801999999999999, 1.3755999999999999, 1.0142, 0.8609, 
0.88100000000000001, 0.85940000000000005, 1.1656,
1.1950000000000001, 1.0125, 1.5835999999999999, 1.0017, 
0.87009999999999998, 1.1676,
0.63480000000000003, 0.73860000000000003, 1.3586, 1.0251999999999999]

Это явно проблема, которая делает мою программу бесполезной. 0,88109999999999999 и 0,881000000000000001 — это один и тот же узел.

Итак, после проверки stackoverflow в течение нескольких дней, я пришел к выводу, что единственный способ решить проблему - использовать Decimal(). Итак, я заменил:

g.add_edge(np.round(node,4), np.round(next_node,4))

с

g.add_edge(Decimal(np.round(node,4)).quantize(Decimal('1.0000')), 
           Decimal(np.round(next_node,4)).quantize(Decimal('1.0000')))

Однако результат был не таким, как я ожидал: потому что

0.88109999999999999 = 0.8811
0.88100000000000001 =0.8810, 

поэтому Python по-прежнему считает их разными числами.

В идеале я бы предпочел не усложнять код с помощью Decimal() и хотел бы отрезать десятичные знаки так, чтобы 0,88109999999999999 = 0,88100000000000001 = 0,8810, но я понятия не имею, как решить эту проблему.

Благодаря вашим ответам я обновил свой код. Я принял предложение использовать f2 как:

def f2(seq): 
    near_equal = lambda x, y: abs(x - y) < 1.e-5
    checked = []
    for e in seq:
        if all([not near_equal(e, x) for x in checked]):
            checked.append(e)
    return np.asarray(checked)

и я удалил все numpy.round(), потому что, если я могу удалить «похожие» узлы, мне вообще не нужно округление.

Однако python по-прежнему не может различать узлы:

g.nodes() выводит 23 узла, хотя их должно быть только 20: (Примечание: я пробовал это при изменении уровня допуска 1.e-5, но не получил ничего другого)

Сколько узлов? 23

[0.63474091729864457, 0.73858020442900385, 0.74781245698436638,
 0.85940689107605128, 0.86088399667008808, 0.86088399667008819,
 0.87014947721450187, 0.88102634567968308, 0.88102634567968319,
 1, 1.00171875, 1.0125, 1.0142402343749999, 1.02515625,
 1.0379707031249998, 1.1655931089239486, 1.1675964720799117,
 1.180163022785498, 1.1949150605703167, 1.358607295570996,
 1.3755898867656333, 1.3755898867656335, 1.5835833014513552]

Это потому что: 0.86088399667008808, 0.86088399667008819; 0,88102634567968308, 0,88102634567968319 и 1,3755898867656333, 1,3755898867656335 по-прежнему рассматриваются как разные узлы.

Полный код:

from __future__ import division
from math import sqrt
import networkx as nx
import numpy as np
import matplotlib.pyplot as plt

mu1 = 0.05; sigma1= 0.25
n=4

a0=1
a1 = 1 + mu1/n + sigma1*sqrt(3)/sqrt(2*n)
a2 = 1 + mu1/n
a3 = 1 + mu1 /n - sigma1*sqrt(3)/sqrt(2*n)
a = np.array([a1,a2,a3])

print " a = " + str(a)

g=nx.DiGraph() #I initiate the graph

def f2(seq): 
    near_equal = lambda x, y: abs(x - y) < 1.e-5
    checked = []
    for e in seq:
        if all([not near_equal(e, x) for x in checked]):
            checked.append(e)
    return np.asarray(checked)

root = np.array([1])
existing_nodes = np.array([1])
previous_step_nodes = np.array([1])
nodes_to_add =np.empty(0)
clean = np.array([1])

print "________________This Makes the Nodes____________________________________"
for step in range(1,n):
    nodes_to_add=np.empty(0)
    for values in previous_step_nodes:
        nodes_to_add = np.append(nodes_to_add,values*a)
    print "--------"    
    print "*****nodes to add ****" + str(f2(nodes_to_add))
    print "clean = " + str(clean) + "\n"
    #Up to here, the code generates the nodes I will need 

    # This for loop makes the edges and adds the nodes.
    for node in clean:
        for next_node in node*a:
            print str(node ) + "     "  + str( next_node)
            g.add_edge(node, next_node)

    previous_step_nodes = f2(nodes_to_add)
    clean = f2(previous_step_nodes)
#    g.add_nodes_from(clean)

    print "\n step" + str(step) + " \n"
    print " Current Step :" + "Number of nodes = " + str(len(f2(previous_step_nodes)))
    print clean

print "______________End of the Nodes_________________________________"
print "How many nodes are there ? " +str(len(g.nodes()))
print sorted(g.nodes())

Результат:

Сколько узлов? 23 [0.63474091729864457, 0.73858020442900385, 0.74781245698436638, 0.85940689107605128, 0.86088399667008808, 0.86088399667008819, 0.87014947721450187, 0.88102634567968308, 0.88102634567968319, 1, 1.00171875, 1.0125, 1.0142402343749999, 1.02515625, 1.0379707031249998, 1.1655931089239486, 1.1675964720799117, 1.180163022785498, 1.1949150605703167, 1.358607295570996, 1.3755898867656333, 1.3755898867656335, 1.5835833014513552]


person Oniropolo    schedule 23.04.2013    source источник


Ответы (1)


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

Если вы не имеете дело с очень близкими узлами, вы можете изменить свою функцию f2 примерно следующим образом (вы можете сделать допуск переменной):

def f2(seq): 
    near_equal = lambda x, y: abs(x - y) < 1.e-8
    checked = []
    for e in seq:
        if all([not near_equal(e, x) for x in checked]):
            checked.append(e)
    return np.asarray(checked)

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

nodes_without_dupes = list(set(nodes_to_add)) 
person bogatron    schedule 23.04.2013
comment
Я смущен. Я принял ваше предложение и изменил свою функцию f2. Добавление вашего f2 ничего не меняет. Моя программа по-прежнему рассматривает эти узлы как разные. Я изменил допуск, чтобы убедиться, что проблема не в этом. - person Oniropolo; 23.04.2013
comment
Кроме того, я удалил все numpy.round(), потому что, насколько я понял, если f2 удаляет похожие узлы, то нет смысла округлять значения. Правильно? Тем не менее, я все еще получаю то же решение, что и раньше. - person Oniropolo; 23.04.2013
comment
Да, вызовы round следует удалить. Трудно сказать, что происходит, потому что я не вижу измененной версии кода, который вы используете, но когда я применяю f2, который я предложил (скопированный из вашего кода выше) к списку из 23 элементов, он возвращает 20 элементов , как и ожидалось. Вы удалили все звонки на round? - person bogatron; 23.04.2013
comment
Я выложил полный код. Я сумасшедший? Я все еще получаю 23 узла. Большое спасибо! - person Oniropolo; 23.04.2013
comment
Опять же, функция f2 работает, поэтому я думаю, что на ваш первоначальный вопрос дан ответ. Похоже, вы имеете дело с другой проблемой здесь. Это может быть связано с тем, что первое значение clean (в вашей первой итерации) устанавливается независимо от nodes_to_add, и эти значения добавляются к g без предварительной фильтрации. - person bogatron; 23.04.2013
comment
О, так проблема должна быть в петле... Спасибо! Буду тщательно пересматривать! - person Oniropolo; 23.04.2013