Экспорт данных сотового автомата в CSV в Python - PullRequest
1 голос
/ 28 апреля 2019

Я работал в клеточных автоматах Reaction-Diffusion с библиотекой cellpylib для курса в моем университете (я написал все это одним сценарием, поэтому вам не нужно ничего устанавливать / загружать). Я хотел бы сохранить эволюцию данных автоматов в CSV-файл для запуска статистики. То есть я хотел бы сохранить данные в столбцах, где первый столбец равен «число из 1», а второй столбец: «временные шаги».

Таким образом, мне нужна помощь в:

(1) Создание переменной, которая сохраняет величину '1' за шаг по времени (я так думаю).

(2) Мне нужно экспортировать все эти данные в CSV-файл (число «1» и соответствующая итерация от 1 до time_steps в приведенном ниже коде).

Код следующий.

#Libraries
import matplotlib
matplotlib.matplotlib_fname()
import matplotlib.pyplot as plt
import matplotlib as mpl
import matplotlib.animation as animation
import numpy as np
import csv

# Conditions 
#############################
theta = 1              # this is the condition for Moore neighbourhood
Int = 100              # this is the iteration speed (just for visualization)
time_steps = 100       # Iterations
size = 8               # this is the size of the matrix (8x8)
#############################

# Definitions
def plot2d_animate(ca, title=''):
    c = mpl.colors.ListedColormap(['green', 'red', 'black', 'gray'])
    n = mpl.colors.Normalize(vmin=0,vmax=3)   
    fig = plt.figure()
    plt.title(title)
    im = plt.imshow(ca[0], animated=True, cmap=c, norm=n)
    i = {'index': 0}
    def updatefig(*args):
        i['index'] += 1
        if i['index'] == len(ca):
            i['index'] = 0
        im.set_array(ca[i['index']])
        return im,
    ani = animation.FuncAnimation(fig, updatefig, interval=Int, blit=True)
    plt.show()


