mne.channels.read_custom_montage('128+8locs.xyz'): ValueError: could not convert string to float:

The problem is that you have your columns separated by a mixture of spaces and tabs, but MNE-Python expects tabs only in XYZ files.

So if we fix it like this:

1	0	0	1	A1
2	0	-0.19937	0.97992	A2
3	0	-0.39073	0.9205	A3
4	0	-0.56641	0.82413	A4
5	-0.27528	-0.66458	0.69466	A5
6	-0.50865	-0.50865	0.69466	A6
7	-0.59637	-0.59637	0.5373	A7
8	-0.54874	-0.75528	0.35837	A8
9	-0.57972	-0.79792	0.16505	A9
10	-0.58743	-0.80852	-0.034899	A10
11	-0.57154	-0.78666	-0.23345	A11
12	-0.53271	-0.73322	-0.42262	A12
13	-0.28006	-0.86195	-0.42262	A13
14	-0.30048	-0.92478	-0.23345	A14
15	-0.30883	-0.95048	-0.034899	A15
16	-0.30478	-0.93801	0.16505	A16
17	-0.28849	-0.88789	0.35837	A17
18	-0.32275	-0.77919	0.5373	A18
19	0	-0.71934	0.69466	A19
20	0	-0.84339	0.5373	A20
21	0	-0.93358	0.35837	A21
22	0	-0.98629	0.16505	A22
23	0	-0.99939	-0.034899	A23
24	0	-0.97237	-0.23345	A24
25	0	-0.90631	-0.42262	A25
26	0.28006	-0.86195	-0.42262	A26
27	0.30048	-0.92478	-0.23345	A27
28	0.30883	-0.95048	-0.034899	A28
29	0.30478	-0.93801	0.16505	A29
30	0.28849	-0.88789	0.35837	A30
31	0.32275	-0.77919	0.5373	A31
32	0.27528	-0.66458	0.69466	A32
33	0.18961	-0.061608	0.97992	B1
34	0.27629	-0.27629	0.9205	B2
35	0.50865	-0.50865	0.69466	B3
36	0.59637	-0.59637	0.5373	B4
37	0.54874	-0.75528	0.35837	B5
38	0.57972	-0.79792	0.16505	B6
39	0.58743	-0.80852	-0.034899	B7
40	0.57154	-0.78666	-0.23345	B8
41	0.53271	-0.73322	-0.42262	B9
42	0.78666	-0.57154	-0.23345	B10
43	0.80852	-0.58743	-0.034899	B11
44	0.79792	-0.57972	0.16505	B12
45	0.75528	-0.54874	0.35837	B13
46	0.95048	-0.30883	-0.034899	B14
47	0.93801	-0.30478	0.16505	B15
48	0.88789	-0.28849	0.35837	B16
49	0.77919	-0.32275	0.5373	B17
50	0.66458	-0.27528	0.69466	B18
51	0.49052	-0.2832	0.82413	B19
52	0.39073	0	0.9205	B20
53	0.56641	0	0.82413	B21
54	0.71934	0	0.69466	B22
55	0.84339	0	0.5373	B23
56	0.93358	0	0.35837	B24
57	0.98629	0	0.16505	B25
58	0.99939	0	-0.034899	B26
59	0.95048	0.30883	-0.034899	B27
60	0.93801	0.30478	0.16505	B28
61	0.88789	0.28849	0.35837	B29
62	0.77919	0.32275	0.5373	B30
63	0.66458	0.27528	0.69466	B31
64	0.49052	0.2832	0.82413	B32
65	0.11719	0.16129	0.97992	C1
66	0.27629	0.27629	0.9205	C2
67	0.50865	0.50865	0.69466	C3
68	0.59637	0.59637	0.5373	C4
69	0.75528	0.54874	0.35837	C5
70	0.79792	0.57972	0.16505	C6
71	0.80852	0.58743	-0.034899	C7
72	0.58743	0.80852	-0.034899	C8
73	0.57972	0.79792	0.16505	C9
74	0.54874	0.75528	0.35837	C10
75	0.2832	0.49052	0.82413	C11
76	0.27528	0.66458	0.69466	C12
77	0.32275	0.77919	0.5373	C13
78	0.28849	0.88789	0.35837	C14
79	0.30478	0.93801	0.16505	C15
80	0.30883	0.95048	-0.034899	C16
81	0	0.99939	-0.034899	C17
82	0	0.98629	0.16505	C18
83	0	0.93358	0.35837	C19
84	0	0.84339	0.5373	C20
85	0	0.71934	0.69466	C21
86	0	0.56641	0.82413	C22
87	0	0.39073	0.9205	C23
88	-0.2832	0.49052	0.82413	C24
89	-0.27528	0.66458	0.69466	C25
90	-0.32275	0.77919	0.5373	C26
91	-0.28849	0.88789	0.35837	C27
92	-0.30478	0.93801	0.16505	C28
93	-0.30883	0.95048	-0.034899	C29
94	-0.58743	0.80852	-0.034899	C30
95	-0.57972	0.79792	0.16505	C31
96	-0.54874	0.75528	0.35837	C32
97	-0.11719	0.16129	0.97992	D1
98	-0.27629	0.27629	0.9205	D2
99	-0.50865	0.50865	0.69466	D3
100	-0.59637	0.59637	0.5373	D4
101	-0.75528	0.54874	0.35837	D5
102	-0.79792	0.57972	0.16505	D6
103	-0.80852	0.58743	-0.034899	D7
104	-0.95048	0.30883	-0.034899	D8
105	-0.93801	0.30478	0.16505	D9
106	-0.88789	0.28849	0.35837	D10
107	-0.77919	0.32275	0.5373	D11
108	-0.66458	0.27528	0.69466	D12
109	-0.49052	0.2832	0.82413	D13
110	-0.39073	0	0.9205	D14
111	-0.18961	-0.061608	0.97992	D15
112	-0.27629	-0.27629	0.9205	D16
113	-0.49052	-0.2832	0.82413	D17
114	-0.56641	0	0.82413	D18
115	-0.71934	0	0.69466	D19
116	-0.84339	0	0.5373	D20
117	-0.93358	0	0.35837	D21
118	-0.98629	0	0.16505	D22
119	-0.99939	0	-0.034899	D23
120	-0.95048	-0.30883	-0.034899	D24
121	-0.93801	-0.30478	0.16505	D25
122	-0.88789	-0.28849	0.35837	D26
123	-0.77919	-0.32275	0.5373	D27
124	-0.66458	-0.27528	0.69466	D28
125	-0.75528	-0.54874	0.35837	D29
126	-0.79792	-0.57972	0.16505	D30
127	-0.80852	-0.58743	-0.034899	D31
128	-0.78666	-0.57154	-0.23345	D32
129	-0.57219	0.78756	-0.2288	EXG1
130	0.58743	0.80852	-0.2349	EXG2
131	-0.30883	0.95048	-0.4349	EXG3
132	0.30883	0.95048	-0.4349	EXG4
133	-0.97751	-0.15881	-0.13872	EXG5
134	0.95048	-0.15442	-0.1349	EXG6
135	0	0.99939	-0.3349	EXG7
136	0	0	0	EXG8

