dendrotweaks 0.4.3__py3-none-any.whl → 0.4.5__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- dendrotweaks/__init__.py +1 -1
- dendrotweaks/analysis/__init__.py +2 -1
- dendrotweaks/analysis/ephys_analysis.py +77 -23
- dendrotweaks/biophys/default_templates/jaxley.py +131 -0
- dendrotweaks/model.py +29 -23
- dendrotweaks/morphology/io/factories.py +2 -2
- dendrotweaks/morphology/io/reader.py +10 -1
- dendrotweaks/morphology/point_trees.py +1 -1
- dendrotweaks/utils.py +34 -11
- {dendrotweaks-0.4.3.dist-info → dendrotweaks-0.4.5.dist-info}/METADATA +1 -1
- {dendrotweaks-0.4.3.dist-info → dendrotweaks-0.4.5.dist-info}/RECORD +14 -13
- {dendrotweaks-0.4.3.dist-info → dendrotweaks-0.4.5.dist-info}/WHEEL +0 -0
- {dendrotweaks-0.4.3.dist-info → dendrotweaks-0.4.5.dist-info}/licenses/LICENSE +0 -0
- {dendrotweaks-0.4.3.dist-info → dendrotweaks-0.4.5.dist-info}/top_level.txt +0 -0
dendrotweaks/__init__.py
CHANGED
@@ -8,4 +8,5 @@ from dendrotweaks.analysis.ephys_analysis import plot_passive_properties
|
|
8
8
|
from dendrotweaks.analysis.ephys_analysis import calculate_voltage_attenuation
|
9
9
|
from dendrotweaks.analysis.ephys_analysis import plot_voltage_attenuation
|
10
10
|
from dendrotweaks.analysis.ephys_analysis import calculate_dendritic_nonlinearity
|
11
|
-
from dendrotweaks.analysis.ephys_analysis import plot_dendritic_nonlinearity
|
11
|
+
from dendrotweaks.analysis.ephys_analysis import plot_dendritic_nonlinearity
|
12
|
+
from dendrotweaks.analysis.ephys_analysis import calculate_sag_ratio
|
@@ -38,6 +38,11 @@ def get_somatic_data(model):
|
|
38
38
|
def calculate_input_resistance(model):
|
39
39
|
"""
|
40
40
|
Calculate the input resistance of the neuron model.
|
41
|
+
|
42
|
+
This function determines the input resistance by calculating the ratio
|
43
|
+
between the voltage change and the injected current. The voltage change
|
44
|
+
is measured as the difference between the membrane potential at the onset
|
45
|
+
and offset of the current injection.
|
41
46
|
|
42
47
|
Parameters
|
43
48
|
----------
|
@@ -52,13 +57,11 @@ def calculate_input_resistance(model):
|
|
52
57
|
|
53
58
|
v, t, dt, iclamp = get_somatic_data(model)
|
54
59
|
|
55
|
-
v_min = np.min(v)
|
56
|
-
|
57
60
|
amp = iclamp.amp
|
58
|
-
start_ts = iclamp.delay / dt
|
59
|
-
|
61
|
+
start_ts = int(iclamp.delay / dt)
|
62
|
+
stop_ts = int((iclamp.delay + iclamp.dur) / dt)
|
60
63
|
v_onset = v[int(start_ts)]
|
61
|
-
v_offset = v[int(
|
64
|
+
v_offset = v[int(stop_ts)]
|
62
65
|
|
63
66
|
R_in = (v_onset - v_offset) / amp
|
64
67
|
print(f"Input resistance: {R_in:.2f} MOhm")
|
@@ -71,44 +74,61 @@ def calculate_input_resistance(model):
|
|
71
74
|
}
|
72
75
|
|
73
76
|
|
74
|
-
def
|
75
|
-
return
|
76
|
-
|
77
|
+
def _double_exp_decay(t, A1, tau1, A2, tau2):
|
78
|
+
return A1 * np.exp(-t / tau1) + A2 * np.exp(-t / tau2)
|
77
79
|
|
78
80
|
def calculate_time_constant(model):
|
79
81
|
"""
|
80
|
-
|
82
|
+
Estimate the passive membrane time constant (τm) by fitting
|
83
|
+
a double exponential to the somatic voltage decay and selecting
|
84
|
+
the slowest τ component.
|
81
85
|
|
82
86
|
Parameters
|
83
87
|
----------
|
84
88
|
model : Model
|
85
|
-
The neuron model.
|
89
|
+
The neuron model (assumes Ih is disabled).
|
86
90
|
|
87
91
|
Returns
|
88
92
|
-------
|
89
93
|
dict
|
90
|
-
A dictionary
|
94
|
+
A dictionary with τm (slowest), both τs, and fit details.
|
91
95
|
"""
|
92
96
|
v, t, dt, iclamp = get_somatic_data(model)
|
93
97
|
|
94
98
|
start_ts = int(iclamp.delay / dt)
|
95
99
|
stop_ts = int((iclamp.delay + iclamp.dur) / dt)
|
96
100
|
min_ts = np.argmin(v[start_ts:stop_ts]) + start_ts
|
97
|
-
v_min =
|
101
|
+
v_min = v[min_ts]
|
98
102
|
v_decay = v[start_ts: min_ts] - v_min
|
99
103
|
t_decay = t[start_ts: min_ts] - t[start_ts]
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
+
|
105
|
+
# Fit double exponential
|
106
|
+
try:
|
107
|
+
popt, _ = curve_fit(
|
108
|
+
_double_exp_decay, t_decay, v_decay,
|
109
|
+
p0=[1, 10, 0.5, 100],
|
110
|
+
bounds=(0, [np.inf, 1000, np.inf, 1000])
|
111
|
+
)
|
112
|
+
A1, tau1, A2, tau2 = popt
|
113
|
+
tau_slowest = max(tau1, tau2)
|
114
|
+
except RuntimeError:
|
115
|
+
print("Fit failed. Could not estimate time constant.")
|
116
|
+
return None
|
117
|
+
|
118
|
+
print(f"Time constant {tau_slowest:.2f} ms. Estimated from double exp fit (slowest component)")
|
119
|
+
|
104
120
|
return {
|
105
|
-
'time_constant':
|
106
|
-
'A': A,
|
121
|
+
'time_constant': tau_slowest,
|
107
122
|
'start_time': start_ts * dt,
|
123
|
+
'tau1': tau1,
|
124
|
+
'tau2': tau2,
|
125
|
+
'A1': A1,
|
126
|
+
'A2': A2,
|
108
127
|
'decay_time': t_decay,
|
109
128
|
'decay_voltage': v_decay
|
110
129
|
}
|
111
130
|
|
131
|
+
|
112
132
|
def calculate_passive_properties(model):
|
113
133
|
"""
|
114
134
|
Calculate the passive properties of the neuron model.
|
@@ -140,7 +160,6 @@ def plot_passive_properties(data, ax=None):
|
|
140
160
|
v_offset = data['offset_voltage']
|
141
161
|
t_decay = data['decay_time']
|
142
162
|
v_decay = data['decay_voltage']
|
143
|
-
A = data['A']
|
144
163
|
start_t = data['start_time']
|
145
164
|
|
146
165
|
ax.set_title(f"R_in: {R_in:.2f} MOhm, Tau: {tau:.2f} ms")
|
@@ -148,8 +167,10 @@ def plot_passive_properties(data, ax=None):
|
|
148
167
|
ax.axhline(v_offset, color='gray', linestyle='--', label='V offset')
|
149
168
|
|
150
169
|
# Shift the exp_decay output along the y-axis
|
151
|
-
|
152
|
-
|
170
|
+
fit_curve = _double_exp_decay(t_decay, data['A1'], data['tau1'], data['A2'], data['tau2']) + v_offset
|
171
|
+
label = f'Double exp fit (tau1 = {data["tau1"]:.1f} ms, tau2 = {data["tau2"]:.1f} ms)'
|
172
|
+
|
173
|
+
ax.plot(t_decay + start_t, fit_curve, color='red', label='Exp. fit', linestyle='--')
|
153
174
|
ax.legend()
|
154
175
|
|
155
176
|
|
@@ -365,16 +386,18 @@ def calculate_voltage_attenuation(model):
|
|
365
386
|
|
366
387
|
|
367
388
|
# Calculate voltage displacement from the resting potential
|
368
|
-
delta_v_at_stimulated = voltage_at_stimulated[0] - np.min(voltage_at_stimulated)
|
369
|
-
delta_vs = [v[0] - np.min(v) for v in voltages]
|
389
|
+
delta_v_at_stimulated = voltage_at_stimulated[0] - voltage_at_stimulated[-2]# np.min(voltage_at_stimulated)
|
390
|
+
delta_vs = [v[0] - v[-2] for v in voltages] # np.min(v) for v in voltages]
|
370
391
|
|
371
392
|
min_voltages = [np.min(v) for v in voltages]
|
393
|
+
end_voltages = [v[-2] for v in voltages]
|
372
394
|
|
373
395
|
attenuation = [dv / delta_v_at_stimulated for dv in delta_vs]
|
374
396
|
|
375
397
|
return {
|
376
398
|
'path_distances': path_distances,
|
377
399
|
'min_voltages': min_voltages,
|
400
|
+
'end_voltages': end_voltages,
|
378
401
|
'attenuation': attenuation
|
379
402
|
}
|
380
403
|
|
@@ -484,5 +507,36 @@ def plot_dendritic_nonlinearity(data, ax=None, **kwargs):
|
|
484
507
|
ax[1].set_title('Dendritic nonlinearity')
|
485
508
|
|
486
509
|
|
510
|
+
def calculate_sag_ratio(model):
|
511
|
+
"""
|
512
|
+
Calculate the sag ratio of the neuron model.
|
513
|
+
|
514
|
+
Parameters
|
515
|
+
----------
|
516
|
+
model : Model
|
517
|
+
The neuron model.
|
518
|
+
|
519
|
+
Returns
|
520
|
+
-------
|
521
|
+
dict
|
522
|
+
A dictionary containing the sag ratio and intermediate values.
|
523
|
+
"""
|
524
|
+
v, t, dt, iclamp = get_somatic_data(model)
|
487
525
|
|
526
|
+
start_ts = int(iclamp.delay / dt)
|
527
|
+
stop_ts = int((iclamp.delay + iclamp.dur) / dt)
|
528
|
+
min_ts = np.argmin(v[start_ts:stop_ts]) + start_ts
|
529
|
+
v_min = np.min(v[start_ts: min_ts])
|
530
|
+
|
531
|
+
a = v[stop_ts] - v_min
|
532
|
+
b = v[start_ts] - v_min
|
533
|
+
|
534
|
+
sag_ratio = a / b if b != 0 else np.nan
|
535
|
+
|
536
|
+
print(f"Sag ratio: {a:.2f}/{b:.2f} = {sag_ratio:.2f}")
|
537
|
+
return {
|
538
|
+
'a': a,
|
539
|
+
'b': b,
|
540
|
+
'sag_ratio': sag_ratio,
|
541
|
+
}
|
488
542
|
|
@@ -0,0 +1,131 @@
|
|
1
|
+
# This Python channel class was automatically generated from a MOD file
|
2
|
+
# using DendroTweaks toolbox, dendrotweaks.dendrites.gr
|
3
|
+
|
4
|
+
|
5
|
+
from jaxley.channels import Channel
|
6
|
+
from jaxley.solver_gate import exponential_euler
|
7
|
+
import jax.numpy as np
|
8
|
+
|
9
|
+
class {{ class_name }}(Channel):
|
10
|
+
"""
|
11
|
+
{{ title }}
|
12
|
+
"""
|
13
|
+
|
14
|
+
def __init__(self, name="{{ class_name }}"):
|
15
|
+
self.current_is_in_mA_per_cm2 = True
|
16
|
+
super().__init__(name=name)
|
17
|
+
self.channel_params = {
|
18
|
+
{% for param, value in channel_params.items() -%}
|
19
|
+
"{{ param }}_{{ class_name }}": {{ value }}
|
20
|
+
{%- if not loop.last -%},
|
21
|
+
{%- endif %}
|
22
|
+
{% endfor -%}
|
23
|
+
}
|
24
|
+
self.channel_states = {
|
25
|
+
{% for state in state_vars -%}
|
26
|
+
"{{ state }}_{{class_name}}": 0.0
|
27
|
+
{%- if not loop.last %},
|
28
|
+
{%- endif %}
|
29
|
+
{% endfor -%}
|
30
|
+
}
|
31
|
+
self._state_powers = {
|
32
|
+
{% for state, power in state_vars.items() -%}
|
33
|
+
"{{ state }}_{{class_name}}": {{ power }}
|
34
|
+
{%- if not loop.last %},
|
35
|
+
{%- endif %}
|
36
|
+
{% endfor -%}
|
37
|
+
}
|
38
|
+
self.ion = "{{ ion }}"
|
39
|
+
self.current_name = "i_{{ ion }}"
|
40
|
+
|
41
|
+
self.independent_var_name = "{{ independent_var_name }}"
|
42
|
+
self.tadj = 1
|
43
|
+
|
44
|
+
def set_tadj(self, temperature):
|
45
|
+
"""
|
46
|
+
Set the temperature adjustment factor for the channel kinetics.
|
47
|
+
|
48
|
+
Parameters
|
49
|
+
----------
|
50
|
+
temperature : float
|
51
|
+
The temperature in degrees Celsius.
|
52
|
+
|
53
|
+
Notes
|
54
|
+
-----
|
55
|
+
The temperature adjustment factor is calculated as:
|
56
|
+
tadj = q10 ** ((temperature - reference_temp) / 10)
|
57
|
+
where q10 is the temperature coefficient and reference_temp is the
|
58
|
+
temperature at which the channel kinetics were measured.
|
59
|
+
"""
|
60
|
+
q10 = self.channel_params.get(f"q10_{{ class_name }}")
|
61
|
+
reference_temp = self.channel_params.get(f"temp_{{ class_name }}")
|
62
|
+
if q10 is None or reference_temp is None:
|
63
|
+
self.tadj = 1
|
64
|
+
print(f"Warning: q10 or reference temperature not set for {self.name}. Using default tadj = 1.")
|
65
|
+
else:
|
66
|
+
self.tadj = q10 ** ((temperature - reference_temp) / 10)
|
67
|
+
|
68
|
+
def __getitem__(self, item):
|
69
|
+
return self.channel_params[item]
|
70
|
+
|
71
|
+
def __setitem__(self, item, value):
|
72
|
+
self.channel_params[item] = value
|
73
|
+
|
74
|
+
{% for function in functions %}
|
75
|
+
{{ function['signature'] }}
|
76
|
+
{%- for param in function['params'] -%}
|
77
|
+
{{ param }} = self.channel_params.get("{{ param }}_{{ class_name }}", 1)
|
78
|
+
{% endfor %}
|
79
|
+
{{ function['body'] }}
|
80
|
+
{% if not loop.last %}
|
81
|
+
{% endif %}{% endfor -%}
|
82
|
+
{% for procedure in procedures %}
|
83
|
+
{{ procedure['signature'] }}
|
84
|
+
{% for param in procedure['params'] -%}
|
85
|
+
{{ param }} = self.channel_params.get("{{ param }}_{{ class_name }}", 1)
|
86
|
+
{% endfor %}
|
87
|
+
{{ procedure['body'] }}
|
88
|
+
{%- if not loop.last %}
|
89
|
+
{% endif %}{% endfor %}
|
90
|
+
|
91
|
+
def update_states(self, states, dt, v, params):
|
92
|
+
{% for state, state_params in state_vars.items() -%}
|
93
|
+
{{state}} = states['{{ state }}_{{class_name}}']
|
94
|
+
{%- if not loop.last %}
|
95
|
+
{%- endif %}
|
96
|
+
{% endfor -%}
|
97
|
+
{{- procedure_calls}}
|
98
|
+
{% for state in state_vars.keys() %}new_{{state}} = exponential_euler({{state}}, dt, {{state}}Inf, {{state}}Tau){% if not loop.last %}
|
99
|
+
{% endif %}{% endfor %}
|
100
|
+
return {
|
101
|
+
{% for state in state_vars -%}
|
102
|
+
"{{ state }}_{{class_name}}": new_{{state}}
|
103
|
+
{%- if not loop.last %},
|
104
|
+
{%- endif %}
|
105
|
+
{% endfor -%}
|
106
|
+
}
|
107
|
+
|
108
|
+
def compute_current(self, states, v, params):
|
109
|
+
{% for state in state_vars.keys() -%}
|
110
|
+
{{state}} = states['{{ state }}_{{class_name}}']
|
111
|
+
{%- if not loop.last %}
|
112
|
+
{%- endif %}
|
113
|
+
{% endfor -%}
|
114
|
+
gbar = params["gbar_{{class_name}}"]
|
115
|
+
# E = params["E_{{ ion }}"]
|
116
|
+
E = {{ E_ion }}
|
117
|
+
{{ procedure_calls}}
|
118
|
+
g = self.tadj * gbar *{% for state, power in state_vars.items()%} {{state}}**{{power['power']}} {% if not loop.last %}*{% endif %}{% endfor %}
|
119
|
+
return g * (v - E)
|
120
|
+
|
121
|
+
def init_state(self, states, v, params, delta_t):
|
122
|
+
{{ procedure_calls}}
|
123
|
+
return {
|
124
|
+
{% for state in state_vars.keys() -%}
|
125
|
+
"{{ state }}_{{class_name}}": {{state}}Inf
|
126
|
+
{%- if not loop.last %},
|
127
|
+
{%- endif %}
|
128
|
+
{% endfor -%}
|
129
|
+
}
|
130
|
+
|
131
|
+
|
dendrotweaks/model.py
CHANGED
@@ -1104,9 +1104,8 @@ class Model():
|
|
1104
1104
|
value = seg.parent.get_param_value(param_name)
|
1105
1105
|
seg.set_param_value(param_name, value)
|
1106
1106
|
else:
|
1107
|
-
|
1108
|
-
|
1109
|
-
for seg, value in zip(filtered_segments, values):
|
1107
|
+
for seg in filtered_segments:
|
1108
|
+
value = distribution(seg.path_distance())
|
1110
1109
|
seg.set_param_value(param_name, value)
|
1111
1110
|
|
1112
1111
|
|
@@ -1422,12 +1421,15 @@ class Model():
|
|
1422
1421
|
domains_to_mechs = {domain_name: mech_names for domain_name, mech_names
|
1423
1422
|
in self.domains_to_mechs.items() if domain_name in [domain.name for domain in domains_in_subtree]}
|
1424
1423
|
common_mechs = set.intersection(*domains_to_mechs.values())
|
1425
|
-
if not common_mechs
|
1424
|
+
if not all(mech_names == common_mechs
|
1425
|
+
for mech_names in domains_to_mechs.values()):
|
1426
1426
|
raise ValueError(
|
1427
1427
|
'The domains in the subtree have different mechanisms. '
|
1428
1428
|
'Please ensure that all domains in the subtree have the same mechanisms. '
|
1429
1429
|
'You may need to insert the missing mechanisms and set their conductances to 0 where they are not needed.'
|
1430
1430
|
)
|
1431
|
+
elif len(domains_in_subtree) == 1:
|
1432
|
+
common_mechs = self.domains_to_mechs[domain_name].copy()
|
1431
1433
|
|
1432
1434
|
inserted_mechs = {mech_name: mech for mech_name, mech
|
1433
1435
|
in self.mechanisms.items()
|
@@ -1497,9 +1499,15 @@ class Model():
|
|
1497
1499
|
|
1498
1500
|
root_segs = [seg for seg in root.segments]
|
1499
1501
|
params_to_coeffs = {}
|
1500
|
-
for param_name in self.params:
|
1501
|
-
|
1502
|
-
|
1502
|
+
# for param_name in self.params:
|
1503
|
+
common_mechs.add('Independent')
|
1504
|
+
for mech in common_mechs:
|
1505
|
+
for param_name in self.mechs_to_params[mech]:
|
1506
|
+
coeffs = self.fit_distribution(param_name, segments=root_segs, plot=False)
|
1507
|
+
if coeffs is None:
|
1508
|
+
warnings.warn(f'Cannot fit distribution for parameter {param_name}. No values found.')
|
1509
|
+
continue
|
1510
|
+
params_to_coeffs[param_name] = coeffs
|
1503
1511
|
|
1504
1512
|
|
1505
1513
|
# Create new domain
|
@@ -1536,9 +1544,12 @@ class Model():
|
|
1536
1544
|
}
|
1537
1545
|
|
1538
1546
|
|
1539
|
-
def fit_distribution(self, param_name, segments, max_degree=
|
1547
|
+
def fit_distribution(self, param_name, segments, max_degree=20, tolerance=1e-7, plot=False):
|
1540
1548
|
from numpy import polyfit, polyval
|
1541
1549
|
values = [seg.get_param_value(param_name) for seg in segments]
|
1550
|
+
# if all values are NaN, return None
|
1551
|
+
if all(np.isnan(values)):
|
1552
|
+
return None
|
1542
1553
|
distances = [seg.path_distance() for seg in segments]
|
1543
1554
|
sorted_pairs = sorted(zip(distances, values))
|
1544
1555
|
distances, values = zip(*sorted_pairs)
|
@@ -1565,7 +1576,7 @@ class Model():
|
|
1565
1576
|
elif len(coeffs) == 2:
|
1566
1577
|
self.params[param_name][group_name] = Distribution('linear', slope=coeffs[0], intercept=coeffs[1])
|
1567
1578
|
else:
|
1568
|
-
self.params[param_name][group_name] = Distribution('polynomial', coeffs=coeffs)
|
1579
|
+
self.params[param_name][group_name] = Distribution('polynomial', coeffs=coeffs.tolist())
|
1569
1580
|
|
1570
1581
|
|
1571
1582
|
# ========================================================================
|
@@ -1906,13 +1917,13 @@ class Model():
|
|
1906
1917
|
|
1907
1918
|
df_iclamps = df_stimuli[df_stimuli['type'] == 'iclamp'].reset_index(drop=True, inplace=False)
|
1908
1919
|
|
1909
|
-
for
|
1920
|
+
for row in df_iclamps.itertuples(index=False):
|
1910
1921
|
self.add_iclamp(
|
1911
|
-
self.sec_tree.sections[row
|
1912
|
-
row
|
1913
|
-
data['stimuli']['iclamps'][
|
1914
|
-
data['stimuli']['iclamps'][
|
1915
|
-
data['stimuli']['iclamps'][
|
1922
|
+
self.sec_tree.sections[row.sec_idx],
|
1923
|
+
row.loc,
|
1924
|
+
data['stimuli']['iclamps'][row.idx]['amp'],
|
1925
|
+
data['stimuli']['iclamps'][row.idx]['delay'],
|
1926
|
+
data['stimuli']['iclamps'][row.idx]['dur']
|
1916
1927
|
)
|
1917
1928
|
|
1918
1929
|
# Populations -------------------------------------------------------
|
@@ -1945,15 +1956,10 @@ class Model():
|
|
1945
1956
|
# Recordings ---------------------------------------------------------
|
1946
1957
|
|
1947
1958
|
df_recs = df_stimuli[df_stimuli['type'] == 'rec'].reset_index(drop=True, inplace=False)
|
1948
|
-
for
|
1949
|
-
|
1950
|
-
# in the JSON structure. It should be removed in the future.
|
1951
|
-
if data['stimuli'].get('recordings'):
|
1952
|
-
var = data['stimuli']['recordings'][i]['var']
|
1953
|
-
else:
|
1954
|
-
var = 'v'
|
1959
|
+
for row in df_recs.itertuples(index=False):
|
1960
|
+
var = data['stimuli']['recordings'][row.idx]['var']
|
1955
1961
|
self.add_recording(
|
1956
|
-
|
1962
|
+
self.sec_tree.sections[row.sec_idx], row.loc, var
|
1957
1963
|
)
|
1958
1964
|
|
1959
1965
|
|
@@ -36,8 +36,8 @@ def create_point_tree(source: Union[str, DataFrame]) -> PointTree:
|
|
36
36
|
raise ValueError("Source must be a file path (str) or a DataFrame.")
|
37
37
|
|
38
38
|
nodes = [
|
39
|
-
Point(row
|
40
|
-
for
|
39
|
+
Point(row.Index, row.Type, row.X, row.Y, row.Z, row.R, row.Parent)
|
40
|
+
for row in df.itertuples(index=False)
|
41
41
|
]
|
42
42
|
point_tree = PointTree(nodes)
|
43
43
|
point_tree.remove_overlaps()
|
@@ -35,7 +35,16 @@ class SWCReader():
|
|
35
35
|
header=None,
|
36
36
|
comment='#',
|
37
37
|
names=['Index', 'Type', 'X', 'Y', 'Z', 'R', 'Parent'],
|
38
|
-
index_col=False
|
38
|
+
index_col=False,
|
39
|
+
dtype={
|
40
|
+
'Index': int,
|
41
|
+
'Type': int,
|
42
|
+
'X': float,
|
43
|
+
'Y': float,
|
44
|
+
'Z': float,
|
45
|
+
'R': float,
|
46
|
+
'Parent': int
|
47
|
+
}
|
39
48
|
)
|
40
49
|
|
41
50
|
if (df['R'] == 0).all():
|
dendrotweaks/utils.py
CHANGED
@@ -182,31 +182,54 @@ def read_file(path_to_file):
|
|
182
182
|
return content
|
183
183
|
|
184
184
|
|
185
|
-
def download_example_data(path_to_destination):
|
185
|
+
def download_example_data(path_to_destination, include_templates=True, include_modfiles=True):
|
186
186
|
"""
|
187
|
-
Download
|
187
|
+
Download and extract specific folders from the DendroTweaks GitHub repository:
|
188
|
+
- examples/ <- from examples subfolder (always included)
|
189
|
+
- examples/Templates/ <- from src/dendrotweaks/biophys/default_templates (optional)
|
190
|
+
- examples/Default/ <- from src/dendrotweaks/biophys/default_mod (optional)
|
188
191
|
|
189
192
|
Parameters
|
190
193
|
----------
|
191
194
|
path_to_destination : str
|
192
|
-
The path to the destination folder where the
|
195
|
+
The path to the destination folder where the data will be downloaded and extracted.
|
196
|
+
|
197
|
+
include_templates : bool, optional
|
198
|
+
If True, also extract default_templates/ into examples/Templates/.
|
199
|
+
|
200
|
+
include_modfiles : bool, optional
|
201
|
+
If True, also extract default_mod/ into examples/Default/.
|
193
202
|
"""
|
194
203
|
if not os.path.exists(path_to_destination):
|
195
204
|
os.makedirs(path_to_destination)
|
196
205
|
|
197
206
|
repo_url = "https://github.com/Poirazi-Lab/DendroTweaks/archive/refs/heads/main.zip"
|
198
|
-
zip_path = os.path.join(path_to_destination, "
|
207
|
+
zip_path = os.path.join(path_to_destination, "dendrotweaks_repo.zip")
|
199
208
|
|
200
|
-
print(f"Downloading
|
209
|
+
print(f"Downloading data from {repo_url}...")
|
201
210
|
urllib.request.urlretrieve(repo_url, zip_path)
|
202
211
|
|
203
|
-
print(f"Extracting
|
212
|
+
print(f"Extracting relevant folders to {path_to_destination}...")
|
204
213
|
with zipfile.ZipFile(zip_path, 'r') as zip_ref:
|
205
214
|
for member in zip_ref.namelist():
|
215
|
+
target_path = None
|
216
|
+
|
217
|
+
# === Always extract examples/ folder ===
|
206
218
|
if member.startswith("DendroTweaks-main/examples/"):
|
207
|
-
|
208
|
-
|
209
|
-
|
219
|
+
rel_path = os.path.relpath(member, "DendroTweaks-main/examples")
|
220
|
+
target_path = os.path.join(path_to_destination, rel_path)
|
221
|
+
|
222
|
+
# === Optionally extract Templates/ folder ===
|
223
|
+
elif include_templates and member.startswith("DendroTweaks-main/src/dendrotweaks/biophys/default_templates/"):
|
224
|
+
rel_path = os.path.relpath(member, "DendroTweaks-main/src/dendrotweaks/biophys/default_templates")
|
225
|
+
target_path = os.path.join(path_to_destination, "Templates", rel_path)
|
226
|
+
|
227
|
+
# === Optionally extract Default/ folder ===
|
228
|
+
elif include_modfiles and member.startswith("DendroTweaks-main/src/dendrotweaks/biophys/default_mod/"):
|
229
|
+
rel_path = os.path.relpath(member, "DendroTweaks-main/src/dendrotweaks/biophys/default_mod")
|
230
|
+
target_path = os.path.join(path_to_destination, "Default", rel_path)
|
231
|
+
|
232
|
+
if target_path:
|
210
233
|
if member.endswith('/'):
|
211
234
|
os.makedirs(target_path, exist_ok=True)
|
212
235
|
else:
|
@@ -214,8 +237,8 @@ def download_example_data(path_to_destination):
|
|
214
237
|
with zip_ref.open(member) as source, open(target_path, 'wb') as target:
|
215
238
|
target.write(source.read())
|
216
239
|
|
217
|
-
os.remove(zip_path)
|
218
|
-
print(f"
|
240
|
+
os.remove(zip_path)
|
241
|
+
print(f"Data downloaded and extracted successfully to {path_to_destination}/.")
|
219
242
|
|
220
243
|
|
221
244
|
|
@@ -1,11 +1,11 @@
|
|
1
|
-
dendrotweaks/__init__.py,sha256=
|
2
|
-
dendrotweaks/model.py,sha256=
|
1
|
+
dendrotweaks/__init__.py,sha256=wAjTFbihG1E0dDURosjTFhIilebuX2iXvefW8BYoDYQ,384
|
2
|
+
dendrotweaks/model.py,sha256=6yvsIu-DImsBNosJmqJt6n59vaFMtRsDog4wAWppwlY,71362
|
3
3
|
dendrotweaks/model_io.py,sha256=xwXKMcUle-Y0HoWFYVZu3G8v4pdQXmeaDfl2Xi65eHw,2137
|
4
4
|
dendrotweaks/path_manager.py,sha256=dai5o6UA0nk-ubwKWRu4LFdDBO77zW_SsMf6k0MLBiI,8703
|
5
5
|
dendrotweaks/simulators.py,sha256=OscZ4H6z9YiNloDtMAgpPx9n2e-9WJyLEWtcSD1YZR8,7411
|
6
|
-
dendrotweaks/utils.py,sha256=
|
7
|
-
dendrotweaks/analysis/__init__.py,sha256=
|
8
|
-
dendrotweaks/analysis/ephys_analysis.py,sha256=
|
6
|
+
dendrotweaks/utils.py,sha256=KrGV8972sCqn-OKiOpr2KOcEekcHnP10Tl499z5M60k,8477
|
7
|
+
dendrotweaks/analysis/__init__.py,sha256=tDi4BHtW1fX5iiZVk1OM3gmLo69VFmUN8AEK_F8tDq4,927
|
8
|
+
dendrotweaks/analysis/ephys_analysis.py,sha256=EuT7bHoSAjNg4736iKxj2Y1UfI2TfeWeoxtvUe78zfw,16188
|
9
9
|
dendrotweaks/analysis/morphometric_analysis.py,sha256=5zohjGssyx-wezI-yY3Q-kYM_wzAQLLFBJ9Xk950_JY,3571
|
10
10
|
dendrotweaks/biophys/__init__.py,sha256=k0o2xwyoaJUb1lfO9OHtqxheNP6R-Ya5o0g-bJOdCZg,360
|
11
11
|
dendrotweaks/biophys/distributions.py,sha256=XGczxBYJ0-vkIfXbfzvlIqlH9OpBuT9J95Kzyd2zL5A,10325
|
@@ -20,6 +20,7 @@ dendrotweaks/biophys/default_mod/NMDA.mod,sha256=tT4Q5UPoeztXcQ45uZc2PUO3-8OkDLC
|
|
20
20
|
dendrotweaks/biophys/default_mod/vecstim.mod,sha256=iSpJgR96O2Z3pLNUFIsZ7YJ529ncKUBaZqDJvA0_oV0,965
|
21
21
|
dendrotweaks/biophys/default_templates/NEURON_template.py,sha256=MWSv2fLKGJxdt2zfSO0b74peuBC8U9j_S6AwM5URXts,14945
|
22
22
|
dendrotweaks/biophys/default_templates/default.py,sha256=7HEbR2GJEOhgiox1QtZUEuHi5ihNAHDLsXQiQk980tI,2201
|
23
|
+
dendrotweaks/biophys/default_templates/jaxley.py,sha256=RFmWyLOwa0GCjkuaT-gBbjVrniu99DD3PPAs3J9os3E,4599
|
23
24
|
dendrotweaks/biophys/default_templates/standard_channel.mod,sha256=sw80c-JyqfXNA7c7v7pZGLY-0MgFUvd3bPvJcAGXNSk,2923
|
24
25
|
dendrotweaks/biophys/default_templates/template_jaxley.py,sha256=t-GsCSUyQ7rDoaLmyuWd9bIxB8W3bCqJdnikD59EVvI,3676
|
25
26
|
dendrotweaks/biophys/default_templates/template_jaxley_new.py,sha256=I62KhnOYNV1bT-nPsDTxjIISYmDcso2X8rnsos28nYs,3631
|
@@ -34,13 +35,13 @@ dendrotweaks/biophys/io/parser.py,sha256=boT27lFrn5LYrJnkZFs0SwrZZrkSkwO8efqGPJ4
|
|
34
35
|
dendrotweaks/biophys/io/reader.py,sha256=JWm5WM9illvSfDkhWEmWBcj8Y7PSi8zeZX9j1ARUHVU,6576
|
35
36
|
dendrotweaks/morphology/__init__.py,sha256=JwXmSmdn9e_jqslITEdiU9kWvzxcxT9Aw_kUkXLbm5o,353
|
36
37
|
dendrotweaks/morphology/domains.py,sha256=l57KVR5eo1LlH_fCd1AOMiG_SsYLBPBTGQ5R78BHfdM,2545
|
37
|
-
dendrotweaks/morphology/point_trees.py,sha256=
|
38
|
+
dendrotweaks/morphology/point_trees.py,sha256=aZl5p6FGn6mph9xNo-3L_HC7mIvLhvi3BLJuhuihssk,21605
|
38
39
|
dendrotweaks/morphology/sec_trees.py,sha256=eKLC-yNhsn_rPdTE7w7p6STa1onYkBTGcpBKBpEWZUI,36957
|
39
40
|
dendrotweaks/morphology/seg_trees.py,sha256=-XeSJuD7ZixBJYQDzvmSEiNvOWbVmX_DanyAPkkR-NA,4042
|
40
41
|
dendrotweaks/morphology/trees.py,sha256=NrNvPMR-U0clt63eqwVJqU0H8NJgY53QGA_BkdcwkQI,16033
|
41
42
|
dendrotweaks/morphology/io/__init__.py,sha256=gAZqZdf5VKPb6ksK8Lwt7MbTAq8TDP8uq3Vs_ebNFEY,324
|
42
|
-
dendrotweaks/morphology/io/factories.py,sha256=
|
43
|
-
dendrotweaks/morphology/io/reader.py,sha256=
|
43
|
+
dendrotweaks/morphology/io/factories.py,sha256=YkTCXWuJ6bwUW0kjcV2TSzu43EvkE8N5yC3q-B02kxc,6372
|
44
|
+
dendrotweaks/morphology/io/reader.py,sha256=R368nkHfAKHdSnqg7jjc_us7gVSOlMJpzAjhUUU7Wfw,2121
|
44
45
|
dendrotweaks/morphology/io/validation.py,sha256=lVkYw9y9yG5QpRh_N0YQ3FbZwuSUsQfSqJTMumMcDdc,7872
|
45
46
|
dendrotweaks/morphology/reduce/__init__.py,sha256=p6Mg3KDHxTt8S4DtI0m7L7MqV6dS2pdIYAwB7B-toVw,921
|
46
47
|
dendrotweaks/morphology/reduce/reduce.py,sha256=5czZDrG3xsvHn3c_tbYhUOlXgST989-RS-ntbhlvvA0,6361
|
@@ -49,8 +50,8 @@ dendrotweaks/stimuli/__init__.py,sha256=bFfSEZhCVpwOVEBgLe65iiY3SdpjKPhyLemC1z5O
|
|
49
50
|
dendrotweaks/stimuli/iclamps.py,sha256=NjkhhwZKJR1f_g3N9BVxMVoO9ubBk5WkQ6h9Bnf9xgA,1681
|
50
51
|
dendrotweaks/stimuli/populations.py,sha256=vq2NUOaxaltUwlcT7wuCe8z1JWc8rk6HfPmfEd1kK68,8335
|
51
52
|
dendrotweaks/stimuli/synapses.py,sha256=g4MgWTske2TZ2i9FIIOE8-KXNx_3dWa3zEhB2rcqYig,5470
|
52
|
-
dendrotweaks-0.4.
|
53
|
-
dendrotweaks-0.4.
|
54
|
-
dendrotweaks-0.4.
|
55
|
-
dendrotweaks-0.4.
|
56
|
-
dendrotweaks-0.4.
|
53
|
+
dendrotweaks-0.4.5.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
54
|
+
dendrotweaks-0.4.5.dist-info/METADATA,sha256=B834Ro9ggHwzr3Ma4FE-twhlZ_v--ZHd-uvUk8HI1SQ,2740
|
55
|
+
dendrotweaks-0.4.5.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
56
|
+
dendrotweaks-0.4.5.dist-info/top_level.txt,sha256=OzT_2BSI5j5zxC447K6Y-0W-GHbued7iX-_hFGAKMxY,13
|
57
|
+
dendrotweaks-0.4.5.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|