@@ -689,6 +689,55 @@ def _simple_new(
689
689
new ._dtype = dtype
690
690
return new
691
691
692
+ @classmethod
693
+ def from_spmatrix (cls , data ):
694
+ """
695
+ Create a SparseArray from a scipy.sparse matrix.
696
+
697
+ .. versionadded:: 0.25.0
698
+
699
+ Parameters
700
+ ----------
701
+ data : scipy.sparse.sp_matrix
702
+ This should be a SciPy sparse matrix where the size
703
+ of the second dimension is 1. In other words, a
704
+ sparse matrix with a single column.
705
+
706
+ Returns
707
+ -------
708
+ SparseArray
709
+
710
+ Examples
711
+ --------
712
+ >>> import scipy.sparse
713
+ >>> mat = scipy.sparse.coo_matrix((4, 1))
714
+ >>> pd.SparseArray.from_spmatrix(mat)
715
+ [0.0, 0.0, 0.0, 0.0]
716
+ Fill: 0.0
717
+ IntIndex
718
+ Indices: array([], dtype=int32)
719
+ """
720
+ length , ncol = data .shape
721
+
722
+ if ncol != 1 :
723
+ raise ValueError (
724
+ "'data' must have a single column, not '{}'" .format (ncol )
725
+ )
726
+
727
+ # our sparse index classes require that the positions be strictly
728
+ # increasing. So we need to sort loc, and arr accordingly.
729
+ arr = data .data
730
+ idx , _ = data .nonzero ()
731
+ loc = np .argsort (idx )
732
+ arr = arr .take (loc )
733
+ idx .sort ()
734
+
735
+ zero = np .array (0 , dtype = arr .dtype ).item ()
736
+ dtype = SparseDtype (arr .dtype , zero )
737
+ index = IntIndex (length , idx )
738
+
739
+ return cls ._simple_new (arr , index , dtype )
740
+
692
741
def __array__ (self , dtype = None , copy = True ):
693
742
fill_value = self .fill_value
694
743
@@ -1898,27 +1947,32 @@ def _make_index(length, indices, kind):
1898
1947
# ----------------------------------------------------------------------------
1899
1948
# Accessor
1900
1949
1950
+
1951
+ class BaseAccessor (object ):
1952
+ _validation_msg = "Can only use the '.sparse' accessor with Sparse data."
1953
+
1954
+ def __init__ (self , data = None ):
1955
+ self ._parent = data
1956
+ self ._validate (data )
1957
+
1958
+ def _validate (self , data ):
1959
+ raise NotImplementedError
1960
+
1961
+
1901
1962
@delegate_names (SparseArray , ['npoints' , 'density' , 'fill_value' ,
1902
1963
'sp_values' ],
1903
1964
typ = 'property' )
1904
- class SparseAccessor (PandasDelegate ):
1965
+ class SparseAccessor (BaseAccessor , PandasDelegate ):
1905
1966
"""
1906
1967
Accessor for SparseSparse from other sparse matrix data types.
1907
1968
"""
1908
1969
1909
- def __init__ (self , data = None ):
1910
- self ._validate (data )
1911
- # Store the Series since we need that for to_coo
1912
- self ._parent = data
1913
-
1914
- @staticmethod
1915
- def _validate (data ):
1970
+ def _validate (self , data ):
1916
1971
if not isinstance (data .dtype , SparseDtype ):
1917
- msg = "Can only use the '.sparse' accessor with Sparse data."
1918
- raise AttributeError (msg )
1972
+ raise AttributeError (self ._validation_msg )
1919
1973
1920
1974
def _delegate_property_get (self , name , * args , ** kwargs ):
1921
- return getattr (self ._parent .values , name )
1975
+ return getattr (self ._parent .array , name )
1922
1976
1923
1977
def _delegate_method (self , name , * args , ** kwargs ):
1924
1978
if name == 'from_coo' :
@@ -2032,3 +2086,188 @@ def to_coo(self, row_levels=(0, ), column_levels=(1, ), sort_labels=False):
2032
2086
column_levels ,
2033
2087
sort_labels = sort_labels )
2034
2088
return A , rows , columns
2089
+
2090
+ def to_dense (self ):
2091
+ """
2092
+ Convert a Series from sparse values to dense.
2093
+
2094
+ .. versionadded:: 0.25.0
2095
+
2096
+ Returns
2097
+ -------
2098
+ Series:
2099
+ A Series with the same values, stored as a dense array.
2100
+
2101
+ Examples
2102
+ --------
2103
+ >>> series = pd.Series(pd.SparseArray([0, 1, 0]))
2104
+ >>> series
2105
+ 0 0
2106
+ 1 1
2107
+ 2 0
2108
+ dtype: Sparse[int64, 0]
2109
+
2110
+ >>> series.sparse.to_dense()
2111
+ 0 0
2112
+ 1 1
2113
+ 2 0
2114
+ dtype: int64
2115
+ """
2116
+ from pandas import Series
2117
+ return Series (self ._parent .array .to_dense (),
2118
+ index = self ._parent .index ,
2119
+ name = self ._parent .name )
2120
+
2121
+
2122
+ class SparseFrameAccessor (BaseAccessor , PandasDelegate ):
2123
+ """
2124
+ DataFrame accessor for sparse data.
2125
+
2126
+ .. versionadded :: 0.25.0
2127
+ """
2128
+
2129
+ def _validate (self , data ):
2130
+ dtypes = data .dtypes
2131
+ if not all (isinstance (t , SparseDtype ) for t in dtypes ):
2132
+ raise AttributeError (self ._validation_msg )
2133
+
2134
+ @classmethod
2135
+ def from_spmatrix (cls , data , index = None , columns = None ):
2136
+ """
2137
+ Create a new DataFrame from a scipy sparse matrix.
2138
+
2139
+ .. versionadded:: 0.25.0
2140
+
2141
+ Parameters
2142
+ ----------
2143
+ data : scipy.sparse.spmatrix
2144
+ Must be convertible to csc format.
2145
+ index, columns : Index, optional
2146
+ Row and column labels to use for the resulting DataFrame.
2147
+ Defaults to a RangeIndex.
2148
+
2149
+ Returns
2150
+ -------
2151
+ DataFrame
2152
+ Each column of the DataFrame is stored as a
2153
+ :class:`SparseArray`.
2154
+
2155
+ Examples
2156
+ --------
2157
+ >>> import scipy.sparse
2158
+ >>> mat = scipy.sparse.eye(3)
2159
+ >>> pd.DataFrame.sparse.from_spmatrix(mat)
2160
+ 0 1 2
2161
+ 0 1.0 0.0 0.0
2162
+ 1 0.0 1.0 0.0
2163
+ 2 0.0 0.0 1.0
2164
+ """
2165
+ from pandas import DataFrame
2166
+
2167
+ data = data .tocsc ()
2168
+ index , columns = cls ._prep_index (data , index , columns )
2169
+ sparrays = [
2170
+ SparseArray .from_spmatrix (data [:, i ])
2171
+ for i in range (data .shape [1 ])
2172
+ ]
2173
+ data = dict (zip (columns , sparrays ))
2174
+ return DataFrame (data , index = index )
2175
+
2176
+ def to_dense (self ):
2177
+ """
2178
+ Convert a DataFrame with sparse values to dense.
2179
+
2180
+ .. versionadded:: 0.25.0
2181
+
2182
+ Returns
2183
+ -------
2184
+ DataFrame
2185
+ A DataFrame with the same values stored as dense arrays.
2186
+
2187
+ Examples
2188
+ --------
2189
+ >>> df = pd.DataFrame({"A": pd.SparseArray([0, 1, 0])})
2190
+ >>> df.sparse.to_dense()
2191
+ A
2192
+ 0 0
2193
+ 1 1
2194
+ 2 0
2195
+ """
2196
+ from pandas import DataFrame
2197
+
2198
+ data = {k : v .array .to_dense ()
2199
+ for k , v in compat .iteritems (self ._parent )}
2200
+ return DataFrame (data ,
2201
+ index = self ._parent .index ,
2202
+ columns = self ._parent .columns )
2203
+
2204
+ def to_coo (self ):
2205
+ """
2206
+ Return the contents of the frame as a sparse SciPy COO matrix.
2207
+
2208
+ .. versionadded:: 0.20.0
2209
+
2210
+ Returns
2211
+ -------
2212
+ coo_matrix : scipy.sparse.spmatrix
2213
+ If the caller is heterogeneous and contains booleans or objects,
2214
+ the result will be of dtype=object. See Notes.
2215
+
2216
+ Notes
2217
+ -----
2218
+ The dtype will be the lowest-common-denominator type (implicit
2219
+ upcasting); that is to say if the dtypes (even of numeric types)
2220
+ are mixed, the one that accommodates all will be chosen.
2221
+
2222
+ e.g. If the dtypes are float16 and float32, dtype will be upcast to
2223
+ float32. By numpy.find_common_type convention, mixing int64 and
2224
+ and uint64 will result in a float64 dtype.
2225
+ """
2226
+ try :
2227
+ from scipy .sparse import coo_matrix
2228
+ except ImportError :
2229
+ raise ImportError ('Scipy is not installed' )
2230
+
2231
+ dtype = find_common_type (self ._parent .dtypes )
2232
+ if isinstance (dtype , SparseDtype ):
2233
+ dtype = dtype .subtype
2234
+
2235
+ cols , rows , datas = [], [], []
2236
+ for col , name in enumerate (self ._parent ):
2237
+ s = self ._parent [name ]
2238
+ row = s .array .sp_index .to_int_index ().indices
2239
+ cols .append (np .repeat (col , len (row )))
2240
+ rows .append (row )
2241
+ datas .append (s .array .sp_values .astype (dtype , copy = False ))
2242
+
2243
+ cols = np .concatenate (cols )
2244
+ rows = np .concatenate (rows )
2245
+ datas = np .concatenate (datas )
2246
+ return coo_matrix ((datas , (rows , cols )), shape = self ._parent .shape )
2247
+
2248
+ @property
2249
+ def density (self ):
2250
+ """
2251
+ Ratio of non-sparse points to total (dense) data points
2252
+ represented in the DataFrame.
2253
+ """
2254
+ return np .mean ([column .array .density
2255
+ for _ , column in self ._parent .iteritems ()])
2256
+
2257
+ @staticmethod
2258
+ def _prep_index (data , index , columns ):
2259
+ import pandas .core .indexes .base as ibase
2260
+
2261
+ N , K = data .shape
2262
+ if index is None :
2263
+ index = ibase .default_index (N )
2264
+ if columns is None :
2265
+ columns = ibase .default_index (K )
2266
+
2267
+ if len (columns ) != K :
2268
+ raise ValueError ('Column length mismatch: {columns} vs. {K}'
2269
+ .format (columns = len (columns ), K = K ))
2270
+ if len (index ) != N :
2271
+ raise ValueError ('Index length mismatch: {index} vs. {N}'
2272
+ .format (index = len (index ), N = N ))
2273
+ return index , columns
0 commit comments