unify polygons and shapes elements - #132

Merged
giovp merged 12 commits into
mainfrom
models/shapes
Feb 20, 2023
Merged

unify polygons and shapes elements#132
giovp merged 12 commits into
mainfrom
models/shapes

Conversation

@giovp

Copy link
Copy Markdown
Member

New Shapes model

This PR unifies polygons and shape elements in a single region element: Shapes.

Motivation

As of now, the Shapes element was stored as an AnnData, with a bunch of metadata to keep track of the type of shapes and size, and with the centroid coordinates store in in adata.obsm["spatial"]. Thinking about it more, it was very much unnecessary:

  • since it's a region element, only minimal amount of information should be stored there, as any annotations would go into Tables -> because of this, the AnnData format is unneccesary
  • it contained a lot of unnecessary metadata for determining which type of shapes are stored (circle, square only so far).
  • it could be simplified by representing it as a geopandas dataframe.

Solution

Since a geopandas.GeoDataFrame is probably the better way to represent Circles and Squares shapes, instead of AnnData, it naturally follows that polygons and shapes should be grouped together. Benefits are the following:

  • unified Shapes element (less complexity)
  • less metadata, offload to shapely handling of different geometry types
  • easier access to aggregation functions for Circles from geopandas.

e.g. example for counting points under circles regions @ivirshup

importgeopandasasgpdfromspatialdata._core.modelsimportShapesModelfromspatialdata._core.modelsimportPointsModelcircles=ShapesModel.parse(np.array([[10, 2], [5, 2]]), geometry=0, radius=[2, 3])
points=PointsModel.parse(
np.array([[11, 3], [5, 3]]), annotation=pd.DataFrame({"a": [0, 1]})
)
spot=circles["geometry"].buffer(distance=circles["radius"])[0]
gpd.points_from_xy(**points[["x", "y"]].compute()).within(spot)

Summary

Changes are minimal:

  • The Polygons element (and models, IO, etc.) it's now called Shapes and it also contains Circles
  • Circles are represented as a geopandas.GeoDataFrame of shapely.Point with additional column for radius
  • The old Shapes element is removed
  • No additional metadata is introduced, only change from IO side is read/write for radius column in the case of Circles

@giovp

Copy link
Copy Markdown
MemberAuthor

tests in transforms are failing but I'd wait for #100 to be merged first before fixing, as there will be many changes to be made as well.

@giovpgiovp mentioned this pull request Feb 13, 2023
@LucaMarconato

LucaMarconato commented Feb 17, 2023

Copy link
Copy Markdown
Member

@giovp all tests are passing now. I did just fixes, no real change. Anyway if you want to review I suggest to diff
70d3f16 (only merged the branches and ran the pre-commits)
with the latest commit.

@codecov

codecovBot commented Feb 17, 2023

Copy link
Copy Markdown

Codecov Report

Merging #132 (3017790) into main (56179d6) will decrease coverage by 0.33%.
The diff coverage is 81.44%.

Additional details and impacted files
@@ Coverage Diff @@## main #132 +/- ##
==========================================
- Coverage 87.51% 87.19% -0.33% 
==========================================
Files 22 22 Lines 3108 3014 -94 ==========================================
- Hits 2720 2628 -92 + Misses 388 386 -2 
Impacted FilesCoverage Δ
spatialdata/__init__.py100.00% <ø> (ø)
spatialdata/_io/__init__.py100.00% <ø> (ø)
spatialdata/_core/_spatialdata.py75.68% <60.00%> (-1.17%)⬇️
spatialdata/_core/_spatial_query.py77.21% <62.50%> (ø)
spatialdata/_core/models.py85.83% <80.00%> (+0.04%)⬆️
spatialdata/_io/format.py88.23% <83.33%> (+0.60%)⬆️
spatialdata/_core/_transform_elements.py85.81% <100.00%> (-1.03%)⬇️
spatialdata/_core/core_utils.py92.39% <100.00%> (-2.93%)⬇️
spatialdata/_io/read.py97.46% <100.00%> (-0.20%)⬇️
spatialdata/_io/write.py96.05% <100.00%> (-0.23%)⬇️
... and 1 more

@kevinyamauchikevinyamauchi left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This LGTM! I think it makes sense to unify the Circles and polygons here. I left a couple of minor comments below.

Comment threadspatialdata/_io/format.py Outdated
Comment threadspatialdata/_core/models.py
Comment on lines +341 to +343
- 0: `Circles`
- 3: `Polygon`
- 6: `MultiPolygon`

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what are the 0, 3, and 6 here?

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thse are defined by GeometryType enum by shapely and are used to set the geometry type in the parser.

@giovp

Copy link
Copy Markdown
MemberAuthor

@LucaMarconato thanks a lot for fixing tests!!! I think we can go on and merge this, one comment:

  • I saw that pyarrow was re-added, do we need it? Think it's an artefact right?

@LucaMarconato

LucaMarconato commented Feb 20, 2023

Copy link
Copy Markdown
Member

Thanks for the PR 😊 we need it because it's used in the PR fixing the __repr__ for SpatialData (also, I noticed that we need it in SpatialData io for thr cosmx reader)

@giovp

giovp commented Feb 20, 2023

Copy link
Copy Markdown
MemberAuthor

Thanks for the PR 😊 we need it because it's used in the PR fixing the __repr__ for SpatialData (also, I noticed that we need it in SpatialData io for thr cosmx reader)

wait, if it's for the cosmx reader then it should be in spatialdata io. There is no other way for the repr to not use pyarrow?

EDIT: wasn't needed to I've removed it again

EDIT EDIT: I was wrong, but do we really want to keep the pyarrow.read_table just for the repr?

@giovp
giovp merged commit 2dfbe25 into mainFeb 20, 2023
@giovp
giovp deleted the models/shapes branch February 20, 2023 13:58
@LucaMarconato

Copy link
Copy Markdown
Member

