Related
I have a directed with no-cycles networkX graph. I would like to create a subgraph with only all direct or direct predecessors of a given node n. For instance, if n has 3 predecessors, a, b and c, I will also search for predecessors for each of those 3 nodes.
I am currently using the ego_graph method of networkX, this works perfectly but the output also keeps sibling nodes with no direct access to my target node since it's an directed graph.
def draw(graph_path: Path, target: str, radius: int)
graph = nx.read_graphml(graphml)
subgraph = nx.ego_graph(graph, target, undirected=True, radius=radius)
draw_graph(subgraph, table)
My undirected is set to False because when I set it True, it is only retuning my target only, does not matter what the radius value is.
Target node is called CORE - SUPPLY CHAIN [DEV].20220128 AS_IS stock_V1norm.DIM calendar and with a radius of 1:
The result is what I am expecting.
Now, same target but with a radius of 2:
The result is not what I was expecting since I am getting sibling and I only wants to get predecessors nodes such as:
graphML sample:
<?xml version='1.0' encoding='utf-8'?>
<graphml xmlns="http://graphml.graphdrawing.org/xmlns" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns http://graphml.graphdrawing.org/xmlns/1.0/graphml.xsd">
<key id="d1" for="node" attr.name="kind" attr.type="string" />
<key id="d0" for="node" attr.name="zone" attr.type="string" />
<graph edgedefault="directed">
<node id="gold_core.customers">
<data key="d0">gold</data>
<data key="d1">core</data>>
</node>
<node id="rt.F0116">
<data key="d0">silver</data>>
</node>
<node id="hy.F4211">
<data key="d0">silver</data>
</node>
<edge
source="hy.F4211"
target="gold_core.customers"
/>
You can get the predecessors for a node using the DiGraph.predecessors method.
#!/usr/bin/env python
"""
Find predecessors to a given node.
"""
import matplotlib.pyplot as plt
import networkx as nx
from netgraph import Graph # pip install netgraph
# create a test graph
edges = [
('parent a', 'target'),
('parent b', 'target'),
('parent c', 'target'),
('grandparent aa', 'parent a'),
('grandparent bb', 'parent b'),
('grandparent cc', 'parent c'),
('parent a', 'sibling'),
('target', 'child')
]
g = nx.from_edgelist(edges, create_using=nx.DiGraph)
# get predecessors
parents = list(g.predecessors('target'))
grandparents = []
for parent in parents:
for grandparent in list(g.predecessors(parent)):
grandparents.append(grandparent)
predecessors = parents + grandparents
# give predecessors a red color
node_color = dict()
for node in g:
if node in predecessors:
node_color[node] = 'red'
else:
node_color[node] = 'white'
# plot
fig, (ax1, ax2) = plt.subplots(1, 2)
Graph(g,
node_layout='dot',
arrows=True,
node_color=node_color,
node_labels=True,
node_label_fontdict=dict(size=10),
node_label_offset=0.1,
ax=ax1
)
# plot subgraph
subgraph = g.subgraph(predecessors + ['target'])
Graph(subgraph,
node_layout='dot',
arrows=True,
node_labels=True,
node_label_fontdict=dict(size=10),
node_label_offset=0.1,
ax=ax2,
)
plt.show()
I'm trying to change the color of an SVG shape upon mouse hover.
Doesn't work.
<style>
a:hover {cursor: pointer}
path:hover {fill: #e51451; opacity: 1}
</style>
The cursor indeed changes to "pointer", so I know that the style tag is fine, but the color doesn't change. Instead of path I tried to select a, or g, but nothing will do.
By the way, the <style> tag is within the <svg>. Is there an alternative to this? Can I do this from my style.css? The SVG is embedded in the HTML as <object>. Can I access and manipulate it from style.css?
Here's the code. Now: the first <a>, named "Pricing", doesn't change when I hover. The second, named "About", turns white (stroke) when I hover.
I've struggled with this the whole day. I can't see the difference in code between the two elements. What am I missing?
And while we're at it: why does this work only with stroke but not with fill? If I change to a:hover path {fill: #ffffff; opacity: 1}, nothing happens anymore.
I appreciate your help!
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink">
<style>
a:hover {cursor: pointer}
a:hover path {stroke: #ffffff; opacity: 1}
</style>
<a
xlink:href="index.html#Pricing"
style="fill:#6e5e33;fill-opacity:1"
target="_top"
id="PricingHREF">
<g
pointer-events="all"
transform="matrix(0.05931772,0,0,0.05931772,111.78048,51.466453)"
style="fill:#6e5e33;fill-opacity:1">
<rect
y="54.223179"
x="112.65012"
height="24.7752"
width="27.46353"
id="rect850-8"
style="opacity:0;fill:#6e5e33;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.80872834;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path1015"
d="m 255.994,57.339 c -109.543,0 -198.655,89.123 -198.655,198.667 0,109.542 89.111,198.654 198.655,198.654 109.549,0 198.666,-89.111 198.666,-198.654 0,-109.544 -89.117,-198.667 -198.666,-198.667 z m 0,365.985 c -92.259,0 -167.319,-75.059 -167.319,-167.318 0,-92.271 75.06,-167.331 167.319,-167.331 92.271,0 167.33,75.06 167.33,167.331 0,92.259 -75.058,167.318 -167.33,167.318 z"
inkscape:connector-curvature="0"
style="fill:#6e5e33;fill-opacity:1" />
<path
id="path1017"
d="m 277.33,190.101 c 19.613,0 34.643,5.21 51.197,12.254 l 4.287,-34.641 c -15.633,-7.659 -35.871,-11.338 -55.484,-11.338 -46.593,0 -83.073,27.898 -94.104,72.96 H 160.84 l -8.588,24.823 h 27.597 v 4.294 c 0,5.826 0.308,11.65 0.917,16.861 h -28.2 l -9.191,26.367 h 44.134 c 14.715,36.787 48.439,58.848 89.822,58.848 19.613,0 39.852,-3.678 55.484,-11.338 l -4.287,-34.641 c -16.555,7.057 -31.584,12.268 -51.197,12.268 -22.687,0 -38.319,-9.203 -47.522,-25.137 h 56.104 l 9.197,-26.367 h -73.877 c -0.929,-5.211 -1.23,-10.723 -1.23,-16.861 v -4.294 h 71.73 l 9.203,-24.823 h -76.941 c 7.345,-23.917 24.521,-39.235 53.335,-39.235 z"
inkscape:connector-curvature="0"
style="fill:#6e5e33;fill-opacity:1" />
</g>
</a>
<a
xlink:href="index.html#About"
style="fill:#6e5e33;fill-opacity:1"
target="_top"
id="AboutHREF">
<g
transform="translate(-67.420614,-6.3980429)"
pointer-events="all"
style="fill:#6e5e33;fill-opacity:1">
<rect
style="opacity:0;fill:#6e5e33;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.80872834;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="rect849"
width="27.46353"
height="24.7752"
x="84.07074"
y="52.621223" />
<path
id="path1076"
d="m 98.242846,59.4605 c -1.001433,0 -1.837351,0.349807 -2.506364,0.917981 -0.669013,0.568174 -1.060546,1.02925 -1.173207,2.420129 h 2.042506 c 0.04451,-0.695439 0.210718,-0.856086 0.497935,-1.120353 0.287216,-0.264267 0.643977,-0.429782 1.071672,-0.429782 0.427695,0 0.776111,0.121007 1.045941,0.397096 0.270526,0.275394 0.405441,0.613378 0.405441,1.029251 0,0.415873 -0.130047,0.760811 -0.388055,1.042464 l -1.232319,1.230232 c -0.360237,0.360238 -0.435345,0.649541 -0.542443,0.868604 -0.107097,0.219064 -0.0021,0.552179 -0.0021,1.002129 v 0.848436 h 1.390879 v -0.578606 c 0,-0.449254 0.200982,-0.798365 0.404051,-1.045941 0.07789,-0.08971 0.231581,-0.199591 0.362324,-0.328943 0.129351,-0.130047 0.311556,-0.281653 0.497243,-0.456208 0.18568,-0.17386 0.34772,-0.317816 0.46038,-0.430477 0.11197,-0.111966 0.28096,-0.303907 0.49515,-0.573738 0.37067,-0.449949 0.55983,-1.01256 0.55983,-1.687832 0,-0.979178 -0.31295,-1.740685 -0.94301,-2.2873 C 100.05725,59.733113 99.244279,59.4605 98.242846,59.4605 Z"
inkscape:connector-curvature="0"
style="fill:#6e5e33;fill-opacity:1;stroke-width:0.69543952" />
<path
id="path1078"
d="m 98.11558,68.462965 c -0.354674,0 -0.657885,0.124484 -0.911721,0.371365 -0.25314,0.247576 -0.37971,0.542442 -0.37971,0.88599 0,0.342851 0.129352,0.635631 0.388055,0.877644 0.258704,0.242013 0.565393,0.362324 0.919371,0.362324 0.354675,0 0.658582,-0.123788 0.911722,-0.370669 0.252444,-0.247576 0.37971,-0.543138 0.37971,-0.88599 0,-0.342852 -0.130048,-0.635632 -0.388751,-0.87834 -0.257313,-0.242013 -0.564002,-0.362324 -0.918676,-0.362324 z"
inkscape:connector-curvature="0"
style="fill:#6e5e33;fill-opacity:1;stroke-width:0.69543952" />
<path
id="path1080"
d="m 98.157307,53.062457 c -6.529482,0 -11.822472,5.29299 -11.822472,11.822472 0,6.529481 5.29299,11.822471 11.822472,11.822471 6.529483,0 11.822473,-5.29299 11.822473,-11.822471 0,-6.529482 -5.29299,-11.822472 -11.822473,-11.822472 z m 0,21.558625 c -5.377139,0 -9.736153,-4.359015 -9.736153,-9.736153 0,-5.377139 4.359014,-9.736153 9.736153,-9.736153 5.377133,0 9.736153,4.359014 9.736153,9.736153 0,5.377138 -4.35902,9.736153 -9.736153,9.736153 z"
inkscape:connector-curvature="0"
style="display:inline;fill:#6e5e33;fill-opacity:1;stroke-width:0.69543952" />
</g>
</a>
</svg>
if you add svg before the path:hover it should work
svg path:hover {
cursor: pointer;
fill: #007cb0;
}
<a><svg width="100" height="100" version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 27.2 32.6" enable-background="new 0 0 27.2 32.6" xml:space="preserve">
<g><path d="M7.9,22.1L4.7,32H0.5L11.2,0.6H16L26.8,32h-4.3l-3.4-9.9H7.9z M18.2,18.9l-3.1-9c-0.7-2-1.2-3.9-1.6-5.7h-0.1 c-0.5,1.9-1,3.8-1.6,5.7l-3.1,9.1H18.2z"/></g>
</svg></a>
NOTE: This is an old post, I've figured out most of the questions in the bullet points, read further for notes. I've written my remaining problem in the first edit.
Now that I discovered the new 3.0 Here Maps after the 2.n series, I don't know how to achieve the following:
Clear the whole map
Update marker positions using some marker ID
Remove some specific markers using some ID
<edit: The last edit this far, I'll add it here as this got long. I rushed things, it looks like I don't know how to redraw markers on the map without causing marker flickering (that is, the map does not flicker, but the markers do upon removing and readding)! The invalidateObject is a method in ``H.map.clustering
Question: How should one update marker positions and then redraw the map?
See further for code as more context for this question. I took a quick way by removing the objects without holding a reference to previously added markers, updating their position and then redrawing the map. Alas, I couldn't figure out how to just update marker positions! There's about 50 markers which position I update every second or so. A point of note is that just removing all the objects and then re-adding them (with approximatelly the same looking standard markers) didn't cause flickering (both have useCIT, in 3.0 series I use also HTTPS) in the version 2.n of the API. The flickering happens on Windows 8.1 on IE, FF and Chrome and on Windows Phones with the 3.0 version.
//Copied from http://stackoverflow.com/questions/26020199/here-nokia-maps-javascript-api-3-0-explorer-how-to-set-maker-color.
var markup = '<svg xmlns="http://www.w3.org/2000/svg" width="28px" height="36px" >' +
'<path d="M 19 31 C 19 32.7 16.3 34 13 34 C 9.7 34 7 32.7 7 31 C 7 29.3 9.7 ' +
'28 13 28 C 16.3 28 19 29.3 19 31 Z" fill="#000" fill-opacity=".2"></path>' +
'<path d="M 13 0 C 9.5 0 6.3 1.3 3.8 3.8 C 1.4 7.8 0 9.4 0 12.8 C 0 16.3 1.4 ' +
'19.5 3.8 21.9 L 13 31 L 22.2 21.9 C 24.6 19.5 25.9 16.3 25.9 12.8 C 25.9 9.4 24.6 ' +
'6.1 22.1 3.8 C 19.7 1.3 16.5 0 13 0 Z" fill="#fff"></path>' +
'<path d="M 13 2.2 C 6 2.2 2.3 7.2 2.1 12.8 C 2.1 16.1 3.1 18.4 5.2 20.5 L ' +
'13 28.2 L 20.8 20.5 C 22.9 18.4 23.8 16.2 23.8 12.8 C 23.6 7.07 20 2.2 ' +
'13 2.2 Z" fill="${COLOR}"></path><text transform="matrix( 1 0 0 1 13 18 )" x="0" y="0" fill-opacity="1" ' +
'fill="#fff" text-anchor="middle" font-weight="bold" font-size="13px" font-family="arial" style="fill:black">${TEXT}</text></svg>'
function createMarker(id, la, ln) {
var icon = new H.map.Icon(markup.replace('${COLOR}', '#FF8800').replace('${TEXT}', id));
var marker = new H.map.Marker({ lat: la, lng: ln }, { icon: icon });
return marker;
}
var updates = updateBatch;
var len = updates.length;
var newMarkers = [len];
for (i = 0; i < len; i++) {
var update = updates[i];
var marker = createMarker(update.Id, update.Latitude, update.Longitude);
newMarkers[i] = marker;
}
map.removeObjects(map.getObjects());
map.addObjects(newMarkers);
For instance, in the 2.n series for clearing the map there's {map}.objects.clear, but it looks like it's gone now and I can't locate the documentation for the 3.0 version of the API.
<edit: After a few hours of sleep I spotted the issue about removing on Maps API for JavaScript Developer's Guide (3.0.5) on page 26 and later, e.g. on page 76, there's removeObjects() to clear all.
Now I just need to figure out how to update marker positions that have been added to the map. A short snippet from the documentation:
Adding and Removing Objects
Each map object type corresponds to a class in the API. A newly created instance of such a class does not automatically appear on the map, but, like a node in the HTML document object model (DOM), must be added to the root. This means that to make an object appear on the map, it must be added to the map's root group through a call to the map object's method addObject(). Conversely, to remove an object from the map, a call to the map object's method removeObject() is required.
Groups have their own addObject() and removeObject() methods and behave like container
elements in the HTML document object model. It is possible to add an empty group to the map and add individual objects later. The code below demonstrates how to create an empty group, add it to the map, then create a marker and make it a member of the group.
// Create a group that can hold map objects:
group = new H.map.Group();
// Add the group to the map object (created earlier):
map.addObject(group);
// Create a marker:
marker = new H.map.Marker(map.getCenter());
// Add the marker to the group (which causes
// it to be displayed on the map)
group.addObject(marker);
<edit 2: It looks like there's a method invalidateObject (mapObject, changes) in H.map.provider, which takes a H.map object. Then changes is a bitmask that defines the types of changes, but the documentation doesn't tell what should be put in if I'd like to redraw the map with markers in the new positions, there' just
signed 32 bit integer (JS restriction) where bit operator can be applied to. The range is
[-2,147,483,648 ... 2,147,483,647] or [-2^31 ... 2^31 − 1]
Question: Should one use, say, -1 as the bitmask to redraw a map object?
(I'm off the development tools for a while now, so can't the hypothesis, though I'd like to know about these bitmasks in any case.)
In case of removing all the objects and then re-adding them, the trick is to add the markers to two H.map.Group objects to set up double buffering by using setVisibility = true/false, calling .removeAll to the one that has has been switched to background (by setting visibility to false) and switching the marker container variables.
A more elegant solution would be to have just one container, update the location of the markers there and then just remove the ones not having an update (or some other appropriate action).
function nonUpdated(markers, updates) {
var map = {};
var updatedMarkers = [];
var nonUpdatedMarkers = [];
for (var i = updates.length; i--;) {
map[updates[i].Id] = null;
}
//The updated ones are here in case someone finds the code useful (remove in production).
for (var key in markers) {
if (map.hasOwnProperty(key)) {
updatedMarkers.push(markers[key]);
} else {
nonUpdatedMarkers.push(markers[key]);
}
}
return nonUpdatedMarkers;
}
//JS 6 brings Map, which is superior: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map.
var markers = Object.create(null);
var g = new H.map.Group();
map.addObject(g);
var updates = updateBatch;
var len = updates.length;
for (i = 0; i < len; i++) {
var update = updates[i];
if (update.Id in markers) {
markers[update.Id].setPosition({ lat: update.Latitude, lng: update.Longitude });
} else {
var marker = createMarker(update.Id, update.Latitude, update.Longitude);
markers[update.Id] = marker;
g.addObject(marker);
}
}
var nonUpdatedToBeRemoved = nonUpdated(markers, updates);
if (nonUpdatedToBeRemoved.length > 0) {
g.removeObjects(nonUpdatedToBeRemoved);
}
I have these set of targets and actuals:
Actual: "-20" / Target: "-10"
Actual" "50" / Target: "-5"
Actual: "-10" / target: "30"
Target values are anticipated values for each of the 3 categories and actual values are year to date actual values.
On the first category; in was anticipated that there would be -10 sales compared to the previous period. It turned out to be -20 at the end of the current period. The answer could be -100% or -200%. None of these percentages make sense since percentage completed shouldn't be a negative amount. Another reason that makes the percentages unreasonable is that I cannot perceive the difference between 100% and -100% in this case.
On the 2nd category, it was anticipated that there would be 5 less sales in the current period but turns out there was actually 50 sales in the current period. The answer should be +1100% if we agree that every amount of 5 is a 100%.
EDIT: Same as above, the answer for the third category should be -133%
I want to see how much of the target is fulfilled. If actual=target then the answer is 100% although this doesn't make sense if both the actual and the target are negative amount.
If I use (actual/target)*100 negative amounts are always wrong. I need a general formula to calculate the correct answer. I don't mind if the formula has many conditional definitions. How can I do this?
When involving negative amount, you should always know what it is that you are looking for.
example 1:
if you use the absolute value, you should agree that target=10 and actual=-5 is and should be 50%. however, the 'pure' mathematical way to look at it is -50%.
A logical explanation for this is that actual=0 is, as logic predicts, 0%, -5 is even worse! since not only no progress was made, but rather a regression occurred, hence -50% is an understandable result.
example 2:
When both are negative then for target=-10 and actual=-20, since anchoring point is 0, the 'pure' mathematical result is 200% - and is correct (depending on your point of view of course) since you wanted a decrease of 10 and got a decrease of 20.
Note:if you want to define your wanted output differently, do so and we will try and come up with a 'custom' percentage calculation method.
Edit:
What you could try in your case (Although I must say I don't agree with this approach):
if target>0 and actual > 0 : (the usual) :
(actual/target)*100
if target < 0 and actual < 0 : (the usual negative) :
if (target>actual) - actual is worse than expected :
-(actual/target)*100
if (target < actual) - actual is better than expected :
(actual/target)*100
if target>0 and actual < 0 :
((actual-target)/target)*100
corresponds with target=50 , actual = -100 -> result = -300%
if target<0 and actual > 0 :
(abs(target)+actual)/abs(target))*100
so that for target = -50 , but actual = 100 -> result = 300%
I believe that covers your options and suits your needs.
Edit:
A good approach to your issue from my point of view is to look at absolute values (rather than differential values):
Lets say your sales in month A is 200, and you want a 10% increase in month A+1 -> set your target at 220 and then compare the actual to it, you can also compare it month's A actual and overall a report would use the absolute values for comparison, those are always positive, and can be understood more clearly.
now this:
target = -10% , actual +5% and base value of last month 100
will simply be this:
target = 90 actual =105 => Overall performance of 105/90 , or (105/90)-1 higher than expected.
If you want to treat Actual “-50” / Target “50” as 100% fulfilled, you should use the absolute value function in your formula.
| ((actual / target) * 100) |
How you use it in your code depends on the language. In JavaScript, your formula would be like this:
Math.abs((actual / target) * 100)
If this is not how you want your scoring to work, please provide an example of what the score should be when the target or actual is negative.
Based on your edit with more details about what you want, here is some JavaScript that implements that formula:
function percent_difference(target, actual) {
if (target === 0 || actual === 0) {
if (actual > target) {
return 100;
} else if (actual < target) {
return -100;
} else {
return 0;
}
}
var relative_to = Math.min(Math.abs(actual), Math.abs(target));
var distance_from_target_to_actual = actual - target;
var fraction_difference = distance_from_target_to_actual / relative_to;
return 100 * fraction_difference;
}
I tried to avoid unnecessary if statements to keep the code simple.
The function passes these tests:
function test_percent_difference() {
console.log("percent_difference(-10, -20)", percent_difference(-10, -20), "should be", -100);
console.log("percent_difference(-5, 50)", percent_difference(-5, 50), "should be", 1100);
console.log("percent_difference(30, -10)", percent_difference(30, -10), "should be", -400);
console.log("percent_difference(15, 0)", percent_difference(15, 0), "should be", 100);
console.log("percent_difference(0, 0)", percent_difference(0, 0), "should be", 0);
}
You can run it for yourself in your browser in this jsFiddle.
Here is the solution with R.
Assume your data is sample with Target and Actual columns:
sample<-structure(list(Actual = c(-20L, 50L, -10L), Target = c(-10L,
-5L, 30L)), .Names = c("Actual", "Target"), row.names = c(NA,
-3L), class = "data.frame")
sample<-
Actual Target
1 -20 -10
2 50 -5
3 -10 30
#first I compute the percentage deviation as ((Actual-Target)/Actual)*100
#then I will use following two conditions:
# if Actual<0 and Target>0 multiply by -1
#if Actual<0 and Target<0 and if absolute(Actual)>absolute(Target) multiply by -1 else leave as original percent
sample$diff<-with(sample,((Actual-Target)/Actual)*100)
> sample
Actual Target diff
1 -20 -10 50
2 50 -5 110
3 -10 30 400
sample$percent<-with(sample,ifelse((Actual<0 & Target>0),diff*(-1),ifelse((Actual<0 & Target<0),ifelse((abs(Actual)>abs(Target)),diff*-1,diff),diff)))
> sample
Actual Target diff percent
1 -20 -10 50 -50
2 50 -5 110 110
3 -10 30 400 -400
#delete diff column
sample$diff<-NULL
#your final output
> sample
Actual Target percent
1 -20 -10 -50
2 50 -5 110
3 -10 30 -400
Updated:
To match your answers:
sample$diff<-with(sample,((Actual-Target)/Target)*100)
sample$percent<-with(sample,ifelse((Actual<0 & Target>0),diff,ifelse((Actual<0 & Target<0),ifelse((abs(Actual)>abs(Target)),diff*(-1),diff),diff*(-1))))
> sample
Actual Target diff percent
1 -20 -10 100.0000 -100.0000
2 50 -5 -1100.0000 1100.0000
3 -10 30 -133.3333 -133.3333
I am trying to extract some names from a XHTML document--
now I have figured out how to extract all these names in one piece of XQuery code--
For example--
<div class="names">
<b>Names</b>
A B.C D
E F G
H I
</div>
I want to extract first/middle/last names, I understand how to use some of the string functions-- what I would like to know first, is the number of times a space occurs in one of the names above, and also the positions of first and last occurence of space. How do I do this?
One of many solutions:
let $doc :=
<div class="names">
<b>Names</b>
A B.C D
E F G
H I
</div>
for $a in $doc//a
return
let $text := $a/text()
let $spaces := index-of(string-to-codepoints($a), 32)
return <result text="{ $text }" spaces="{ count($spaces) }"
first="{ $spaces[1] }" last="{ $spaces[last()] }"/>
However, it may be easier to use regular expressions instead of working with character positions.