类型错误:在 Matplotlib 中绘制对数转换序列时,“int”对象没有属性“log”

问题描述 投票:0回答:1

我正在开发一个 Python 项目,在其中计算并绘制等分序列图。我创建了一个 list.py 脚本,用于显示 .db 文件中的数字,结果证明这些数字已正确计算并保存到数据库中。但是,当我尝试使用 graph.py 绘制序列时,遇到以下错误:

Exception in Tkinter callback
AttributeError: 'int' object has no attribute 'log'

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "C:\Python312\Lib\tkinter\__init__.py", line 1967, in __call__
    return self.func(*args)
           ^^^^^^^^^^^^^^^^
  File "graph.py", line 94, in plot_graph    
    plot_sequence(sequence, log_scale=True, log_base=log_base)
  File "graph.py", line 35, in plot_sequence 
    transformed_sequence = np.log(sequence) / np.log(log_base)
                           ^^^^^^^^^^^^^^^^
TypeError: loop of ufunc does not support argument 0 of type int which has no callable log method

这是 graph.py 脚本:

import os
import sqlite3
import tkinter as tk
from tkinter import filedialog, messagebox
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.animation import FuncAnimation

def fetch_sequence_from_db(db_path):
    try:
        conn = sqlite3.connect(db_path)
        c = conn.cursor()
        c.execute("SELECT sequence FROM sequences")
        sequence_str = c.fetchone()[0]
        sequence = [int(x) for x in sequence_str.strip('][').split(', ')]
        conn.close()
        return sequence
    except Exception as e:
        messagebox.showerror("Error", f"Failed to read database: {e}")
        return None

def format_scientific(number):
    exponent = int(np.log10(number))
    base = number / 10**exponent
    return f"{base:.1f} x 10^{exponent}"

def plot_sequence(sequence, log_scale=False, log_base=None):
    fig, ax = plt.subplots(figsize=(10, 5))

    if log_scale and log_base:
        transformed_sequence = np.log(sequence) / np.log(log_base)
        ylabel = f"log_{log_base}(Number)"
    else:
        transformed_sequence = sequence
        ylabel = "Number"

    ax.set_title("Aliquot Sequence")
    ax.set_xlabel("Iteration")
    ax.set_ylabel(ylabel)
    ax.grid(True)
    ax.set_xlim(0, len(transformed_sequence) - 1)
    
    line, = ax.plot([], [], linestyle='-', color='b')

    def update(frame):
        line.set_data(range(frame + 1), transformed_sequence[:frame + 1])
        ax.relim()
        ax.autoscale_view()
        return line,

    ani = FuncAnimation(fig, update, frames=len(transformed_sequence), blit=True, interval=10, repeat=False)
    
    # Find and format the largest number
    largest_number = max(sequence)
    formatted_largest_number = format_scientific(largest_number)
    
    # Display the largest number on the graph
    plt.text(0.5, 1.05, f"Largest number: {formatted_largest_number}", ha='center', va='center', transform=ax.transAxes, fontsize=12)
    
    plt.subplots_adjust(left=0.05, right=0.95)  # Adjusting the left margin to make the 0th iteration close to the border
    plt.show()

def select_file():
    file_path = filedialog.askopenfilename(
        initialdir=os.path.join(os.path.dirname(os.path.abspath(__file__)), "Number_Sequences"),
        title="Select a Database File",
        filetypes=(("Database Files", "*.db"), ("All Files", "*.*"))
    )
    if file_path:
        entry_file_path.delete(0, tk.END)
        entry_file_path.insert(0, file_path)

def plot_graph():
    db_path = entry_file_path.get()
    if not os.path.exists(db_path):
        messagebox.showerror("Error", "File not found.")
        return

    sequence = fetch_sequence_from_db(db_path)
    if not sequence:
        return

    if var_log_transform.get():
        try:
            log_base = float(entry_log_base.get())
        except ValueError:
            messagebox.showerror("Error", "Invalid log base.")
            return
        plot_sequence(sequence, log_scale=True, log_base=log_base)
    else:
        plot_sequence(sequence, log_scale=False)

# Setting up the Tkinter window
root = tk.Tk()
root.title("Aliquot Sequence Grapher")

frame = tk.Frame(root)
frame.pack(padx=10, pady=10)

label_file_path = tk.Label(frame, text="Database File:")
label_file_path.grid(row=0, column=0, sticky="e")

entry_file_path = tk.Entry(frame, width=40)
entry_file_path.grid(row=0, column=1, padx=5)

button_browse = tk.Button(frame, text="Browse", command=select_file)
button_browse.grid(row=0, column=2, padx=5)

var_log_transform = tk.BooleanVar(value=True)
check_log_transform = tk.Checkbutton(frame, text="Log Transform", variable=var_log_transform)
check_log_transform.grid(row=1, column=0, columnspan=3)