@giovp yes for the moment better to keep it. Dask will allow to get the length of the dataframe soon (dask/dask#9973), at that point we can remove the dependency.

@ivirshup

Copy link
Copy Markdown
Member

@LucaMarconato I don't think that dask is going to get this soon. I don't see an active PR for it at the moment, and it's been open for a few years now.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@giovp@LucaMarconato@ivirshup@kevinyamauchi
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

unify polygons and shapes elements - #132

Merged
giovp merged 12 commits into
mainfrom
models/shapes
Feb 20, 2023
Merged

unify polygons and shapes elements#132
giovp merged 12 commits into
mainfrom
models/shapes

Conversation

@giovp

Copy link
Copy Markdown
Member

New Shapes model

This PR unifies polygons and shape elements in a single region element: Shapes.

Motivation

As of now, the Shapes element was stored as an AnnData, with a bunch of metadata to keep track of the type of shapes and size, and with the centroid coordinates store in in adata.obsm["spatial"]. Thinking about it more, it was very much unnecessary:

  • since it's a region element, only minimal amount of information should be stored there, as any annotations would go into Tables -> because of this, the AnnData format is unneccesary
  • it contained a lot of unnecessary metadata for determining which type of shapes are stored (circle, square only so far).
  • it could be simplified by representing it as a geopandas dataframe.

Solution

Since a geopandas.GeoDataFrame is probably the better way to represent Circles and Squares shapes, instead of AnnData, it naturally follows that polygons and shapes should be grouped together. Benefits are the following:

  • unified Shapes element (less complexity)
  • less metadata, offload to shapely handling of different geometry types
  • easier access to aggregation functions for Circles from geopandas.

e.g. example for counting points under circles regions @ivirshup

importgeopandasasgpdfromspatialdata._core.modelsimportShapesModelfromspatialdata._core.modelsimportPointsModelcircles=ShapesModel.parse(np.array([[10, 2], [5, 2]]), geometry=0, radius=[2, 3])
points=PointsModel.parse(
np.array([[11, 3], [5, 3]]), annotation=pd.DataFrame({"a": [0, 1]})
)
spot=circles["geometry"].buffer(distance=circles["radius"])[0]
gpd.points_from_xy(**points[["x", "y"]].compute()).within(spot)

Summary

Changes are minimal:

  • The Polygons element (and models, IO, etc.) it's now called Shapes and it also contains Circles
  • Circles are represented as a geopandas.GeoDataFrame of shapely.Point with additional column for radius
  • The old Shapes element is removed
  • No additional metadata is introduced, only change from IO side is read/write for radius column in the case of Circles

@giovp

Copy link
Copy Markdown
MemberAuthor

tests in transforms are failing but I'd wait for #100 to be merged first before fixing, as there will be many changes to be made as well.

@giovpgiovp mentioned this pull request Feb 13, 2023
@LucaMarconato

LucaMarconato commented Feb 17, 2023

Copy link
Copy Markdown
Member

@giovp all tests are passing now. I did just fixes, no real change. Anyway if you want to review I suggest to diff
70d3f16 (only merged the branches and ran the pre-commits)
with the latest commit.

@codecov

codecovBot commented Feb 17, 2023

Copy link
Copy Markdown

Codecov Report

Merging #132 (3017790) into main (56179d6) will decrease coverage by 0.33%.
The diff coverage is 81.44%.

Additional details and impacted files
@@ Coverage Diff @@## main #132 +/- ##
==========================================
- Coverage 87.51% 87.19% -0.33% 
==========================================
Files 22 22 Lines 3108 3014 -94 ==========================================
- Hits 2720 2628 -92 + Misses 388 386 -2 
Impacted FilesCoverage Δ
spatialdata/__init__.py100.00% <ø> (ø)
spatialdata/_io/__init__.py100.00% <ø> (ø)
spatialdata/_core/_spatialdata.py75.68% <60.00%> (-1.17%)⬇️
spatialdata/_core/_spatial_query.py77.21% <62.50%> (ø)
spatialdata/_core/models.py85.83% <80.00%> (+0.04%)⬆️
spatialdata/_io/format.py88.23% <83.33%> (+0.60%)⬆️
spatialdata/_core/_transform_elements.py85.81% <100.00%> (-1.03%)⬇️
spatialdata/_core/core_utils.py92.39% <100.00%> (-2.93%)⬇️
spatialdata/_io/read.py97.46% <100.00%> (-0.20%)⬇️
spatialdata/_io/write.py96.05% <100.00%> (-0.23%)⬇️
... and 1 more

@kevinyamauchikevinyamauchi left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This LGTM! I think it makes sense to unify the Circles and polygons here. I left a couple of minor comments below.

Comment threadspatialdata/_io/format.py Outdated
Comment threadspatialdata/_core/models.py
Comment on lines +341 to +343
- 0: `Circles`
- 3: `Polygon`
- 6: `MultiPolygon`

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what are the 0, 3, and 6 here?

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thse are defined by GeometryType enum by shapely and are used to set the geometry type in the parser.

@giovp

Copy link
Copy Markdown
MemberAuthor

@LucaMarconato thanks a lot for fixing tests!!! I think we can go on and merge this, one comment:

  • I saw that pyarrow was re-added, do we need it? Think it's an artefact right?

@LucaMarconato

LucaMarconato commented Feb 20, 2023

Copy link
Copy Markdown
Member

Thanks for the PR 😊 we need it because it's used in the PR fixing the __repr__ for SpatialData (also, I noticed that we need it in SpatialData io for thr cosmx reader)

@giovp

giovp commented Feb 20, 2023

Copy link
Copy Markdown
MemberAuthor

Thanks for the PR 😊 we need it because it's used in the PR fixing the __repr__ for SpatialData (also, I noticed that we need it in SpatialData io for thr cosmx reader)

wait, if it's for the cosmx reader then it should be in spatialdata io. There is no other way for the repr to not use pyarrow?

EDIT: wasn't needed to I've removed it again

EDIT EDIT: I was wrong, but do we really want to keep the pyarrow.read_table just for the repr?

@giovp
giovp merged commit 2dfbe25 into mainFeb 20, 2023
@giovp
giovp deleted the models/shapes branch February 20, 2023 13:58
@LucaMarconato

Copy link
Copy Markdown
Member

@giovp yes for the moment better to keep it. Dask will allow to get the length of the dataframe soon (dask/dask#9973), at that point we can remove the dependency.

@ivirshup

Copy link
Copy Markdown
Member

@LucaMarconato I don't think that dask is going to get this soon. I don't see an active PR for it at the moment, and it's been open for a few years now.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@giovp@LucaMarconato@ivirshup@kevinyamauchi
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

unify polygons and shapes elements - #132

Merged
giovp merged 12 commits into
mainfrom
models/shapes
Feb 20, 2023
Merged

unify polygons and shapes elements#132
giovp merged 12 commits into
mainfrom
models/shapes

Conversation

@giovp

Copy link
Copy Markdown
Member

New Shapes model

This PR unifies polygons and shape elements in a single region element: Shapes.

Motivation

As of now, the Shapes element was stored as an AnnData, with a bunch of metadata to keep track of the type of shapes and size, and with the centroid coordinates store in in adata.obsm["spatial"]. Thinking about it more, it was very much unnecessary:

  • since it's a region element, only minimal amount of information should be stored there, as any annotations would go into Tables -> because of this, the AnnData format is unneccesary
  • it contained a lot of unnecessary metadata for determining which type of shapes are stored (circle, square only so far).
  • it could be simplified by representing it as a geopandas dataframe.

Solution

Since a geopandas.GeoDataFrame is probably the better way to represent Circles and Squares shapes, instead of AnnData, it naturally follows that polygons and shapes should be grouped together. Benefits are the following:

  • unified Shapes element (less complexity)
  • less metadata, offload to shapely handling of different geometry types
  • easier access to aggregation functions for Circles from geopandas.

e.g. example for counting points under circles regions @ivirshup

importgeopandasasgpdfromspatialdata._core.modelsimportShapesModelfromspatialdata._core.modelsimportPointsModelcircles=ShapesModel.parse(np.array([[10, 2], [5, 2]]), geometry=0, radius=[2, 3])
points=PointsModel.parse(
np.array([[11, 3], [5, 3]]), annotation=pd.DataFrame({"a": [0, 1]})
)
spot=circles["geometry"].buffer(distance=circles["radius"])[0]
gpd.points_from_xy(**points[["x", "y"]].compute()).within(spot)

Summary

Changes are minimal:

  • The Polygons element (and models, IO, etc.) it's now called Shapes and it also contains Circles
  • Circles are represented as a geopandas.GeoDataFrame of shapely.Point with additional column for radius
  • The old Shapes element is removed
  • No additional metadata is introduced, only change from IO side is read/write for radius column in the case of Circles

@giovp

Copy link
Copy Markdown
MemberAuthor

tests in transforms are failing but I'd wait for #100 to be merged first before fixing, as there will be many changes to be made as well.

@giovpgiovp mentioned this pull request Feb 13, 2023
@LucaMarconato

LucaMarconato commented Feb 17, 2023

Copy link
Copy Markdown
Member

@giovp all tests are passing now. I did just fixes, no real change. Anyway if you want to review I suggest to diff
70d3f16 (only merged the branches and ran the pre-commits)
with the latest commit.

@codecov

codecovBot commented Feb 17, 2023

Copy link
Copy Markdown

Codecov Report

Merging #132 (3017790) into main (56179d6) will decrease coverage by 0.33%.
The diff coverage is 81.44%.

Additional details and impacted files
@@ Coverage Diff @@## main #132 +/- ##
==========================================
- Coverage 87.51% 87.19% -0.33% 
==========================================
Files 22 22 Lines 3108 3014 -94 ==========================================
- Hits 2720 2628 -92 + Misses 388 386 -2 
Impacted FilesCoverage Δ
spatialdata/__init__.py100.00% <ø> (ø)
spatialdata/_io/__init__.py100.00% <ø> (ø)
spatialdata/_core/_spatialdata.py75.68% <60.00%> (-1.17%)⬇️
spatialdata/_core/_spatial_query.py77.21% <62.50%> (ø)
spatialdata/_core/models.py85.83% <80.00%> (+0.04%)⬆️
spatialdata/_io/format.py88.23% <83.33%> (+0.60%)⬆️
spatialdata/_core/_transform_elements.py85.81% <100.00%> (-1.03%)⬇️
spatialdata/_core/core_utils.py92.39% <100.00%> (-2.93%)⬇️
spatialdata/_io/read.py97.46% <100.00%> (-0.20%)⬇️
spatialdata/_io/write.py96.05% <100.00%> (-0.23%)⬇️
... and 1 more

@kevinyamauchikevinyamauchi left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This LGTM! I think it makes sense to unify the Circles and polygons here. I left a couple of minor comments below.

Comment threadspatialdata/_io/format.py Outdated
Comment threadspatialdata/_core/models.py
Comment on lines +341 to +343
- 0: `Circles`
- 3: `Polygon`
- 6: `MultiPolygon`

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what are the 0, 3, and 6 here?

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thse are defined by GeometryType enum by shapely and are used to set the geometry type in the parser.

@giovp

Copy link
Copy Markdown
MemberAuthor

@LucaMarconato thanks a lot for fixing tests!!! I think we can go on and merge this, one comment:

  • I saw that pyarrow was re-added, do we need it? Think it's an artefact right?

@LucaMarconato

LucaMarconato commented Feb 20, 2023

Copy link
Copy Markdown
Member

Thanks for the PR 😊 we need it because it's used in the PR fixing the __repr__ for SpatialData (also, I noticed that we need it in SpatialData io for thr cosmx reader)

@giovp

giovp commented Feb 20, 2023

Copy link
Copy Markdown
MemberAuthor

Thanks for the PR 😊 we need it because it's used in the PR fixing the __repr__ for SpatialData (also, I noticed that we need it in SpatialData io for thr cosmx reader)

wait, if it's for the cosmx reader then it should be in spatialdata io. There is no other way for the repr to not use pyarrow?

EDIT: wasn't needed to I've removed it again

EDIT EDIT: I was wrong, but do we really want to keep the pyarrow.read_table just for the repr?

@giovp
giovp merged commit 2dfbe25 into mainFeb 20, 2023
@giovp
giovp deleted the models/shapes branch February 20, 2023 13:58
@LucaMarconato

Copy link
Copy Markdown
Member

@giovp yes for the moment better to keep it. Dask will allow to get the length of the dataframe soon (dask/dask#9973), at that point we can remove the dependency.

@ivirshup

Copy link
Copy Markdown
Member

@LucaMarconato I don't think that dask is going to get this soon. I don't see an active PR for it at the moment, and it's been open for a few years now.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@giovp@LucaMarconato@ivirshup@kevinyamauchi
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

unify polygons and shapes elements - #132

Merged
giovp merged 12 commits into
mainfrom
models/shapes
Feb 20, 2023
Merged

unify polygons and shapes elements#132
giovp merged 12 commits into
mainfrom
models/shapes

Conversation

@giovp

Copy link
Copy Markdown
Member

New Shapes model

This PR unifies polygons and shape elements in a single region element: Shapes.

Motivation

As of now, the Shapes element was stored as an AnnData, with a bunch of metadata to keep track of the type of shapes and size, and with the centroid coordinates store in in adata.obsm["spatial"]. Thinking about it more, it was very much unnecessary:

  • since it's a region element, only minimal amount of information should be stored there, as any annotations would go into Tables -> because of this, the AnnData format is unneccesary
  • it contained a lot of unnecessary metadata for determining which type of shapes are stored (circle, square only so far).
  • it could be simplified by representing it as a geopandas dataframe.

Solution

Since a geopandas.GeoDataFrame is probably the better way to represent Circles and Squares shapes, instead of AnnData, it naturally follows that polygons and shapes should be grouped together. Benefits are the following:

  • unified Shapes element (less complexity)
  • less metadata, offload to shapely handling of different geometry types
  • easier access to aggregation functions for Circles from geopandas.

e.g. example for counting points under circles regions @ivirshup

importgeopandasasgpdfromspatialdata._core.modelsimportShapesModelfromspatialdata._core.modelsimportPointsModelcircles=ShapesModel.parse(np.array([[10, 2], [5, 2]]), geometry=0, radius=[2, 3])
points=PointsModel.parse(
np.array([[11, 3], [5, 3]]), annotation=pd.DataFrame({"a": [0, 1]})
)
spot=circles["geometry"].buffer(distance=circles["radius"])[0]
gpd.points_from_xy(**points[["x", "y"]].compute()).within(spot)

Summary

Changes are minimal:

  • The Polygons element (and models, IO, etc.) it's now called Shapes and it also contains Circles
  • Circles are represented as a geopandas.GeoDataFrame of shapely.Point with additional column for radius
  • The old Shapes element is removed
  • No additional metadata is introduced, only change from IO side is read/write for radius column in the case of Circles

@giovp

Copy link
Copy Markdown
MemberAuthor

tests in transforms are failing but I'd wait for #100 to be merged first before fixing, as there will be many changes to be made as well.

@giovpgiovp mentioned this pull request Feb 13, 2023
@LucaMarconato

LucaMarconato commented Feb 17, 2023

Copy link
Copy Markdown
Member

@giovp all tests are passing now. I did just fixes, no real change. Anyway if you want to review I suggest to diff
70d3f16 (only merged the branches and ran the pre-commits)
with the latest commit.

@codecov

codecovBot commented Feb 17, 2023

Copy link
Copy Markdown

Codecov Report

Merging #132 (3017790) into main (56179d6) will decrease coverage by 0.33%.
The diff coverage is 81.44%.

Additional details and impacted files
@@ Coverage Diff @@## main #132 +/- ##
==========================================
- Coverage 87.51% 87.19% -0.33% 
==========================================
Files 22 22 Lines 3108 3014 -94 ==========================================
- Hits 2720 2628 -92 + Misses 388 386 -2 
Impacted FilesCoverage Δ
spatialdata/__init__.py100.00% <ø> (ø)
spatialdata/_io/__init__.py100.00% <ø> (ø)
spatialdata/_core/_spatialdata.py75.68% <60.00%> (-1.17%)⬇️
spatialdata/_core/_spatial_query.py77.21% <62.50%> (ø)
spatialdata/_core/models.py85.83% <80.00%> (+0.04%)⬆️
spatialdata/_io/format.py88.23% <83.33%> (+0.60%)⬆️
spatialdata/_core/_transform_elements.py85.81% <100.00%> (-1.03%)⬇️
spatialdata/_core/core_utils.py92.39% <100.00%> (-2.93%)⬇️
spatialdata/_io/read.py97.46% <100.00%> (-0.20%)⬇️
spatialdata/_io/write.py96.05% <100.00%> (-0.23%)⬇️
... and 1 more

@kevinyamauchikevinyamauchi left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This LGTM! I think it makes sense to unify the Circles and polygons here. I left a couple of minor comments below.

Comment threadspatialdata/_io/format.py Outdated
Comment threadspatialdata/_core/models.py
Comment on lines +341 to +343
- 0: `Circles`
- 3: `Polygon`
- 6: `MultiPolygon`

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what are the 0, 3, and 6 here?

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thse are defined by GeometryType enum by shapely and are used to set the geometry type in the parser.

@giovp

Copy link
Copy Markdown
MemberAuthor

@LucaMarconato thanks a lot for fixing tests!!! I think we can go on and merge this, one comment:

  • I saw that pyarrow was re-added, do we need it? Think it's an artefact right?

@LucaMarconato

LucaMarconato commented Feb 20, 2023

Copy link
Copy Markdown
Member

Thanks for the PR 😊 we need it because it's used in the PR fixing the __repr__ for SpatialData (also, I noticed that we need it in SpatialData io for thr cosmx reader)

@giovp

giovp commented Feb 20, 2023

Copy link
Copy Markdown
MemberAuthor

Thanks for the PR 😊 we need it because it's used in the PR fixing the __repr__ for SpatialData (also, I noticed that we need it in SpatialData io for thr cosmx reader)

wait, if it's for the cosmx reader then it should be in spatialdata io. There is no other way for the repr to not use pyarrow?

EDIT: wasn't needed to I've removed it again

EDIT EDIT: I was wrong, but do we really want to keep the pyarrow.read_table just for the repr?

@giovp
giovp merged commit 2dfbe25 into mainFeb 20, 2023
@giovp
giovp deleted the models/shapes branch February 20, 2023 13:58
@LucaMarconato

Copy link
Copy Markdown
Member

@giovp yes for the moment better to keep it. Dask will allow to get the length of the dataframe soon (dask/dask#9973), at that point we can remove the dependency.

@ivirshup

Copy link
Copy Markdown
Member

@LucaMarconato I don't think that dask is going to get this soon. I don't see an active PR for it at the moment, and it's been open for a few years now.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@giovp@LucaMarconato@ivirshup@kevinyamauchi
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

unify polygons and shapes elements - #132

Merged
giovp merged 12 commits into
mainfrom
models/shapes
Feb 20, 2023
Merged

unify polygons and shapes elements#132
giovp merged 12 commits into
mainfrom
models/shapes

Conversation

@giovp

Copy link
Copy Markdown
Member

New Shapes model

This PR unifies polygons and shape elements in a single region element: Shapes.

Motivation

As of now, the Shapes element was stored as an AnnData, with a bunch of metadata to keep track of the type of shapes and size, and with the centroid coordinates store in in adata.obsm["spatial"]. Thinking about it more, it was very much unnecessary:

  • since it's a region element, only minimal amount of information should be stored there, as any annotations would go into Tables -> because of this, the AnnData format is unneccesary
  • it contained a lot of unnecessary metadata for determining which type of shapes are stored (circle, square only so far).
  • it could be simplified by representing it as a geopandas dataframe.

Solution

Since a geopandas.GeoDataFrame is probably the better way to represent Circles and Squares shapes, instead of AnnData, it naturally follows that polygons and shapes should be grouped together. Benefits are the following:

  • unified Shapes element (less complexity)
  • less metadata, offload to shapely handling of different geometry types
  • easier access to aggregation functions for Circles from geopandas.

e.g. example for counting points under circles regions @ivirshup

importgeopandasasgpdfromspatialdata._core.modelsimportShapesModelfromspatialdata._core.modelsimportPointsModelcircles=ShapesModel.parse(np.array([[10, 2], [5, 2]]), geometry=0, radius=[2, 3])
points=PointsModel.parse(
np.array([[11, 3], [5, 3]]), annotation=pd.DataFrame({"a": [0, 1]})
)
spot=circles["geometry"].buffer(distance=circles["radius"])[0]
gpd.points_from_xy(**points[["x", "y"]].compute()).within(spot)

Summary

Changes are minimal:

  • The Polygons element (and models, IO, etc.) it's now called Shapes and it also contains Circles
  • Circles are represented as a geopandas.GeoDataFrame of shapely.Point with additional column for radius
  • The old Shapes element is removed
  • No additional metadata is introduced, only change from IO side is read/write for radius column in the case of Circles

@giovp

Copy link
Copy Markdown
MemberAuthor

tests in transforms are failing but I'd wait for #100 to be merged first before fixing, as there will be many changes to be made as well.

@giovpgiovp mentioned this pull request Feb 13, 2023
@LucaMarconato

LucaMarconato commented Feb 17, 2023

Copy link
Copy Markdown
Member

@giovp all tests are passing now. I did just fixes, no real change. Anyway if you want to review I suggest to diff
70d3f16 (only merged the branches and ran the pre-commits)
with the latest commit.

@codecov

codecovBot commented Feb 17, 2023

Copy link
Copy Markdown

Codecov Report

Merging #132 (3017790) into main (56179d6) will decrease coverage by 0.33%.
The diff coverage is 81.44%.

Additional details and impacted files
@@ Coverage Diff @@## main #132 +/- ##
==========================================
- Coverage 87.51% 87.19% -0.33% 
==========================================
Files 22 22 Lines 3108 3014 -94 ==========================================
- Hits 2720 2628 -92 + Misses 388 386 -2 
Impacted FilesCoverage Δ
spatialdata/__init__.py100.00% <ø> (ø)
spatialdata/_io/__init__.py100.00% <ø> (ø)
spatialdata/_core/_spatialdata.py75.68% <60.00%> (-1.17%)⬇️
spatialdata/_core/_spatial_query.py77.21% <62.50%> (ø)
spatialdata/_core/models.py85.83% <80.00%> (+0.04%)⬆️
spatialdata/_io/format.py88.23% <83.33%> (+0.60%)⬆️
spatialdata/_core/_transform_elements.py85.81% <100.00%> (-1.03%)⬇️
spatialdata/_core/core_utils.py92.39% <100.00%> (-2.93%)⬇️
spatialdata/_io/read.py97.46% <100.00%> (-0.20%)⬇️
spatialdata/_io/write.py96.05% <100.00%> (-0.23%)⬇️
... and 1 more

@kevinyamauchikevinyamauchi left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This LGTM! I think it makes sense to unify the Circles and polygons here. I left a couple of minor comments below.

Comment threadspatialdata/_io/format.py Outdated
Comment threadspatialdata/_core/models.py
Comment on lines +341 to +343
- 0: `Circles`
- 3: `Polygon`
- 6: `MultiPolygon`

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what are the 0, 3, and 6 here?

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thse are defined by GeometryType enum by shapely and are used to set the geometry type in the parser.

@giovp

Copy link
Copy Markdown
MemberAuthor

@LucaMarconato thanks a lot for fixing tests!!! I think we can go on and merge this, one comment:

  • I saw that pyarrow was re-added, do we need it? Think it's an artefact right?

@LucaMarconato

LucaMarconato commented Feb 20, 2023

Copy link
Copy Markdown
Member

Thanks for the PR 😊 we need it because it's used in the PR fixing the __repr__ for SpatialData (also, I noticed that we need it in SpatialData io for thr cosmx reader)

@giovp

giovp commented Feb 20, 2023

Copy link
Copy Markdown
MemberAuthor

Thanks for the PR 😊 we need it because it's used in the PR fixing the __repr__ for SpatialData (also, I noticed that we need it in SpatialData io for thr cosmx reader)

wait, if it's for the cosmx reader then it should be in spatialdata io. There is no other way for the repr to not use pyarrow?

EDIT: wasn't needed to I've removed it again

EDIT EDIT: I was wrong, but do we really want to keep the pyarrow.read_table just for the repr?

@giovp
giovp merged commit 2dfbe25 into mainFeb 20, 2023
@giovp
giovp deleted the models/shapes branch February 20, 2023 13:58
@LucaMarconato

Copy link
Copy Markdown
Member

@giovp yes for the moment better to keep it. Dask will allow to get the length of the dataframe soon (dask/dask#9973), at that point we can remove the dependency.

@ivirshup

Copy link
Copy Markdown
Member

@LucaMarconato I don't think that dask is going to get this soon. I don't see an active PR for it at the moment, and it's been open for a few years now.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@giovp@LucaMarconato@ivirshup@kevinyamauchi
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

unify polygons and shapes elements - #132

Merged
giovp merged 12 commits into
mainfrom
models/shapes
Feb 20, 2023
Merged

unify polygons and shapes elements#132
giovp merged 12 commits into
mainfrom
models/shapes

Conversation

@giovp

Copy link
Copy Markdown
Member

New Shapes model

This PR unifies polygons and shape elements in a single region element: Shapes.

Motivation

As of now, the Shapes element was stored as an AnnData, with a bunch of metadata to keep track of the type of shapes and size, and with the centroid coordinates store in in adata.obsm["spatial"]. Thinking about it more, it was very much unnecessary:

  • since it's a region element, only minimal amount of information should be stored there, as any annotations would go into Tables -> because of this, the AnnData format is unneccesary
  • it contained a lot of unnecessary metadata for determining which type of shapes are stored (circle, square only so far).
  • it could be simplified by representing it as a geopandas dataframe.

Solution

Since a geopandas.GeoDataFrame is probably the better way to represent Circles and Squares shapes, instead of AnnData, it naturally follows that polygons and shapes should be grouped together. Benefits are the following:

  • unified Shapes element (less complexity)
  • less metadata, offload to shapely handling of different geometry types
  • easier access to aggregation functions for Circles from geopandas.

e.g. example for counting points under circles regions @ivirshup

importgeopandasasgpdfromspatialdata._core.modelsimportShapesModelfromspatialdata._core.modelsimportPointsModelcircles=ShapesModel.parse(np.array([[10, 2], [5, 2]]), geometry=0, radius=[2, 3])
points=PointsModel.parse(
np.array([[11, 3], [5, 3]]), annotation=pd.DataFrame({"a": [0, 1]})
)
spot=circles["geometry"].buffer(distance=circles["radius"])[0]
gpd.points_from_xy(**points[["x", "y"]].compute()).within(spot)

Summary

Changes are minimal:

  • The Polygons element (and models, IO, etc.) it's now called Shapes and it also contains Circles
  • Circles are represented as a geopandas.GeoDataFrame of shapely.Point with additional column for radius
  • The old Shapes element is removed
  • No additional metadata is introduced, only change from IO side is read/write for radius column in the case of Circles

@giovp

Copy link
Copy Markdown
MemberAuthor

tests in transforms are failing but I'd wait for #100 to be merged first before fixing, as there will be many changes to be made as well.

@giovpgiovp mentioned this pull request Feb 13, 2023
@LucaMarconato

LucaMarconato commented Feb 17, 2023

Copy link
Copy Markdown
Member

@giovp all tests are passing now. I did just fixes, no real change. Anyway if you want to review I suggest to diff
70d3f16 (only merged the branches and ran the pre-commits)
with the latest commit.

@codecov

codecovBot commented Feb 17, 2023

Copy link
Copy Markdown

Codecov Report

Merging #132 (3017790) into main (56179d6) will decrease coverage by 0.33%.
The diff coverage is 81.44%.

Additional details and impacted files
@@ Coverage Diff @@## main #132 +/- ##
==========================================
- Coverage 87.51% 87.19% -0.33% 
==========================================
Files 22 22 Lines 3108 3014 -94 ==========================================
- Hits 2720 2628 -92 + Misses 388 386 -2 
Impacted FilesCoverage Δ
spatialdata/__init__.py100.00% <ø> (ø)
spatialdata/_io/__init__.py100.00% <ø> (ø)
spatialdata/_core/_spatialdata.py75.68% <60.00%> (-1.17%)⬇️
spatialdata/_core/_spatial_query.py77.21% <62.50%> (ø)
spatialdata/_core/models.py85.83% <80.00%> (+0.04%)⬆️
spatialdata/_io/format.py88.23% <83.33%> (+0.60%)⬆️
spatialdata/_core/_transform_elements.py85.81% <100.00%> (-1.03%)⬇️
spatialdata/_core/core_utils.py92.39% <100.00%> (-2.93%)⬇️
spatialdata/_io/read.py97.46% <100.00%> (-0.20%)⬇️
spatialdata/_io/write.py96.05% <100.00%> (-0.23%)⬇️
... and 1 more

@kevinyamauchikevinyamauchi left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This LGTM! I think it makes sense to unify the Circles and polygons here. I left a couple of minor comments below.

Comment threadspatialdata/_io/format.py Outdated
Comment threadspatialdata/_core/models.py
Comment on lines +341 to +343
- 0: `Circles`
- 3: `Polygon`
- 6: `MultiPolygon`

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what are the 0, 3, and 6 here?

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thse are defined by GeometryType enum by shapely and are used to set the geometry type in the parser.

@giovp

Copy link
Copy Markdown
MemberAuthor

@LucaMarconato thanks a lot for fixing tests!!! I think we can go on and merge this, one comment:

  • I saw that pyarrow was re-added, do we need it? Think it's an artefact right?

@LucaMarconato

LucaMarconato commented Feb 20, 2023

Copy link
Copy Markdown
Member

Thanks for the PR 😊 we need it because it's used in the PR fixing the __repr__ for SpatialData (also, I noticed that we need it in SpatialData io for thr cosmx reader)

@giovp

giovp commented Feb 20, 2023

Copy link
Copy Markdown
MemberAuthor

Thanks for the PR 😊 we need it because it's used in the PR fixing the __repr__ for SpatialData (also, I noticed that we need it in SpatialData io for thr cosmx reader)

wait, if it's for the cosmx reader then it should be in spatialdata io. There is no other way for the repr to not use pyarrow?

EDIT: wasn't needed to I've removed it again

EDIT EDIT: I was wrong, but do we really want to keep the pyarrow.read_table just for the repr?

@giovp
giovp merged commit 2dfbe25 into mainFeb 20, 2023
@giovp
giovp deleted the models/shapes branch February 20, 2023 13:58
@LucaMarconato

Copy link
Copy Markdown
Member

@giovp yes for the moment better to keep it. Dask will allow to get the length of the dataframe soon (dask/dask#9973), at that point we can remove the dependency.

@ivirshup

Copy link
Copy Markdown
Member

@LucaMarconato I don't think that dask is going to get this soon. I don't see an active PR for it at the moment, and it's been open for a few years now.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@giovp@LucaMarconato@ivirshup@kevinyamauchi
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

unify polygons and shapes elements - #132

Merged
giovp merged 12 commits into
mainfrom
models/shapes
Feb 20, 2023
Merged

unify polygons and shapes elements#132
giovp merged 12 commits into
mainfrom
models/shapes

Conversation

@giovp

Copy link
Copy Markdown
Member

New Shapes model

This PR unifies polygons and shape elements in a single region element: Shapes.

Motivation

As of now, the Shapes element was stored as an AnnData, with a bunch of metadata to keep track of the type of shapes and size, and with the centroid coordinates store in in adata.obsm["spatial"]. Thinking about it more, it was very much unnecessary:

  • since it's a region element, only minimal amount of information should be stored there, as any annotations would go into Tables -> because of this, the AnnData format is unneccesary
  • it contained a lot of unnecessary metadata for determining which type of shapes are stored (circle, square only so far).
  • it could be simplified by representing it as a geopandas dataframe.

Solution

Since a geopandas.GeoDataFrame is probably the better way to represent Circles and Squares shapes, instead of AnnData, it naturally follows that polygons and shapes should be grouped together. Benefits are the following:

  • unified Shapes element (less complexity)
  • less metadata, offload to shapely handling of different geometry types
  • easier access to aggregation functions for Circles from geopandas.

e.g. example for counting points under circles regions @ivirshup

importgeopandasasgpdfromspatialdata._core.modelsimportShapesModelfromspatialdata._core.modelsimportPointsModelcircles=ShapesModel.parse(np.array([[10, 2], [5, 2]]), geometry=0, radius=[2, 3])
points=PointsModel.parse(
np.array([[11, 3], [5, 3]]), annotation=pd.DataFrame({"a": [0, 1]})
)
spot=circles["geometry"].buffer(distance=circles["radius"])[0]
gpd.points_from_xy(**points[["x", "y"]].compute()).within(spot)

Summary

Changes are minimal:

  • The Polygons element (and models, IO, etc.) it's now called Shapes and it also contains Circles
  • Circles are represented as a geopandas.GeoDataFrame of shapely.Point with additional column for radius
  • The old Shapes element is removed
  • No additional metadata is introduced, only change from IO side is read/write for radius column in the case of Circles

@giovp

Copy link
Copy Markdown
MemberAuthor

tests in transforms are failing but I'd wait for #100 to be merged first before fixing, as there will be many changes to be made as well.

@giovpgiovp mentioned this pull request Feb 13, 2023
@LucaMarconato

LucaMarconato commented Feb 17, 2023

Copy link
Copy Markdown
Member

@giovp all tests are passing now. I did just fixes, no real change. Anyway if you want to review I suggest to diff
70d3f16 (only merged the branches and ran the pre-commits)
with the latest commit.

@codecov

codecovBot commented Feb 17, 2023

Copy link
Copy Markdown

Codecov Report

Merging #132 (3017790) into main (56179d6) will decrease coverage by 0.33%.
The diff coverage is 81.44%.

Additional details and impacted files
@@ Coverage Diff @@## main #132 +/- ##
==========================================
- Coverage 87.51% 87.19% -0.33% 
==========================================
Files 22 22 Lines 3108 3014 -94 ==========================================
- Hits 2720 2628 -92 + Misses 388 386 -2 
Impacted FilesCoverage Δ
spatialdata/__init__.py100.00% <ø> (ø)
spatialdata/_io/__init__.py100.00% <ø> (ø)
spatialdata/_core/_spatialdata.py75.68% <60.00%> (-1.17%)⬇️
spatialdata/_core/_spatial_query.py77.21% <62.50%> (ø)
spatialdata/_core/models.py85.83% <80.00%> (+0.04%)⬆️
spatialdata/_io/format.py88.23% <83.33%> (+0.60%)⬆️
spatialdata/_core/_transform_elements.py85.81% <100.00%> (-1.03%)⬇️
spatialdata/_core/core_utils.py92.39% <100.00%> (-2.93%)⬇️
spatialdata/_io/read.py97.46% <100.00%> (-0.20%)⬇️
spatialdata/_io/write.py96.05% <100.00%> (-0.23%)⬇️
... and 1 more

@kevinyamauchikevinyamauchi left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This LGTM! I think it makes sense to unify the Circles and polygons here. I left a couple of minor comments below.

Comment threadspatialdata/_io/format.py Outdated
Comment threadspatialdata/_core/models.py
Comment on lines +341 to +343
- 0: `Circles`
- 3: `Polygon`
- 6: `MultiPolygon`

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what are the 0, 3, and 6 here?

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thse are defined by GeometryType enum by shapely and are used to set the geometry type in the parser.

@giovp

Copy link
Copy Markdown
MemberAuthor

@LucaMarconato thanks a lot for fixing tests!!! I think we can go on and merge this, one comment:

  • I saw that pyarrow was re-added, do we need it? Think it's an artefact right?

@LucaMarconato

LucaMarconato commented Feb 20, 2023

Copy link
Copy Markdown
Member

Thanks for the PR 😊 we need it because it's used in the PR fixing the __repr__ for SpatialData (also, I noticed that we need it in SpatialData io for thr cosmx reader)

@giovp

giovp commented Feb 20, 2023

Copy link
Copy Markdown
MemberAuthor

Thanks for the PR 😊 we need it because it's used in the PR fixing the __repr__ for SpatialData (also, I noticed that we need it in SpatialData io for thr cosmx reader)

wait, if it's for the cosmx reader then it should be in spatialdata io. There is no other way for the repr to not use pyarrow?

EDIT: wasn't needed to I've removed it again

EDIT EDIT: I was wrong, but do we really want to keep the pyarrow.read_table just for the repr?

@giovp
giovp merged commit 2dfbe25 into mainFeb 20, 2023
@giovp
giovp deleted the models/shapes branch February 20, 2023 13:58
@LucaMarconato

Copy link
Copy Markdown
Member

@giovp yes for the moment better to keep it. Dask will allow to get the length of the dataframe soon (dask/dask#9973), at that point we can remove the dependency.

@ivirshup

Copy link
Copy Markdown
Member

@LucaMarconato I don't think that dask is going to get this soon. I don't see an active PR for it at the moment, and it's been open for a few years now.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@giovp@LucaMarconato@ivirshup@kevinyamauchi
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

unify polygons and shapes elements - #132

Merged
giovp merged 12 commits into
mainfrom
models/shapes
Feb 20, 2023
Merged

unify polygons and shapes elements#132
giovp merged 12 commits into
mainfrom
models/shapes

Conversation

@giovp

Copy link
Copy Markdown
Member

New Shapes model

This PR unifies polygons and shape elements in a single region element: Shapes.

Motivation

As of now, the Shapes element was stored as an AnnData, with a bunch of metadata to keep track of the type of shapes and size, and with the centroid coordinates store in in adata.obsm["spatial"]. Thinking about it more, it was very much unnecessary:

  • since it's a region element, only minimal amount of information should be stored there, as any annotations would go into Tables -> because of this, the AnnData format is unneccesary
  • it contained a lot of unnecessary metadata for determining which type of shapes are stored (circle, square only so far).
  • it could be simplified by representing it as a geopandas dataframe.

Solution

Since a geopandas.GeoDataFrame is probably the better way to represent Circles and Squares shapes, instead of AnnData, it naturally follows that polygons and shapes should be grouped together. Benefits are the following:

  • unified Shapes element (less complexity)
  • less metadata, offload to shapely handling of different geometry types
  • easier access to aggregation functions for Circles from geopandas.

e.g. example for counting points under circles regions @ivirshup

importgeopandasasgpdfromspatialdata._core.modelsimportShapesModelfromspatialdata._core.modelsimportPointsModelcircles=ShapesModel.parse(np.array([[10, 2], [5, 2]]), geometry=0, radius=[2, 3])
points=PointsModel.parse(
np.array([[11, 3], [5, 3]]), annotation=pd.DataFrame({"a": [0, 1]})
)
spot=circles["geometry"].buffer(distance=circles["radius"])[0]
gpd.points_from_xy(**points[["x", "y"]].compute()).within(spot)

Summary

Changes are minimal:

  • The Polygons element (and models, IO, etc.) it's now called Shapes and it also contains Circles
  • Circles are represented as a geopandas.GeoDataFrame of shapely.Point with additional column for radius
  • The old Shapes element is removed
  • No additional metadata is introduced, only change from IO side is read/write for radius column in the case of Circles

@giovp

Copy link
Copy Markdown
MemberAuthor

tests in transforms are failing but I'd wait for #100 to be merged first before fixing, as there will be many changes to be made as well.

@giovpgiovp mentioned this pull request Feb 13, 2023
@LucaMarconato

LucaMarconato commented Feb 17, 2023

Copy link
Copy Markdown
Member

@giovp all tests are passing now. I did just fixes, no real change. Anyway if you want to review I suggest to diff
70d3f16 (only merged the branches and ran the pre-commits)
with the latest commit.

@codecov

codecovBot commented Feb 17, 2023

Copy link
Copy Markdown

Codecov Report

Merging #132 (3017790) into main (56179d6) will decrease coverage by 0.33%.
The diff coverage is 81.44%.

Additional details and impacted files
@@ Coverage Diff @@## main #132 +/- ##
==========================================
- Coverage 87.51% 87.19% -0.33% 
==========================================
Files 22 22 Lines 3108 3014 -94 ==========================================
- Hits 2720 2628 -92 + Misses 388 386 -2 
Impacted FilesCoverage Δ
spatialdata/__init__.py100.00% <ø> (ø)
spatialdata/_io/__init__.py100.00% <ø> (ø)
spatialdata/_core/_spatialdata.py75.68% <60.00%> (-1.17%)⬇️
spatialdata/_core/_spatial_query.py77.21% <62.50%> (ø)
spatialdata/_core/models.py85.83% <80.00%> (+0.04%)⬆️
spatialdata/_io/format.py88.23% <83.33%> (+0.60%)⬆️
spatialdata/_core/_transform_elements.py85.81% <100.00%> (-1.03%)⬇️
spatialdata/_core/core_utils.py92.39% <100.00%> (-2.93%)⬇️
spatialdata/_io/read.py97.46% <100.00%> (-0.20%)⬇️
spatialdata/_io/write.py96.05% <100.00%> (-0.23%)⬇️
... and 1 more

@kevinyamauchikevinyamauchi left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This LGTM! I think it makes sense to unify the Circles and polygons here. I left a couple of minor comments below.

Comment threadspatialdata/_io/format.py Outdated
Comment threadspatialdata/_core/models.py
Comment on lines +341 to +343
- 0: `Circles`
- 3: `Polygon`
- 6: `MultiPolygon`

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what are the 0, 3, and 6 here?

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thse are defined by GeometryType enum by shapely and are used to set the geometry type in the parser.

@giovp

Copy link
Copy Markdown
MemberAuthor

@LucaMarconato thanks a lot for fixing tests!!! I think we can go on and merge this, one comment:

  • I saw that pyarrow was re-added, do we need it? Think it's an artefact right?

@LucaMarconato

LucaMarconato commented Feb 20, 2023

Copy link
Copy Markdown
Member

Thanks for the PR 😊 we need it because it's used in the PR fixing the __repr__ for SpatialData (also, I noticed that we need it in SpatialData io for thr cosmx reader)

@giovp

giovp commented Feb 20, 2023

Copy link
Copy Markdown
MemberAuthor

Thanks for the PR 😊 we need it because it's used in the PR fixing the __repr__ for SpatialData (also, I noticed that we need it in SpatialData io for thr cosmx reader)

wait, if it's for the cosmx reader then it should be in spatialdata io. There is no other way for the repr to not use pyarrow?

EDIT: wasn't needed to I've removed it again

EDIT EDIT: I was wrong, but do we really want to keep the pyarrow.read_table just for the repr?

@giovp
giovp merged commit 2dfbe25 into mainFeb 20, 2023
@giovp
giovp deleted the models/shapes branch February 20, 2023 13:58
@LucaMarconato

Copy link
Copy Markdown
Member

@giovp yes for the moment better to keep it. Dask will allow to get the length of the dataframe soon (dask/dask#9973), at that point we can remove the dependency.

@ivirshup

Copy link
Copy Markdown
Member

@LucaMarconato I don't think that dask is going to get this soon. I don't see an active PR for it at the moment, and it's been open for a few years now.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@giovp@LucaMarconato@ivirshup@kevinyamauchi