we can successfully load and visualize the montage:

import mne

montage_path = '~/Development/Support/mne-python/montage-xyz/montage_fixed_delim.xyz'
montage = mne.channels.read_custom_montage(fname=montage_path)
montage.plot()

output

But you can see that something is off: the head circle is way too small. This is because MNE interprets the values in the XYZ file as coordinates in meters, and the head circle is made for a head with a … well, more natural size.

The problem with all these different data formats is that they’re so poorly specified. So while we could try to fix the reader in MNE, I’d suggest to manually import and scale the original data, so you wouldn’t even have to worry about this “mixed tabs and spaces thing” and can work directly with the file you originally shared above. We’ll use pandas to read the data; pandas can readily handle mixed delimiters (which pandas calls “separators”, as it uses the term “delimiters” for a different thing…):

import pandas as pd
import mne

montage_path = '~/Development/Support/mne-python/montage-xyz/montage.xyz'
montage_data = pd.read_csv(
    montage_path,
    sep='\s+|\t+',  # multiple spaces or tabs
    header=None,
    names=['ch_idx', 'x', 'y', 'z', 'ch_name'],
    engine='python'  # avoid a warning
)
montage_data = montage_data.drop(columns=['ch_idx'])  # We don't need it
montage_data = montage_data.set_index('ch_name', drop=True)

# Now scale based on the head radius (i.e., a value of 1 becomes equal to the head radius)
head_radius = 0.095  # in meters
montage_data *= head_radius 

# %%
# Prepare for creation of our custom montage – MNE needs the data in the following shape
ch_name_to_pos_mapping = montage_data.T.to_dict(orient='list')

# Finally, create the montage
montage = mne.channels.make_dig_montage(
    ch_pos=montage_data.T.to_dict(orient='list')  # need to get it into the right shape
)
montage.plot()

output

Voila!

2 Likes