Repository: alteryx/woodwork (python)

Issue:
Remove dtype from column dictionary
When Woodwork is initialized, the Schema object creates data dictionaries for each column and stores these in the Schema object. These column dictionaries include the dtype of the column, which is currently set to the value of the `LogicalType.pandas_dtype` on init. This is problematic for a few reasons:
- We have two sources of information for the dtype of the columns (the series dtype and the dtype stored in the schema)
- For Koalas dataframes, the dtype of the column might be changed to the value of the `LogicalType.backup_dtype` instead of `LogicalType.pandas_dtype` and as a result the value in the column dict could be incorrect
- When we initialize a new column using the column dictionary we need to drop the dtype from the dictionary as dtype is not a valid init parameter

We could drop the dtype from the column dictionary completely and instead pull the dtype from the series directly when it is needed. After init the dtype of the series should be set to the correct value - either `LogicalType.pandas_dtype` or `LogicalType.backup_dtype`. (Note one potential complication here is that the backup dtype for string is `str`. Running `.astype('str') results in an object dtype of `object` for a Koalas Series).

If we drop the dtype from the column dictionary, we would also need to update the Schmea and accessor repr methods appropriately - the schema has no access to the dataframe, so we would either need to display the pandas_dtype or backup_dtype values, or not display the dtype in the Schema repr and only include those in the accessor repr. Also, we would need to move the `physical_types` property from the Schema to the accessor and update the code accordingly.

If we make this change, we should also review the code when initializing a table with an existing schema to make sure the proper validation occurs to prevent initialization with dtypes that are incompatible between the series and the LogicalType, or to perform the proper type conversion during init if differences are detected.

Pull request:
Remove dtype from Schema dictionary
- Removes the `dtype` field from the Schema columns dictionary, relying on Series dtype when checking the dtype of a column.
- Adds a separate repr for Schema and Table Accessor since only the Table Accessor has access to physical types
- Closes #653 

Patch:
diff --git a/docs/source/api_reference.rst b/docs/source/api_reference.rst
index 9438175..1dff1a6 100644
--- a/docs/source/api_reference.rst
+++ b/docs/source/api_reference.rst
@@ -72,7 +72,6 @@ Schema
     Schema.add_semantic_tags
     Schema.index
     Schema.logical_types
-    Schema.physical_types
     Schema.rename
     Schema.remove_semantic_tags
     Schema.reset_semantic_tags
diff --git a/docs/source/release_notes.rst b/docs/source/release_notes.rst
index 6559086..4340886 100644
--- a/docs/source/release_notes.rst
+++ b/docs/source/release_notes.rst
@@ -51,6 +51,8 @@ Release Notes
         * Preserve pandas underlying index when not creating a Woodwork index (:pr:`664`)
         * Restrict Koalas version to ``<1.7.0`` due to breaking changes (:pr:`674`)
         * Clean up dtype usage across Woodwork (:pr:`682`)
+        * Remove dtype from Schema dictionary (:pr:`685`)
+
     * Documentation Changes
         * Update docstrings and API Reference page (:pr:`660`)
     * Testing Changes
diff --git a/woodwork/accessor_utils.py b/woodwork/accessor_utils.py
index 4ec8241..8ddb1ec 100644
--- a/woodwork/accessor_utils.py
+++ b/woodwork/accessor_utils.py
@@ -65,8 +65,8 @@ def _update_column_dtype(series, logical_type):
             series = ks.from_pandas(formatted_series)
         else:
             series = series.apply(_reformat_to_latlong)
-    if logical_type.primary_dtype != str(series.dtype):
-        new_dtype = _get_valid_dtype(type(series), logical_type)
+    new_dtype = _get_valid_dtype(type(series), logical_type)
+    if new_dtype != str(series.dtype):
         # Update the underlying series
         try:
             if _get_ltype_class(logical_type) == Datetime:
diff --git a/woodwork/column_accessor.py b/woodwork/column_accessor.py
index 17a437d..9c049d1 100644
--- a/woodwork/column_accessor.py
+++ b/woodwork/column_accessor.py
@@ -195,8 +195,6 @@ class WoodworkColumnAccessor:
                     valid_dtype = _get_valid_dtype(type(result), self._schema['logical_type'])
                     if str(result.dtype) == valid_dtype:
                         schema = copy.deepcopy(self._schema)
-                        # We don't need to pass dtype from the schema to init
-                        del schema['dtype']
                         result.ww.init(**schema, use_standard_tags=self.use_standard_tags)
                     else:
                         invalid_schema_message = 'dtype mismatch between original dtype, ' \
diff --git a/woodwork/indexers.py b/woodwork/indexers.py
index 33c1db9..46de813 100644
--- a/woodwork/indexers.py
+++ b/woodwork/indexers.py
@@ -90,7 +90,6 @@ def _process_selection(selection, original_data):
             # Selecting a new Series from an existing Series
             schema = copy.deepcopy(original_data.ww._schema)
         if schema:
-            del schema['dtype']
             selection.ww.init(**schema, use_standard_tags=original_data.ww.use_standard_tags)
     elif _is_dataframe(selection):
         # Selecting a new DataFrame from an existing DataFrame
diff --git a/woodwork/schema.py b/woodwork/schema.py
index b2b5990..4f70b3c 100644
--- a/woodwork/schema.py
+++ b/woodwork/schema.py
@@ -109,10 +109,10 @@ class Schema(object):
         typing_info = {}
         for col_name, col_dict in self.columns.items():
 
-            types = [col_dict['dtype'], col_dict['logical_type'], str(list(col_dict['semantic_tags']))]
+            types = [col_dict['logical_type'], str(list(col_dict['semantic_tags']))]
             typing_info[col_name] = types
 
-        columns = ['Physical Type', 'Logical Type', 'Semantic Tag(s)']
+        columns = ['Logical Type', 'Semantic Tag(s)']
 
         df = pd.DataFrame.from_dict(typing_info,
                                     orient='index',
@@ -126,11 +126,6 @@ class Schema(object):
         """A dictionary containing logical types for each column"""
         return {col_name: col['logical_type'] for col_name, col in self.columns.items()}
 
-    @property
-    def physical_types(self):
-        """A dictionary containing physical types for each column"""
-        return {col_name: col['dtype'] for col_name, col in self.columns.items()}
-
     @property
     def semantic_tags(self):
         """A dictionary containing semantic tags for each column"""
diff --git a/woodwork/schema_column.py b/woodwork/schema_column.py
index c88d0a5..41f693d 100644
--- a/woodwork/schema_column.py
+++ b/woodwork/schema_column.py
@@ -36,7 +36,6 @@ def _get_column_dict(name,
     semantic_tags = _get_column_tags(semantic_tags, logical_type, use_standard_tags, name)
 
     return {
-        'dtype': logical_type.primary_dtype,
         'logical_type': logical_type,
         'semantic_tags': semantic_tags,
         'description': description,
diff --git a/woodwork/statistics_utils.py b/woodwork/statistics_utils.py
index 1cb83aa..81a12e0 100644
--- a/woodwork/statistics_utils.py
+++ b/woodwork/statistics_utils.py
@@ -93,7 +93,7 @@ def _get_describe_dict(dataframe, include=None):
 
         values["nan_count"] = series.isna().sum()
         values["mode"] = mode
-        values["physical_type"] = column['dtype']
+        values["physical_type"] = series.dtype
         values["logical_type"] = logical_type
         values["semantic_tags"] = semantic_tags
         results[column_name] = values
diff --git a/woodwork/table_accessor.py b/woodwork/table_accessor.py
index b299c49..9168f61 100644
--- a/woodwork/table_accessor.py
+++ b/woodwork/table_accessor.py
@@ -163,13 +163,28 @@ class WoodworkTableAccessor:
 
         series = self._dataframe[key]
         column = self.schema.columns[key]
-        del column['dtype']
         column['semantic_tags'] -= {'index', 'time_index'}
         series.ww.init(**column, use_standard_tags=self.use_standard_tags)
         return series
 
     def __repr__(self):
-        return repr(self.types)
+        """A string representation of a Woodwork table containing typing information"""
+        return repr(self._get_typing_info())
+
+    def _repr_html_(self):
+        """An HTML representation of a Woodwork table for IPython.display in Jupyter Notebooks
+        containing typing information and a preview of the data."""
+        return self._get_typing_info().to_html()
+
+    def _get_typing_info(self):
+        """Creates a DataFrame that contains the typing information for a Woodwork table."""
+        if self._schema is None:
+            _raise_init_error()
+        typing_info = self._schema._get_typing_info().copy()
+        typing_info.insert(0, 'Physical Type', pd.Series(self.physical_types))
+        # Maintain the same column order used in the DataFrame
+        typing_info = typing_info.loc[list(self._dataframe.columns), :]
+        return typing_info
 
     @property
     def iloc(self):
@@ -230,26 +245,22 @@ class WoodworkTableAccessor:
         if self._schema:
             return self._schema._get_subset_schema(list(self._dataframe.columns))
 
+    @property
+    def physical_types(self):
+        """A dictionary containing physical types for each column"""
+        return {col_name: _get_valid_dtype(type(self._dataframe[col_name]), self.schema.logical_types[col_name]) for col_name in self._dataframe.columns}
+
     @property
     def types(self):
         """DataFrame containing the physical dtypes, logical types and semantic
         tags for the Schema."""
-        repr_df = self._schema._get_typing_info()
-        # Maintain the same column order used in the DataFrame
-        repr_df = repr_df.loc[list(self._dataframe.columns), :]
-
-        return repr_df
+        return self._get_typing_info()
 
     @property
     def logical_types(self):
         """A dictionary containing logical types for each column"""
         return self._schema.logical_types
 
-    @property
-    def physical_types(self):
-        """A dictionary containing physical types for each column"""
-        return self._schema.physical_types
-
     @property
     def semantic_tags(self):
         """A dictionary containing semantic tags for each column"""
@@ -546,7 +557,6 @@ class WoodworkTableAccessor:
 
         # Initialize Woodwork typing info for series
         col_schema = self._schema.columns[column_name]
-        del col_schema['dtype']
         series.ww.init(**col_schema, use_standard_tags=self.use_standard_tags)
 
         # Update schema to not include popped column


Test patch:
diff --git a/woodwork/tests/accessor/test_table_accessor.py b/woodwork/tests/accessor/test_table_accessor.py
index 03f50bf..2b65442 100644
--- a/woodwork/tests/accessor/test_table_accessor.py
+++ b/woodwork/tests/accessor/test_table_accessor.py
@@ -112,6 +112,19 @@ def test_accessor_schema_property(sample_df):
     assert sample_df.ww._schema == sample_df.ww.schema
 
 
+def test_accessor_physical_types_property(sample_df):
+    sample_df.ww.init(logical_types={'age': 'Categorical'})
+
+    assert isinstance(sample_df.ww.physical_types, dict)
+    assert set(sample_df.ww.physical_types.keys()) == set(sample_df.columns)
+    for k, v in sample_df.ww.physical_types.items():
+        logical_type = sample_df.ww.columns[k]['logical_type']
+        if ks and isinstance(sample_df, ks.DataFrame) and logical_type.backup_dtype is not None:
+            assert v == logical_type.backup_dtype
+        else:
+            assert v == logical_type.primary_dtype
+
+
 def test_accessor_separation_of_params(sample_df):
     # mix up order of acccessor and schema params
     schema_df = sample_df.copy()
@@ -271,10 +284,6 @@ def test_accessor_equality_with_schema(sample_df, sample_column_names, sample_in
     # eq not implemented on Accessor class, so Schema's eq is called
     assert schema_df.ww.__eq__(comparison_schema)
 
-    # Since there's a repr on Accessor, it gets called
-    assert schema_df.ww._repr_html_() == comparison_schema._repr_html_()
-    assert schema_df.ww.__repr__() == comparison_schema.__repr__()
-
     logical_types = {
         'id': Double,
         'full_name': FullName
@@ -514,7 +523,6 @@ def test_sets_category_dtype_on_init():
             df = pd.DataFrame(series)
             df.ww.init(logical_types=ltypes)
             assert df.ww.columns[column_name]['logical_type'] == logical_type
-            assert df.ww.columns[column_name]['dtype'] == logical_type.primary_dtype
             assert df[column_name].dtype == logical_type.primary_dtype
 
 
@@ -526,7 +534,6 @@ def test_sets_object_dtype_on_init(latlong_df):
         df = latlong_df.loc[:, [column_name]]
         df.ww.init(logical_types=ltypes)
         assert df.ww.columns[column_name]['logical_type'] == LatLong
-        assert df.ww.columns[column_name]['dtype'] == LatLong.primary_dtype
         assert df[column_name].dtype == LatLong.primary_dtype
         df_pandas = to_pandas(df[column_name])
         expected_val = (3, 4)
@@ -562,7 +569,6 @@ def test_sets_string_dtype_on_init():
             df = pd.DataFrame(series)
             df.ww.init(logical_types=ltypes)
             assert df.ww.columns[column_name]['logical_type'] == logical_type
-            assert df.ww.columns[column_name]['dtype'] == logical_type.primary_dtype
             assert df[column_name].dtype == logical_type.primary_dtype
 
 
@@ -584,7 +590,6 @@ def test_sets_boolean_dtype_on_init():
         df = pd.DataFrame(series)
         df.ww.init(logical_types=ltypes)
         assert df.ww.columns[column_name]['logical_type'] == logical_type
-        assert df.ww.columns[column_name]['dtype'] == logical_type.primary_dtype
         assert df[column_name].dtype == logical_type.primary_dtype
 
 
@@ -607,7 +612,6 @@ def test_sets_int64_dtype_on_init():
             df = pd.DataFrame(series)
             df.ww.init(logical_types=ltypes)
             assert df.ww.columns[column_name]['logical_type'] == logical_type
-            assert df.ww.columns[column_name]['dtype'] == logical_type.primary_dtype
             assert df[column_name].dtype == logical_type.primary_dtype
 
 
@@ -628,7 +632,6 @@ def test_sets_float64_dtype_on_init():
         df = pd.DataFrame(series)
         df.ww.init(logical_types=ltypes)
         assert df.ww.columns[column_name]['logical_type'] == logical_type
-        assert df.ww.columns[column_name]['dtype'] == logical_type.primary_dtype
         assert df[column_name].dtype == logical_type.primary_dtype
 
 
@@ -651,7 +654,6 @@ def test_sets_datetime64_dtype_on_init():
         df = pd.DataFrame(series)
         df.ww.init(logical_types=ltypes)
         assert df.ww.columns[column_name]['logical_type'] == logical_type
-        assert df.ww.columns[column_name]['dtype'] == logical_type.primary_dtype
         assert df[column_name].dtype == logical_type.primary_dtype
 
 
@@ -969,13 +971,6 @@ def test_accessor_with_falsy_column_names(falsy_names_df):
     assert 'col_with_name' in renamed_df.columns
 
 
-def test_accessor_repr(sample_df, sample_column_names, sample_inferred_logical_types):
-    schema = Schema(sample_column_names, sample_inferred_logical_types)
-    sample_df.ww.init()
-
-    assert repr(schema) == repr(sample_df.ww)
-
-
 def test_get_invalid_schema_message(sample_df):
     schema_df = sample_df.copy()
     schema_df.ww.init(name='test_schema', index='id', logical_types={'id': 'Double', 'full_name': 'FullName'})
@@ -1146,6 +1141,22 @@ def test_dataframe_methods_on_accessor_other_returns(sample_df):
         pd.testing.assert_index_equal(schema_df.ww.keys(), schema_df.keys())
 
 
+def test_dataframe_methods_on_accessor_to_pandas(sample_df):
+    if isinstance(sample_df, pd.DataFrame):
+        pytest.skip("No need to test converting pandas DataFrame to pandas")
+
+    sample_df.ww.init(name='woodwork', index='id')
+
+    if dd and isinstance(sample_df, dd.DataFrame):
+        pd_df = sample_df.ww.compute()
+    elif ks and isinstance(sample_df, ks.DataFrame):
+        pd_df = sample_df.ww.to_pandas()
+
+    assert isinstance(pd_df, pd.DataFrame)
+    assert pd_df.ww.index == 'id'
+    assert pd_df.ww.name == 'woodwork'
+
+
 def test_get_subset_df_with_schema(sample_df):
     schema_df = sample_df.copy()
     schema_df.ww.init(time_index='signup_date',
@@ -1684,7 +1695,6 @@ def test_accessor_rename(sample_df):
     new_col = new_df.ww.columns['birthday']
     assert old_col['logical_type'] == new_col['logical_type']
     assert old_col['semantic_tags'] == new_col['semantic_tags']
-    assert old_col['dtype'] == new_col['dtype']
 
     new_df = sample_df.ww.rename({'age': 'full_name', 'full_name': 'age'})
 
@@ -1722,15 +1732,12 @@ def test_accessor_schema_properties(sample_df):
     sample_df.ww.init(index='id',
                       time_index='signup_date')
 
-    schema_properties = ['types', 'logical_types', 'physical_types', 'semantic_tags', 'index', 'time_index']
+    schema_properties = ['logical_types', 'semantic_tags', 'index', 'time_index']
     for schema_property in schema_properties:
         prop_from_accessor = getattr(sample_df.ww, schema_property)
         prop_from_schema = getattr(sample_df.ww.schema, schema_property)
 
-        if schema_property == 'types':
-            pd.testing.assert_frame_equal(prop_from_accessor, prop_from_schema)
-        else:
-            assert prop_from_accessor == prop_from_schema
+        assert prop_from_accessor == prop_from_schema
 
         # Assumes we don't have setters for any of these attributes
         error = "can't set attribute"
@@ -1814,3 +1821,78 @@ def test_maintain_column_order_disordered_schema(sample_df):
     sample_df.ww.init(schema=scramble_df.ww.schema)
     assert all(sample_df.columns == column_order)
     assert all(sample_df.ww.types.index == column_order)
+
+
+def test_accessor_types(sample_df):
+    sample_df.ww.init()
+
+    returned_types = sample_df.ww.types
+    assert isinstance(returned_types, pd.DataFrame)
+    assert all(returned_types.columns == ['Physical Type', 'Logical Type', 'Semantic Tag(s)'])
+    assert returned_types.shape[1] == 3
+    assert len(returned_types.index) == len(sample_df.columns)
+
+    string_dtype = 'string'
+    boolean_dtype = 'boolean'
+    int_dtype = 'Int64'
+
+    correct_physical_types = {
+        'id': int_dtype,
+        'full_name': string_dtype,
+        'email': string_dtype,
+        'phone_number': string_dtype,
+        'age': int_dtype,
+        'signup_date': 'datetime64[ns]',
+        'is_registered': boolean_dtype,
+    }
+    correct_physical_types = pd.Series(list(correct_physical_types.values()),
+                                       index=list(correct_physical_types.keys()))
+    assert correct_physical_types.equals(returned_types['Physical Type'])
+
+    correct_logical_types = {
+        'id': Integer,
+        'full_name': NaturalLanguage,
+        'email': NaturalLanguage,
+        'phone_number': NaturalLanguage,
+        'age': Integer,
+        'signup_date': Datetime,
+        'is_registered': Boolean,
+    }
+    correct_logical_types = pd.Series(list(correct_logical_types.values()),
+                                      index=list(correct_logical_types.keys()))
+    assert correct_logical_types.equals(returned_types['Logical Type'])
+
+    correct_semantic_tags = {
+        'id': "['numeric']",
+        'full_name': "[]",
+        'email': "[]",
+        'phone_number': "[]",
+        'age': "['numeric']",
+        'signup_date': "[]",
+        'is_registered': "[]",
+    }
+    correct_semantic_tags = pd.Series(list(correct_semantic_tags.values()),
+                                      index=list(correct_semantic_tags.keys()))
+    assert correct_semantic_tags.equals(returned_types['Semantic Tag(s)'])
+
+
+def test_accessor_repr(small_df):
+    error = 'Woodwork not initialized for this DataFrame. Initialize by calling DataFrame.ww.init'
+    with pytest.raises(AttributeError, match=error):
+        repr(small_df.ww)
+    small_df.ww.init()
+
+    table_repr = repr(small_df.ww)
+    expected_repr = '                         Physical Type Logical Type Semantic Tag(s)\nColumn                                                             \nsample_datetime_series  datetime64[ns]     Datetime              []'
+    assert table_repr == expected_repr
+
+    table_html_repr = small_df.ww._repr_html_()
+    expected_repr = '<table border="1" class="dataframe">\n  <thead>\n    <tr style="text-align: right;">\n      <th></th>\n      <th>Physical Type</th>\n      <th>Logical Type</th>\n      <th>Semantic Tag(s)</th>\n    </tr>\n    <tr>\n      <th>Column</th>\n      <th></th>\n      <th></th>\n      <th></th>\n    </tr>\n  </thead>\n  <tbody>\n    <tr>\n      <th>sample_datetime_series</th>\n      <td>datetime64[ns]</td>\n      <td>Datetime</td>\n      <td>[]</td>\n    </tr>\n  </tbody>\n</table>'
+    assert table_html_repr == expected_repr
+
+
+def test_accessor_repr_empty(empty_df):
+    empty_df.ww.init()
+
+    assert repr(empty_df.ww) == 'Empty DataFrame\nColumns: [Physical Type, Logical Type, Semantic Tag(s)]\nIndex: []'
+    assert empty_df.ww._repr_html_() == '<table border="1" class="dataframe">\n  <thead>\n    <tr style="text-align: right;">\n      <th></th>\n      <th>Physical Type</th>\n      <th>Logical Type</th>\n      <th>Semantic Tag(s)</th>\n    </tr>\n    <tr>\n      <th>Column</th>\n      <th></th>\n      <th></th>\n      <th></th>\n    </tr>\n  </thead>\n  <tbody>\n  </tbody>\n</table>'
diff --git a/woodwork/tests/schema/test_schema.py b/woodwork/tests/schema/test_schema.py
index df650a6..7a7d1a9 100644
--- a/woodwork/tests/schema/test_schema.py
+++ b/woodwork/tests/schema/test_schema.py
@@ -20,14 +20,6 @@ from woodwork.logical_types import (
 from woodwork.schema import Schema
 
 
-def test_schema_physical_types(sample_column_names, sample_inferred_logical_types):
-    schema = Schema(sample_column_names, sample_inferred_logical_types)
-    assert isinstance(schema.physical_types, dict)
-    assert set(schema.physical_types.keys()) == set(sample_column_names)
-    for k, v in schema.physical_types.items():
-        assert v == schema.columns[k]['logical_type'].primary_dtype
-
-
 def test_schema_logical_types(sample_column_names, sample_inferred_logical_types):
     schema = Schema(sample_column_names, sample_inferred_logical_types)
     assert isinstance(schema.logical_types, dict)
@@ -58,10 +50,9 @@ def test_schema_types(sample_column_names, sample_inferred_logical_types):
 
     returned_types = schema.types
     assert isinstance(returned_types, pd.DataFrame)
-    assert 'Physical Type' in returned_types.columns
     assert 'Logical Type' in returned_types.columns
     assert 'Semantic Tag(s)' in returned_types.columns
-    assert returned_types.shape[1] == 3
+    assert returned_types.shape[1] == 2
     assert len(returned_types.index) == len(sample_column_names)
     correct_logical_types = {
         'id': Integer,
@@ -96,19 +87,19 @@ def test_schema_repr(small_df):
     schema = Schema(list(small_df.columns), logical_types={'sample_datetime_series': Datetime})
 
     schema_repr = repr(schema)
-    expected_repr = '                         Physical Type Logical Type Semantic Tag(s)\nColumn                                                             \nsample_datetime_series  datetime64[ns]     Datetime              []'
+    expected_repr = '                       Logical Type Semantic Tag(s)\nColumn                                             \nsample_datetime_series     Datetime              []'
     assert schema_repr == expected_repr
 
     schema_html_repr = schema._repr_html_()
-    expected_repr = '<table border="1" class="dataframe">\n  <thead>\n    <tr style="text-align: right;">\n      <th></th>\n      <th>Physical Type</th>\n      <th>Logical Type</th>\n      <th>Semantic Tag(s)</th>\n    </tr>\n    <tr>\n      <th>Column</th>\n      <th></th>\n      <th></th>\n      <th></th>\n    </tr>\n  </thead>\n  <tbody>\n    <tr>\n      <th>sample_datetime_series</th>\n      <td>datetime64[ns]</td>\n      <td>Datetime</td>\n      <td>[]</td>\n    </tr>\n  </tbody>\n</table>'
+    expected_repr = '<table border="1" class="dataframe">\n  <thead>\n    <tr style="text-align: right;">\n      <th></th>\n      <th>Logical Type</th>\n      <th>Semantic Tag(s)</th>\n    </tr>\n    <tr>\n      <th>Column</th>\n      <th></th>\n      <th></th>\n    </tr>\n  </thead>\n  <tbody>\n    <tr>\n      <th>sample_datetime_series</th>\n      <td>Datetime</td>\n      <td>[]</td>\n    </tr>\n  </tbody>\n</table>'
     assert schema_html_repr == expected_repr
 
 
 def test_schema_repr_empty():
     schema = Schema([], {})
-    assert repr(schema) == 'Empty DataFrame\nColumns: [Physical Type, Logical Type, Semantic Tag(s)]\nIndex: []'
+    assert repr(schema) == 'Empty DataFrame\nColumns: [Logical Type, Semantic Tag(s)]\nIndex: []'
 
-    assert schema._repr_html_() == '<table border="1" class="dataframe">\n  <thead>\n    <tr style="text-align: right;">\n      <th></th>\n      <th>Physical Type</th>\n      <th>Logical Type</th>\n      <th>Semantic Tag(s)</th>\n    </tr>\n    <tr>\n      <th>Column</th>\n      <th></th>\n      <th></th>\n      <th></th>\n    </tr>\n  </thead>\n  <tbody>\n  </tbody>\n</table>'
+    assert schema._repr_html_() == '<table border="1" class="dataframe">\n  <thead>\n    <tr style="text-align: right;">\n      <th></th>\n      <th>Logical Type</th>\n      <th>Semantic Tag(s)</th>\n    </tr>\n    <tr>\n      <th>Column</th>\n      <th></th>\n      <th></th>\n    </tr>\n  </thead>\n  <tbody>\n  </tbody>\n</table>'
 
 
 def test_schema_equality(sample_column_names, sample_inferred_logical_types):
@@ -836,7 +827,6 @@ def test_schema_rename(sample_column_names, sample_inferred_logical_types):
     new_col = renamed_schema.columns['birthday']
     assert old_col['logical_type'] == new_col['logical_type']
     assert old_col['semantic_tags'] == new_col['semantic_tags']
-    assert old_col['dtype'] == new_col['dtype']
 
     swapped_schema = schema.rename({'age': 'full_name', 'full_name': 'age'})
     swapped_back_schema = swapped_schema.rename({'age': 'full_name', 'full_name': 'age'})
diff --git a/woodwork/tests/schema/test_schema_column.py b/woodwork/tests/schema/test_schema_column.py
index d39661a..709cb24 100644
--- a/woodwork/tests/schema/test_schema_column.py
+++ b/woodwork/tests/schema/test_schema_column.py
@@ -60,10 +60,9 @@ def test_validate_metadata_errors():
 def test_get_column_dict():
     column = _get_column_dict('column', Integer, semantic_tags='test_tag')
 
-    assert set(column.keys()) == {'dtype', 'logical_type', 'semantic_tags', 'description', 'metadata'}
+    assert set(column.keys()) == {'logical_type', 'semantic_tags', 'description', 'metadata'}
 
     assert column.get('logical_type') == Integer
-    assert column.get('dtype') == 'Int64'
     assert column.get('semantic_tags') == {'numeric', 'test_tag'}
 
     assert column.get('description') is None
diff --git a/woodwork/tests/testing_utils/datatable_utils.py b/woodwork/tests/testing_utils/datatable_utils.py
index 49816cf..9a3ffc2 100644
--- a/woodwork/tests/testing_utils/datatable_utils.py
+++ b/woodwork/tests/testing_utils/datatable_utils.py
@@ -25,7 +25,6 @@ def validate_subset_schema(subset_schema, schema):
         col = schema.columns[subset_col_name]
         assert subset_col['logical_type'] == col['logical_type']
         assert subset_col['semantic_tags'] == col['semantic_tags']
-        assert subset_col['dtype'] == col['dtype']
 
 
 def mi_between_cols(col1, col2, df):