@@ -75,7 +75,6 @@ def map_interchange_dtype_to_narwhals_dtype(
75
75
76
76
class InterchangeFrame :
77
77
def __init__ (self , df : Any , dtypes : DTypes ) -> None :
78
- self ._native_frame = df
79
78
self ._interchange_frame = df .__dataframe__ ()
80
79
self ._dtypes = dtypes
81
80
@@ -97,21 +96,11 @@ def __getitem__(self, item: str) -> InterchangeSeries:
97
96
self ._interchange_frame .get_column_by_name (item ), dtypes = self ._dtypes
98
97
)
99
98
100
- @property
101
- def schema (self ) -> dict [str , DType ]:
102
- return {
103
- column_name : map_interchange_dtype_to_narwhals_dtype (
104
- self ._interchange_frame .get_column_by_name (column_name ).dtype ,
105
- self ._dtypes ,
106
- )
107
- for column_name in self ._interchange_frame .column_names ()
108
- }
109
-
110
99
def to_pandas (self : Self ) -> pd .DataFrame :
111
100
import pandas as pd # ignore-banned-import()
112
101
113
102
if parse_version (pd .__version__ ) >= parse_version ("1.5.0" ):
114
- return pd .api .interchange .from_dataframe (self ._native_frame )
103
+ return pd .api .interchange .from_dataframe (self ._interchange_frame )
115
104
else : # pragma: no cover
116
105
msg = (
117
106
"Conversion to pandas is achieved via interchange protocol which requires"
@@ -122,9 +111,19 @@ def to_pandas(self: Self) -> pd.DataFrame:
122
111
def to_arrow (self : Self ) -> pa .Table :
123
112
from pyarrow .interchange import from_dataframe # ignore-banned-import()
124
113
125
- return from_dataframe (self ._native_frame )
126
-
127
- def __getattr__ (self , attr : str ) -> NoReturn :
114
+ return from_dataframe (self ._interchange_frame )
115
+
116
+ def __getattr__ (self , attr : str ) -> Any :
117
+ if attr == "schema" :
118
+ return {
119
+ column_name : map_interchange_dtype_to_narwhals_dtype (
120
+ self ._interchange_frame .get_column_by_name (column_name ).dtype ,
121
+ self ._dtypes ,
122
+ )
123
+ for column_name in self ._interchange_frame .column_names ()
124
+ }
125
+ elif attr == "columns" :
126
+ return list (self ._interchange_frame .column_names ())
128
127
msg = (
129
128
f"Attribute { attr } is not supported for metadata-only dataframes.\n \n "
130
129
"Hint: you probably called `nw.from_native` on an object which isn't fully "
@@ -133,3 +132,26 @@ def __getattr__(self, attr: str) -> NoReturn:
133
132
"at https://github.com/narwhals-dev/narwhals/issues."
134
133
)
135
134
raise NotImplementedError (msg )
135
+
136
+ def select (
137
+ self : Self ,
138
+ * exprs : Any ,
139
+ ** named_exprs : Any ,
140
+ ) -> Self :
141
+ if named_exprs or not all (isinstance (x , str ) for x in exprs ): # pragma: no cover
142
+ msg = (
143
+ "`select`-ing not by name is not supported for interchange-only level.\n \n "
144
+ "If you would like to see this kind of object better supported in "
145
+ "Narwhals, please open a feature request "
146
+ "at https://github.com/narwhals-dev/narwhals/issues."
147
+ )
148
+ raise NotImplementedError (msg )
149
+
150
+ frame = self ._interchange_frame .select_columns_by_name (exprs )
151
+ if not hasattr (frame , "_df" ): # pragma: no cover
152
+ msg = (
153
+ "Expected interchange object to implement `_df` property to allow for recovering original object.\n "
154
+ "See https://github.com/data-apis/dataframe-api/issues/360."
155
+ )
156
+ raise NotImplementedError (frame )
157
+ return self .__class__ (frame ._df , dtypes = self ._dtypes )
0 commit comments