def init_simple2d(rows, cols, val=1, dtype=np.int):
    x = np.zeros((rows, cols), dtype=dtype)
    x[x.shape[0]//2][x.shape[1]//2] = val
    return np.array([x])

def evolve2d(cellular_automaton, timesteps, apply_rule, r=1, neighbourhood='Moore'):
    _, rows, cols = cellular_automaton.shape
    array = np.zeros((timesteps, rows, cols), dtype=cellular_automaton.dtype)
    array[0] = cellular_automaton

    von_neumann_mask = np.zeros((2*r + 1, 2*r + 1), dtype=bool)
    for i in range(len(von_neumann_mask)):
        mask_size = np.absolute(r - i)
        von_neumann_mask[i][:mask_size] = 1
        if mask_size != 0:
            von_neumann_mask[i][-mask_size:] = 1

    def get_neighbourhood(cell_layer, row, col):
        row_indices = [0]*(2*r+1)
        for i in range(-r,r+1):
            row_indices[i+r]=(i+row) % cell_layer.shape[0]
        col_indices = [0]*(2*r+1)
        for i in range(-r,r+1):
            col_indices[i+r]=(i+col) % cell_layer.shape[1]
        n = cell_layer[np.ix_(row_indices, col_indices)]
        if neighbourhood == 'Moore':
            return n
        elif neighbourhood == 'von Neumann':
            return np.ma.masked_array(n, von_neumann_mask)
        else:
            raise Exception("unknown neighbourhood type: %s" % neighbourhood)

    for t in range(1, timesteps):
        cell_layer = array[t - 1]
        for row, cell_row in enumerate(cell_layer):
            for col, cell in enumerate(cell_row):
                n = get_neighbourhood(cell_layer, row, col)
                array[t][row][col] = apply_rule(n, (row, col), t)
    return array


def ca_reaction_diffusion(neighbourhood, c, t):
    center_cell = neighbourhood[1][1]
    total = np.sum(neighbourhood==1)
    if total >= theta and center_cell==0: 
            return 1
    elif center_cell == 1:
            return 2
    elif center_cell == 2:
            return 3
    elif center_cell == 3:
            return 0
    else:
            return 0

# Initial condition
cellular_automaton = init_simple2d(size, size, val=0, dtype=int)

# Excitable initial cells
cellular_automaton[:, [1,2], [1,1]] = 1

# The evolution
cellular_automaton = evolve2d(cellular_automaton,
                              timesteps=time_steps,
                              neighbourhood='Moore',
                              apply_rule=ca_reaction_diffusion)


animation=plot2d_animate(cellular_automaton)

Пояснение к коду: Как видите, существует 4 состояния: 0 (зеленый), 1 (красный), 2 (черный) и 3 (серый). Способ эволюции автоматов зависит от условий cellular_automaton. То есть, например, если центральная ячейка имеет значение 0 (возбудимая ячейка) и, по меньшей мере, одна ячейка (тета-значение) в ее окрестности Мура находится в состоянии 1, на следующем временном шаге та же самая ячейка будет в состоянии 1 (в восторге).

Чтобы заметить: Конфигурация этой матрицы является тороидальной, а определения взяты из библиотеки cellpylib.

Я застрял с этим более недели, поэтому я очень признателен за помощь. Заранее спасибо!

1 Ответ

1 голос
/ 28 апреля 2019

Я не очень опытен в этом вопросе (и я не до конца понимал, что вы намеревались сделать). Я прошел и реализовал подсчет конкретных ячеек значений «0», «1», «2» и «3» в функции «evolve2d». Этот код следует рассматривать как «стартовый код»; все, что конкретно вы пытаетесь сделать, должно сочетаться с тем, что я вам дал. Кроме того, эту задачу можно было бы решить с помощью некоторого лучшего дизайна кода и, безусловно, лучшего планирования расположения ваших функций (как часть лучшей практики кодирования и общего более чистого кода, который легко отлаживать). Пожалуйста, ознакомьтесь и ПОНИМАЙТЕ изменения, которые я сделал.

#Libraries
import matplotlib
matplotlib.matplotlib_fname()
import matplotlib.pyplot as plt
import matplotlib as mpl
import matplotlib.animation as animation
import numpy as np
import csv

# Conditions
#############################
theta = 1              # this is the condition for Moore neighbourhood
iter_speed = 100              # this is the iteration speed (just for visualization)
time_steps = 100       # Iterations
size = 8               # this is the size of the matrix (8x8)
#############################

# Definitions
def plot2d_animate(ca, title=''):
    c = mpl.colors.ListedColormap(['green', 'red', 'black', 'gray'])
    n = mpl.colors.Normalize(vmin=0,vmax=3)
    fig = plt.figure()
    plt.title(title)
    im = plt.imshow(ca[0], animated=True, cmap=c, norm=n)
    i = {'index': 0}
    def updatefig(*args):
        i['index'] += 1
        if i['index'] == len(ca):
            i['index'] = 0
        im.set_array(ca[i['index']])
        return im,
    ani = animation.FuncAnimation(fig, updatefig, interval=iter_speed, blit=True)
    plt.show()
#############I ADDED EXTRA ARGUMENTs FOR THE FUNCTION BELOW
def get_neighbourhood(cell_layer, row, col, r = 1, neighbourhood = "Moore"):
    row_indices = [0]*(2*r+1)
    for i in range(-r,r+1):
        row_indices[i+r]=(i+row) % cell_layer.shape[0]
    col_indices = [0]*(2*r+1)
    for i in range(-r,r+1):
        col_indices[i+r]=(i+col) % cell_layer.shape[1]
    n = cell_layer[np.ix_(row_indices, col_indices)]
    if neighbourhood == 'Moore':
        return n
    elif neighbourhood == 'von Neumann':
        return np.ma.masked_array(n, von_neumann_mask)
    else:
        raise Exception("unknown neighbourhood type: %s" % neighbourhood)

def init_simple2d(rows, cols, val=1, dtype=np.int):
    x = np.zeros((rows, cols), dtype=dtype)
    x[x.shape[0]//2][x.shape[1]//2] = val
    return np.array([x])
#Inner functions was moved due to bad coding practice. Arguments were also changed. Make sure you understand what I did.
def evolve2d(cellular_automaton, timesteps, apply_rule, r=1, neighbourhood='Moore'):
    _, rows, cols = cellular_automaton.shape
    array = np.zeros((timesteps, rows, cols), dtype=cellular_automaton.dtype)
    array[0] = cellular_automaton

    von_neumann_mask = np.zeros((2*r + 1, 2*r + 1), dtype=bool)
    for i in range(len(von_neumann_mask)):
        mask_size = np.absolute(r - i)
        von_neumann_mask[i][:mask_size] = 1
        if mask_size != 0:
            von_neumann_mask[i][-mask_size:] = 1
    #################################################
    #These lists keep track of values over the course of the function:
    Result_0 = ["Number of 0"]
    Result_1 = ["Number of 1"]
    Result_2 = ["Number of 2"]
    Result_3 = ["Number of 3"]
    #################################################
    for t in range(1, timesteps):
    #################################################
        #This dictionary keeps track of values per timestep
        value_iter_tracker = {0: 0, 1: 0, 2: 0, 3: 0 }
    #################################################
        cell_layer = array[t - 1]
        for row, cell_row in enumerate(cell_layer):
            for col, cell in enumerate(cell_row):
                n = get_neighbourhood(cell_layer, row, col)
                ################################################
                res = apply_rule(n, (row, col), t)
                value_iter_tracker[res]+=1
                array[t][row][col] = res
                ################################################
        print(value_iter_tracker)
        ########################################################
        #Now we need to add the results of the iteration dictionary to the corresponding
        #lists in order to eventually export to the csv
        Result_0.append(value_iter_tracker[0])
        Result_1.append(value_iter_tracker[1])
        Result_2.append(value_iter_tracker[2])
        Result_3.append(value_iter_tracker[3])
        ########################################################
    ############################################################
    #function call to export lists to a csv:
    timesteps_result = list(range(1, timesteps))
    timesteps_result = ["Time Step"] + timesteps_result
    #If you don't understand what is going on here, put print statement and/or read docs
    vals = zip(timesteps_result, Result_0, Result_1, Result_2, Result_3)
    write_to_csv_file(list(vals))
    ############################################################

    return array

################################################################################
#THIS CODE IS FROM:
#https://stackoverflow.com/questions/14037540/writing-a-python-list-of-lists-to-a-csv-file
import pandas as pd
def write_to_csv_file(data):
    data = [list(x) for x in data]
    my_df = pd.DataFrame(data)
    my_df.to_csv('output1.csv', index=False, header=False)
################################################################################


def ca_reaction_diffusion(neighbourhood, c, t):
    center_cell = neighbourhood[1][1]
    total = np.sum(neighbourhood==1)
    if total >= theta and center_cell==0:
            return 1
    elif center_cell == 1:
            return 2
    elif center_cell == 2:
            return 3
    elif center_cell == 3:
            return 0
    else:
            return 0

# Initial condition
cellular_automaton = init_simple2d(size, size, val=0, dtype=int)

# Excitable initial cells
cellular_automaton[:, [1,2], [1,1]] = 1

# The evolution
cellular_automaton = evolve2d(cellular_automaton,
                              timesteps=time_steps,
                              neighbourhood='Moore',
                              apply_rule=ca_reaction_diffusion)




animation=plot2d_animate(cellular_automaton)

Я оставил комментарии, которые должны прояснить внесенные мной изменения. По сути, когда вы вызываете функцию evolve2d, создается файл csv с именем «output1.csv» с результатами временного шага. Я использовал пакет pandas для записи данных в csv, но можно было использовать и другие методы. Я предоставлю вам возможность воспользоваться изменениями, которые я внес для вашего использования. Надеюсь, это поможет.

...