@@ -1463,7 +1463,11 @@ def obs_names_make_unique(self):
14631463 self .update_obs ()
14641464
14651465 for k in self .mod :
1466- self .mod [k ].obs_names_make_unique ()
1466+ if isinstance (self .mod [k ], AnnData ):
1467+ self .mod [k ].obs_names_make_unique ()
1468+ # Only propagate to individual modalities with shared vars
1469+ elif isinstance (self .mod [k ], MuData ) and getattr (self .mod [k ], 'axis' , 1 ) == 1 :
1470+ self .mod [k ].obs_names_make_unique ()
14671471
14681472 # Check if there are observations with the same name in different modalities
14691473 common_obs = []
@@ -1490,13 +1494,44 @@ def obs_names_make_unique(self):
14901494 def obs_names (self ) -> pd .Index :
14911495 """
14921496 Names of variables (alias for `.obs.index`)
1493-
1494- This property is read-only.
1495- To be modified, obs_names of individual modalities
1496- should be changed, and .update_obs() should be called then.
14971497 """
14981498 return self .obs .index
14991499
1500+ @obs_names .setter
1501+ def obs_names (self , names : Sequence [str ]):
1502+ """
1503+ Set the observation names for all the nested AnnData/MuData objects.
1504+ """
1505+ if isinstance (names , pd .Index ):
1506+ if not isinstance (names .name , str | type (None )):
1507+ raise ValueError (
1508+ f"MuData expects .obs.index.name to be a string or None, "
1509+ f"but you passed a name of type { type (names .name ).__name__ !r} "
1510+ )
1511+ else :
1512+ names = pd .Index (names )
1513+ if not isinstance (names .name , str | type (None )):
1514+ names .name = None
1515+
1516+ mod_obs_sum = np .sum ([a .n_obs for a in self .mod .values ()])
1517+ if mod_obs_sum != self .n_obs :
1518+ self .update_obs ()
1519+
1520+ if len (names ) != self .n_obs :
1521+ raise ValueError (
1522+ f"The length of provided observation names { len (names )} does not match the length { self .shape [0 ]} of MuData.obs."
1523+ )
1524+
1525+ if self .is_view :
1526+ self ._init_as_actual (self .copy ())
1527+
1528+ self ._obs .index = names
1529+ for mod , a in self .mod .items ():
1530+ indices = self .obsmap [mod ]
1531+ self .mod [mod ].obs_names = names [indices [indices != 0 ] - 1 ]
1532+
1533+ self .update_obs ()
1534+
15001535 @property
15011536 def var (self ) -> pd .DataFrame :
15021537 """
@@ -1565,7 +1600,11 @@ def var_names_make_unique(self):
15651600 self .update_var ()
15661601
15671602 for k in self .mod :
1568- self .mod [k ].var_names_make_unique ()
1603+ if isinstance (self .mod [k ], AnnData ):
1604+ self .mod [k ].var_names_make_unique ()
1605+ # Only propagate to individual modalities with shared obs
1606+ elif isinstance (self .mod [k ], MuData ) and getattr (self .mod [k ], 'axis' , 0 ) == 0 :
1607+ self .mod [k ].var_names_make_unique ()
15691608
15701609 # Check if there are variables with the same name in different modalities
15711610 common_vars = []
@@ -1592,13 +1631,44 @@ def var_names_make_unique(self):
15921631 def var_names (self ) -> pd .Index :
15931632 """
15941633 Names of variables (alias for `.var.index`)
1595-
1596- This property is read-only.
1597- To be modified, var_names of individual modalities
1598- should be changed, and .update_var() should be called then.
15991634 """
16001635 return self .var .index
16011636
1637+ @var_names .setter
1638+ def var_names (self , names : Sequence [str ]):
1639+ """
1640+ Set the variable names for all the nested AnnData/MuData objects.
1641+ """
1642+ if isinstance (names , pd .Index ):
1643+ if not isinstance (names .name , str | type (None )):
1644+ raise ValueError (
1645+ f"MuData expects .var.index.name to be a string or None, "
1646+ f"but you passed a name of type { type (names .name ).__name__ !r} "
1647+ )
1648+ else :
1649+ names = pd .Index (names )
1650+ if not isinstance (names .name , str | type (None )):
1651+ names .name = None
1652+
1653+ mod_var_sum = np .sum ([a .n_vars for a in self .mod .values ()])
1654+ if mod_var_sum != self .n_vars :
1655+ self .update_var ()
1656+
1657+ if len (names ) != self .n_vars :
1658+ raise ValueError (
1659+ f"The length of provided variable names { len (names )} does not match the length { self .shape [0 ]} of MuData.var."
1660+ )
1661+
1662+ if self .is_view :
1663+ self ._init_as_actual (self .copy ())
1664+
1665+ self ._var .index = names
1666+ for mod , a in self .mod .items ():
1667+ indices = self .varmap [mod ]
1668+ self .mod [mod ].var_names = names [indices [indices != 0 ] - 1 ]
1669+
1670+ self .update_var ()
1671+
16021672 # Multi-dimensional annotations (.obsm and .varm)
16031673
16041674 @property
0 commit comments