label_log_base = tk.Label(frame, text="Log Base:")
label_log_base.grid(row=2, column=0, sticky="e")

entry_log_base = tk.Entry(frame, width=10)
entry_log_base.grid(row=2, column=1, padx=5, sticky="w")
entry_log_base.insert(0, "10")

button_plot = tk.Button(frame, text="Plot Graph", command=plot_graph)
button_plot.grid(row=3, column=0, columnspan=3, pady=10)

root.mainloop()

这是 list.py 中的数字列表。迭代次数仅在 list.py 脚本中显示:

1 : 276
2 : 396
3 : 696
4 : 1104
5 : 1872
6 : 3770
7 : 3790
8 : 3050
9 : 2716
10 : 2772
11 : 5964
12 : 10164
13 : 19628
14 : 19684
15 : 22876
16 : 26404
17 : 30044
18 : 33796
19 : 38780
20 : 54628
21 : 54684
22 : 111300
23 : 263676
24 : 465668
25 : 465724
26 : 465780
27 : 1026060
28 : 2325540
29 : 5335260
30 : 11738916
31 : 23117724
32 : 45956820
33 : 121129260
34 : 266485716
35 : 558454764
36 : 1092873236
37 : 1470806764
38 : 1471882804
39 : 1642613196
40 : 2737688884
41 : 2740114636
42 : 2791337780
43 : 4946860492
44 : 4946860548
45 : 9344070652
46 : 9344070708
47 : 15573451404
48 : 27078171764
49 : 27284104204
50 : 27410152084
51 : 27410152140
52 : 76787720100
53 : 220578719452
54 : 254903331620
55 : 361672366300
56 : 603062136740
57 : 921203207260
58 : 1381419996068
59 : 1395444575644
60 : 1395478688996
61 : 1395546402460
62 : 2069258468900
63 : 3065057872156
64 : 3277068463844
65 : 3429776547484
66 : 3597527970596
67 : 4028517592540
68 : 5641400009252
69 : 5641400009308
70 : 5641400009364
71 : 9709348326636
72 : 16331909651988
73 : 31948891146732
74 : 54770416120644
75 : 100509779504316
76 : 208751080955844
77 : 388416032284476
78 : 749365894850244
79 : 1414070378301756
80 : 2556878765995204
81 : 2556878765995260
82 : 6726041614128900
83 : 15626498692840700
84 : 23762659088671300
85 : 35168735451235260
86 : 78257359358590020
87 : 186897487211247036
88 : 340813223900632644
89 : 592585414385033916
90 : 1326583294186844484
91 : 2594892903616159356
92 : 4946738730471899844
93 : 8244565422068579772
94 : 13740942370114299844
95 : 13780400058385352252
96 : 13780400058385352308
97 : 14272557426581383244
98 : 14272557426581383300
99 : 21155073391000330684
100 : 21374326697892540932
101 : 22138822441861473292

我也有相同的文件,但只有 70 次迭代且数字相同,并且它绘制的图表很好。这是 main.py 脚本,用于计算等分序列(如果需要):

import os
import math
import sqlite3

