Repository: scikit-hep/hist (python)

Issue:
[FEATURE] .T shortcut for .project(1,0) of a 2D hist
I was trying to flip x/y axes of a 2d hist and was pointed to #133 after initially trying some guesswork. One of the things I reached for was  a `.T` method which incidentally is also mentioned in #133. Is that perhaps enough of a suggestion that this would be a good API shortcut to have?

Pull request description:
feat: add a .T shortcut
Closes #513.


Patch:
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 97bc31b..b1f5345 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -3,7 +3,7 @@ ci:
   autofix_commit_msg: "style: pre-commit fixes"
 
 repos:
-- repo: https://github.com/psf/black
+- repo: https://github.com/psf/black-pre-commit-mirror
   rev: 23.7.0
   hooks:
   - id: black-jupyter
@@ -29,7 +29,7 @@ repos:
   - id: trailing-whitespace
 
 - repo: https://github.com/astral-sh/ruff-pre-commit
-  rev: v0.0.281
+  rev: v0.0.282
   hooks:
   - id: ruff
     args: ["--fix", "--show-fixes"]
diff --git a/src/hist/basehist.py b/src/hist/basehist.py
index cc008b7..de19f9e 100644
--- a/src/hist/basehist.py
+++ b/src/hist/basehist.py
@@ -204,6 +204,10 @@ class BaseHist(bh.Histogram, metaclass=MetaConstructor, family=hist):
         int_args = [self._name_to_index(a) if isinstance(a, str) else a for a in args]
         return super().project(*int_args)
 
+    @property
+    def T(self) -> Self:
+        return self.project(*reversed(range(self.ndim)))  # type: ignore[return-value]
+
     def fill(
         self,
         *args: ArrayLike,


Test patch:
diff --git a/tests/test_general.py b/tests/test_general.py
index 092a326..ecce322 100644
--- a/tests/test_general.py
+++ b/tests/test_general.py
@@ -953,3 +953,48 @@ def test_integrate():
     assert h1[{"x": 4j}] == 2
     assert h1[{"x": 2j}] == 1
     assert h2[{"x": 1j}] == 3
+
+
+def test_T_property():
+    # Create a 2D histogram with some data
+    hist_data = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
+    h = hist.Hist(
+        hist.axis.Regular(3, 0, 1, flow=False),
+        hist.axis.Regular(3, 5, 6, flow=False),
+        data=hist_data,
+    )
+
+    assert h.T.values() == approx(h.values().T)
+    assert h.T.axes[0] == h.axes[1]
+    assert h.T.axes[1] == h.axes[0]
+
+
+def test_T_empty():
+    hist_empty = hist.Hist()
+    hist_T_empty = hist_empty.T
+    assert hist_empty == hist_T_empty
+
+
+def test_T_1D():
+    # Create a 1D histogram with some data
+    hist_data_1D = np.array([1, 2, 3, 4, 5])
+    h_1D = hist.Hist(hist.axis.Regular(5, 0, 1, flow=False), data=hist_data_1D)
+
+    assert h_1D.T.values() == approx(h_1D.values().T)
+    assert h_1D.T.axes[0] == h_1D.axes[0]
+
+
+def test_T_3D():
+    # Create a 3D histogram with some data
+    hist_data_3D = np.random.rand(1, 3, 4)
+    h_3D = hist.Hist(
+        hist.axis.Regular(2, 0, 1, flow=False),
+        hist.axis.Regular(3, 2, 3, flow=False),
+        hist.axis.Regular(4, 5, 6, flow=False),
+        data=hist_data_3D,
+    )
+
+    assert h_3D.T.values() == approx(h_3D.values().T)
+    assert h_3D.T.axes[0] == h_3D.axes[2]
+    assert h_3D.T.axes[1] == h_3D.axes[1]
+    assert h_3D.T.axes[2] == h_3D.axes[0]