# Function to get all divisors of a given number
def get_divisors(n):
    divisors = [1]
    for i in range(2, int(math.sqrt(n)) + 1):
        if n % i == 0:
            divisors.append(i)
            if i != n // i:
                divisors.append(n // i)
    if n > 1:
        divisors.append(n)
    return sorted(divisors)

# Function to calculate the sum of divisors for a given number
def sum_of_divisors(n):
    return sum(get_divisors(n)) - n

# Function to calculate the Aliquot Sequence for a given number
def aliquot_sequence(start_number, iterations=None):
    sequence = [start_number]
    current_iteration = 1

    while True:
        next_number = sum_of_divisors(sequence[-1])
        sequence.append(next_number)
        print(f"Iteration {current_iteration}: {next_number}")
        if next_number == 1 or next_number in sequence[:-1] or next_number == sum_of_divisors(next_number):
            break
        if iterations and current_iteration >= iterations:
            break
        current_iteration += 1

    return sequence

# Function to save the result to the database
def save_to_database(number, sequence, iterations=None, continue_from_file=None):
    current_dir = os.path.dirname(os.path.abspath(__file__))
    folder_name = os.path.join(current_dir, "Number Sequences")
    if not os.path.exists(folder_name):
        try:
            os.makedirs(folder_name)
        except OSError as e:
            print(f"Error creating folder: {e}")
            return
    
    if continue_from_file:
        db_file_path = continue_from_file
        original_name, _ = os.path.splitext(os.path.basename(continue_from_file))
        original_number, original_iterations = original_name.split('_')
        original_iterations = int(original_iterations)
        total_iterations = original_iterations + (len(sequence) - 1)
        if sequence[-1] == 1 or sequence[-1] in sequence[:-1] or sequence[-1] == sum_of_divisors(sequence[-1]):
            final_file_name = f"{number}.db"
        else:
            final_file_name = f"{number}_{total_iterations}.db"
        final_db_file_path = os.path.join(folder_name, final_file_name)
    else:
        if iterations:
            file_name = f"{number}_{iterations}.db"
        else:
            file_name = f"{number}.db"
        db_file_path = os.path.join(folder_name, file_name)

    try:
        conn = sqlite3.connect(db_file_path)
        c = conn.cursor()
        c.execute('''CREATE TABLE IF NOT EXISTS sequences (number INTEGER, sequence TEXT)''')
        if continue_from_file:
            c.execute('''SELECT sequence FROM sequences''')
            old_sequence_str = c.fetchone()[0]
            old_sequence = [int(x) for x in old_sequence_str.strip('][').split(', ')]
            combined_sequence = old_sequence + sequence[1:]  # Exclude the initial number from new sequence
            c.execute('''UPDATE sequences SET sequence = ? WHERE number = ?''', (str(combined_sequence), number))
        else:
            c.execute('''INSERT INTO sequences (number, sequence) VALUES (?, ?)''', (number, str(sequence)))
        conn.commit()
        conn.close()

        if continue_from_file and db_file_path != final_db_file_path:
            try:
                os.rename(db_file_path, final_db_file_path)
            except OSError as e:
                print(f"Error renaming file to final name: {e}")

    except sqlite3.Error as e:
        print(f"Database error: {e}")
        return

# Function to check if number exists in the database
def number_exists_in_database(number, iterations=None):
    current_dir = os.path.dirname(os.path.abspath(__file__))
    folder_name = os.path.join(current_dir, "Number Sequences")
    if os.path.exists(folder_name):
        for file in os.listdir(folder_name):
            if file.endswith(".db"):
                db_name, _ = os.path.splitext(file)
                split_name = db_name.split('_')
                db_number = int(split_name[0])
                db_iterations = None if len(split_name) == 1 else int(split_name[1])
                if db_number == number and db_iterations == iterations:
                    return True
    return False

# Main function
def main():
    # Get input from the user
    number = int(input("Enter a number to calculate its Aliquot Sequence: "))
    iterations = input("Enter 'auto' to calculate until stopped automatically, or enter the number of iterations: ")
    if iterations.lower() == 'auto':
        iterations = None
    else:
        iterations = int(iterations)

    continue_calculation = input("Do you want to continue calculation from where you left? (yes/no): ")
    if continue_calculation.lower() == 'yes':
        continue_from_file = input("Enter the file name to continue from: ")
        continue_from_file = os.path.join(os.path.dirname(os.path.abspath(__file__)), "Number Sequences", continue_from_file)
        if not os.path.exists(continue_from_file):
            print("File not found.")
            return
        
        conn = sqlite3.connect(continue_from_file)
        c = conn.cursor()
        c.execute('''SELECT sequence FROM sequences''')
        sequence_str = c.fetchone()[0]
        sequence = [int(x) for x in sequence_str.strip('][').split(', ')]
        conn.close()
        start_number = sequence[-1]  # Start from the last number in the sequence
    else:
        continue_from_file = None
        start_number = number

    # Check if number exists in the database
    if number_exists_in_database(number, iterations):
        print("Sequence already exists in the database.")
        return
    
    # Calculate Aliquot Sequence
    new_sequence = aliquot_sequence(start_number, iterations)
    
    # Print the sequence
    print(f"Aliquot Sequence for {number}: {new_sequence}")
    
    # Save the sequence to the database
    save_to_database(number, new_sequence, iterations, continue_from_file)
    print("Sequence saved to database.")

if __name__ == "__main__":
    main()

确保log_base在使用前转换为float。

python sqlite math logging
1个回答
0
投票

问题始于 fetch_sequence_from_db 函数。 try 块失败,异常返回 None 作为序列值(我相信您的消息框中应该有错误)。

从您的代码来看,您似乎是通过拆分连接数组来生成序列值的:

sequence = [int(x) for x in sequence_str.strip('][').split(', ')]

但是如果您的sequence_str值为'[2, 3, 4][4, 5, 6]',那么您将删除']['并将外部括号留在字符串'[2, 3, 44, 5, 6中]'。 您可以看到,用逗号分割将生成一个列表,其中字符串“[2”和“6]”作为第一个和最后一个值。和中间的字符串“44”。 第一个字符串将使尝试失败,因此,它将返回 None 作为值,并且 np.log(None) 生成您得到的错误。

我建议:

sequence = [int(x) for x in sequence_str[1:-1].replace('][', ', ').split(', ')]
© www.soinside.com 2019 - 2024. All rights reserved.