NumPy Input and Output: Complete Guide to Reading and Saving Data

 


NumPy Input and Output: Complete Guide to Reading and Saving Data

When we work with NumPy, creating an array is only one part of the job.

In real projects, data usually comes from files, CSV files, text files, databases, sensors, or other applications. After processing the data, we also need to save the results.

NumPy provides several functions for reading and writing data.

In this tutorial, we will learn NumPy Input and Output, commonly called NumPy I/O, from beginner to advanced level.

We will cover:

  • Saving arrays using np.save()
  • Loading arrays using np.load()
  • Saving multiple arrays using np.savez()
  • Using compressed .npz files
  • Reading text and CSV files
  • Writing text and CSV files
  • Using np.loadtxt()
  • Using np.savetxt()
  • Understanding np.genfromtxt()
  • Handling missing data
  • Selecting columns
  • Assigning column names
  • Converting data while reading
  • Using masked arrays
  • Practical examples
  • Exercises

1. What is NumPy I/O?

I/O means Input and Output.

Input means bringing data into your Python program.

Output means saving data from your Python program.

For example:

import numpy as np

 

data = np.array([10, 20, 30, 40, 50])

The array currently exists in memory.

If the program ends, the array will normally disappear.

If we want to use it later, we can save it to a file.

The basic flow is:

             NumPy Program

                  |

        -----------------------

        |                     |

      Input                  Output

        |                     |

     Read data             Save data

        |                     |

   CSV / TXT / NPY        NPY / NPZ / TXT

NumPy supports several formats for storing and reading arrays.


2. Main NumPy I/O Functions

Some important NumPy I/O functions are:

Function

Purpose

np.save()

Save one NumPy array

np.load()

Load NumPy data

np.savez()

Save multiple arrays

np.savez_compressed()

Save multiple arrays in compressed form

np.loadtxt()

Read simple text or CSV data

np.savetxt()

Write arrays to text files

np.genfromtxt()

Read text data with more control and missing-value support

np.fromfile()

Read raw binary or text data

np.ndarray.tofile()

Write array data directly to a file

The most commonly used functions for beginners are:

save()

load()

savetxt()

loadtxt()

genfromtxt()

savez()


3. Saving a NumPy Array with np.save()

The np.save() function is useful when we want to store a NumPy array in NumPy's binary .npy format.

Basic syntax:

np.save(file, array)

Example:

import numpy as np

 

marks = np.array([78, 85, 92, 67, 88])

 

np.save("marks.npy", marks)

This creates:

marks.npy

The .npy format is designed specifically for NumPy arrays.

It preserves important information about the array, such as its data type and shape.


4. Loading an Array with np.load()

After saving an array, we can retrieve it using np.load().

Example:

import numpy as np

 

marks = np.load("marks.npy")

 

print(marks)

Output:

[78 85 92 67 88]

The basic pattern is:

np.save("data.npy", data)

 

data = np.load("data.npy")

This is very useful when a program needs to save its results and use them later.


5. Saving a Two-Dimensional Array

NumPy can save multidimensional arrays as well.

Example:

import numpy as np

 

marks = np.array([

    [78, 85, 92],

    [67, 88, 76],

    [90, 95, 89]

])

 

np.save("student_marks.npy", marks)

Load it again:

data = np.load("student_marks.npy")

 

print(data)

Output:

[[78 85 92]

 [67 88 76]

 [90 95 89]]

The shape is retained.

print(data.shape)

Output:

(3, 3)


6. Why Use the NPY Format?

The .npy format is especially useful when the data is going to be used again by NumPy.

For example:

Python program

      |

      v

NumPy array

      |

      v

student_marks.npy

      |

      v

Later Python program

      |

      v

NumPy array

It is generally more convenient for NumPy-to-NumPy data exchange than manually creating a text file.


7. Saving Multiple Arrays with np.savez()

Sometimes one project contains several arrays.

For example:

students = np.array(["Asha", "Rahul", "Kiran"])

 

marks = np.array([85, 91, 78])

 

attendance = np.array([90, 95, 88])

Instead of creating three separate files, we can store them together.

Use:

np.savez()

Example:

np.savez(

    "student_data.npz",

    students=students,

    marks=marks,

    attendance=attendance

)

This creates:

student_data.npz


8. Loading an NPZ File

We can load the file using:

data = np.load("student_data.npz")

The stored arrays can be accessed by their names.

print(data["students"])

print(data["marks"])

print(data["attendance"])

This makes .npz files useful for storing related arrays together.


9. np.savez_compressed()

Large datasets can consume considerable storage space.

NumPy also provides:

np.savez_compressed()

Example:

np.savez_compressed(

    "student_data_compressed.npz",

    marks=marks,

    attendance=attendance

)

The resulting file is compressed.

Use compressed storage when reducing file size is important.


10. NPY vs NPZ

A simple way to remember the difference is:

.npy

  |

  +-- Usually one NumPy array

 

.npz

  |

  +-- Multiple NumPy arrays

Example:

np.save("marks.npy", marks)

For multiple arrays:

np.savez(

    "data.npz",

    marks=marks,

    attendance=attendance

)


11. Reading Text Files with np.loadtxt()

Not every dataset is stored as .npy.

Many datasets are available as:

.txt

.csv

For simple numeric text data, we can use:

np.loadtxt()

Suppose marks.txt contains:

78 85 92

67 88 76

90 95 89

We can read it using:

import numpy as np

 

data = np.loadtxt("marks.txt")

 

print(data)

Output:

[[78. 85. 92.]

 [67. 88. 76.]

 [90. 95. 89.]]


12. Reading CSV Data with loadtxt()

CSV means Comma-Separated Values.

Suppose students.csv contains:

78,85,92

67,88,76

90,95,89

We can specify the comma as the delimiter.

data = np.loadtxt("students.csv", delimiter=",")

The delimiter tells NumPy how columns are separated.

For CSV:

delimiter=","

For tab-separated data:

delimiter="\t"


13. Writing Data with np.savetxt()

The opposite operation is writing an array to a text file.

Use:

np.savetxt()

Example:

import numpy as np

 

marks = np.array([

    [78, 85, 92],

    [67, 88, 76],

    [90, 95, 89]

])

 

np.savetxt("marks.txt", marks)

This writes the array into a text file.


14. Creating a CSV File with savetxt()

We can also specify a comma delimiter.

np.savetxt(

    "marks.csv",

    marks,

    delimiter=","

)

The resulting file will contain values separated by commas.


15. Adding a Header

A header can make a text or CSV file easier to understand.

Example:

np.savetxt(

    "marks.csv",

    marks,

    delimiter=",",

    header="Maths,Science,Computer"

)

The resulting file will contain a header followed by the data.


16. Controlling Number Formatting

Suppose we have decimal values:

data = np.array([

    [12.34567, 45.67891],

    [23.45678, 67.89123]

])

We can control how numbers are written.

np.savetxt(

    "values.txt",

    data,

    fmt="%.2f"

)

The format:

%.2f

means that two digits should be displayed after the decimal point.


17. When loadtxt() Is Not Enough

loadtxt() works well when the input data is clean and regular.

But real-world datasets are often messy.

For example:

78,85,92

67,,76

90,95,89

One value is missing.

Another dataset might contain:

78,N/A,92

67,85,?

90,95,89

Now we need more control.

This is where:

np.genfromtxt()

becomes useful.


18. Introduction to np.genfromtxt()

genfromtxt() is designed for importing tabular text data when more flexibility is required.

It can help with:

  • Different delimiters
  • Missing values
  • Headers
  • Selecting columns
  • Different data types
  • Named columns
  • Custom conversion functions
  • Fixed-width data

A simple example:

import numpy as np

 

data = np.genfromtxt(

    "students.csv",

    delimiter=","

)


19. How genfromtxt() Processes Data

A useful way to understand genfromtxt() is to imagine two stages.

Text file

   |

   v

Split each line into values

   |

   v

Convert values to required data types

   |

   v

NumPy array

This additional processing gives genfromtxt() more flexibility than simpler input functions.


20. Using String Data Instead of a File

genfromtxt() can also work with data supplied directly by Python.

For example:

from io import StringIO

import numpy as np

 

data = "10,20,30\n40,50,60"

 

result = np.genfromtxt(

    StringIO(data),

    delimiter=","

)

 

print(result)

Output:

[[10. 20. 30.]

 [40. 50. 60.]]

This is useful when data is already available as a string.


21. The delimiter Parameter

The delimiter tells NumPy how to separate columns.

For comma-separated data:

delimiter=","

For tab-separated data:

delimiter="\t"

For space-separated data, we can often leave the delimiter unspecified.

Example:

data = "10 20 30\n40 50 60"

 

result = np.genfromtxt(

    StringIO(data)

)

NumPy can split the values based on whitespace.


22. Fixed-Width Data

Not every text file uses commas or spaces.

Sometimes each column occupies a fixed number of characters.

For example:

001002003

004005006

We can specify the width of each field.

Example:

data = "123456789\n987654321"

 

result = np.genfromtxt(

    StringIO(data),

    delimiter=3

)

 

print(result)

Here each column is three characters wide.

We can also specify different widths:

delimiter=(4, 3, 2)

This is useful for fixed-width files.


23. Removing Extra Spaces with autostrip

Consider:

1,   Alice,   85

2,   Rahul,   91

Extra spaces can become part of the imported strings.

We can use:

autostrip=True

Example:

result = np.genfromtxt(

    StringIO(data),

    delimiter=",",

    dtype="U10",

    autostrip=True

)

This tells NumPy to remove unnecessary spaces around the values.


24. Ignoring Comments

Many data files contain comments.

For example:

# Student data

78,85,92

67,88,76

90,95,89

By default, # is treated as a comment marker.

Example:

data = np.genfromtxt(

    "marks.csv",

    delimiter=","

)

Comment lines are ignored.

Comments can also appear after data.

For example:

78,85,92 # first student

The comment portion can be ignored.


25. Changing the Comment Character

The comment character can be customized.

For example:

np.genfromtxt(

    "data.txt",

    delimiter=",",

    comments=";"

)

Now ; is treated as the beginning of a comment.


26. Skipping Header Lines

Many files begin with a title or header.

Example:

Student Marks Report

78,85,92

67,88,76

90,95,89

We may want to skip the first line.

Use:

data = np.genfromtxt(

    "marks.csv",

    delimiter=",",

    skip_header=1

)

skip_header=1 means:

Skip the first line

Read the remaining data


27. Skipping Footer Lines

Sometimes unwanted information appears at the end of the file.

Example:

78,85,92

67,88,76

90,95,89

End of report

We can skip the last line:

data = np.genfromtxt(

    "marks.csv",

    delimiter=",",

    skip_footer=1

)


28. Selecting Specific Columns

Suppose the file contains:

101 85 90

102 78 88

103 92 95

Perhaps we only need the first and last columns.

We can use:

data = np.genfromtxt(

    StringIO(text),

    usecols=(0, 2)

)

Remember that NumPy uses zero-based indexing.

Therefore:

Column 0 = first column

Column 1 = second column

Column 2 = third column


29. Selecting Columns by Name

If column names are available, we can also use their names.

Example:

data = np.genfromtxt(

    StringIO(text),

    names="id,maths,science",

    usecols=("id", "science")

)

This can make the code easier to understand.


30. Choosing the Data Type

The dtype parameter controls how imported data is represented.

For example:

data = np.genfromtxt(

    StringIO("10 20 30\n40 50 60"),

    dtype=np.int64

)

The resulting values are integers.

We can also specify different types for different columns.

Example:

dtype=(np.int64, np.float64, np.float64)

This is useful when a dataset contains different kinds of values.


31. Using dtype=None

Sometimes we do not know the data type of each column in advance.

We can use:

dtype=None

NumPy will try to determine suitable types from the data.

Example:

data = np.genfromtxt(

    StringIO("10 Alice\n20 Rahul"),

    dtype=None,

    names=True

)

However, automatic type detection requires additional processing.

If performance is important and the data types are known, explicitly specifying dtype is generally preferable.


32. Giving Names to Columns

Column names are very useful when working with structured data.

Example:

data = np.genfromtxt(

    StringIO("101 85\n102 91"),

    names="id,marks"

)

Now we can access fields using their names.

For example:

print(data["id"])

print(data["marks"])

This is easier to understand than remembering column positions.


33. Reading Names from the File

Suppose the input itself contains column names:

id marks

101 85

102 91

103 78

We can use:

data = np.genfromtxt(

    StringIO(text),

    names=True

)

NumPy reads the first appropriate line as the field names.


34. The defaultfmt Parameter

When structured data needs names but some names are not supplied, NumPy can generate default field names.

For example:

f0

f1

f2

We can customize the generated names using defaultfmt.

Example:

data = np.genfromtxt(

    StringIO("10 20 30"),

    dtype=(int, float, int),

    defaultfmt="value_%02i"

)

This can produce names such as:

value_00

value_01

value_02


35. Validating Column Names

Column names should be valid and should not create confusion with Python or NumPy attributes.

For example, names containing spaces or certain special characters may need to be cleaned.

genfromtxt() provides options such as:

deletechars

excludelist

case_sensitive

These options give more control over field names.


36. deletechars

deletechars specifies characters that should be removed from field names.

This is useful when imported column names contain unwanted special characters.


37. excludelist

Some names may conflict with existing Python or NumPy names.

For example:

print

file

return

The excludelist option can be used to protect against such conflicts.


38. case_sensitive

We can control the capitalization of field names.

For example:

case_sensitive=True

keeps the names case-sensitive.

Other settings can convert names to upper or lower case.


39. Converting Values While Reading

Sometimes the values in a file are not directly suitable for the required data type.

Consider:

101,25%

102,78.5%

103,91%

The percentage values are strings because of %.

We can create a converter.

Example:

convert_percent = lambda x: float(x.strip("%")) / 100

Then:

data = np.genfromtxt(

    StringIO(text),

    delimiter=",",

    converters={1: convert_percent}

)

Now:

25%   -> 0.25

78.5% -> 0.785

91%   -> 0.91


40. Why Are Converters Useful?

Converters are useful when the input format needs transformation.

Examples include:

25%        -> 0.25

2026/09/21 -> date value

" 45 "     -> 45

special text -> numeric value

The converter receives the original value and returns the desired representation.


41. Converters Can Be Specified by Column Name

If the data has named columns, we do not always need to use the column number.

For example:

converters={"percentage": convert_percent}

This can make the code easier to read.


42. Handling Missing Data

Missing data is common in real-world datasets.

Example:

101,85,90

102,,88

103,92,95

The second student's second value is missing.

Another dataset may use:

N/A

?

???

to indicate missing values.

This is one of the important reasons to use genfromtxt().


43. The missing_values Parameter

We can tell NumPy which values should be considered missing.

Example:

data = np.genfromtxt(

    StringIO(text),

    delimiter=",",

    missing_values="N/A"

)

We can also define different missing markers for different columns.

For example:

missing_values={

    0: "N/A",

    1: "???"

}


44. The filling_values Parameter

Finding a missing value is only one part of the problem.

We may also want to replace it with a particular value.

This is where:

filling_values

is useful.

Example:

data = np.genfromtxt(

    StringIO(text),

    delimiter=",",

    missing_values="N/A",

    filling_values=0

)

Missing values will be replaced according to the specified filling rule.


45. Default Filling Values

If we do not specify filling_values, NumPy chooses a default based on the expected data type.

For example:

Boolean  -> False

Integer  -> -1

Float    -> np.nan

Complex  -> np.nan+0j

String   -> ???

This is important because the default value may not always be appropriate for your application.

For example, replacing a missing student mark with -1 may be technically valid but may not be suitable for statistical calculations.

Therefore, choose filling values carefully.


46. Column-Specific Missing Values

Different columns may use different missing-value markers.

Example:

N/A,85,???

90, ,95

We can define rules for each column.

Example:

missing_values = {

    0: "N/A",

    1: " ",

    2: "???"

}

And then define the replacement values:

filling_values = {

    0: 0,

    1: 0,

    2: -999

}

This gives fine control over messy datasets.


47. Keeping Track of Missing Values with usemask

Sometimes replacing missing data is not what we want.

We may want to know exactly which values were missing.

For this purpose, we can use:

usemask=True

Example:

data = np.genfromtxt(

    StringIO(text),

    delimiter=",",

    usemask=True

)

NumPy returns a masked array.

The mask identifies positions where values were missing.

Conceptually:

Data:

10   20   missing

30   40   50

 

Mask:

False False True

False False False

This is useful when missing values must remain identifiable during later processing.


48. A Complete genfromtxt Example

Let us combine several concepts.

Suppose our data is:

# Student data

101,85,90

102,N/A,88

103,92,95

We can write:

import numpy as np

from io import StringIO

 

text = """# Student data

101,85,90

102,N/A,88

103,92,95

"""

 

data = np.genfromtxt(

    StringIO(text),

    delimiter=",",

    dtype=np.float64,

    missing_values="N/A",

    filling_values=0

)

 

print(data)

The program:

  1. Reads the text.
  2. Uses comma as the separator.
  3. Ignores the comment.
  4. Detects N/A as missing.
  5. Replaces the missing value with 0.
  6. Creates a NumPy array.

49. Choosing the Right NumPy I/O Function

A simple decision guide is useful.

Need to save one NumPy array?

        |

        +---- np.save()

 

Need to load a .npy/.npz file?

        |

        +---- np.load()

 

Need to save multiple arrays?

        |

        +---- np.savez()

 

Need compression?

        |

        +---- np.savez_compressed()

 

Simple clean text data?

        |

        +---- np.loadtxt()

 

Write array to text/CSV?

        |

        +---- np.savetxt()

 

Messy text data or missing values?

        |

        +---- np.genfromtxt()


50. Practical Student Marks Project

Suppose we have:

import numpy as np

 

marks = np.array([

    [85, 90, 78],

    [76, 88, 92],

    [91, 84, 89]

])

Save the data:

np.save("marks.npy", marks)

Load it:

loaded_marks = np.load("marks.npy")

Save it as CSV:

np.savetxt(

    "marks.csv",

    marks,

    delimiter=",",

    fmt="%d"

)

Load the CSV:

data = np.loadtxt(

    "marks.csv",

    delimiter=","

)

This demonstrates the difference between NumPy's binary format and a human-readable text format.


51. Binary Files vs Text Files

There are two important approaches.

Binary NumPy format

Examples:

.npy

.npz

Advantages:

  • Designed for NumPy data
  • Preserves array information
  • Convenient for NumPy applications
  • Suitable for storing arrays for later processing

Text format

Examples:

.txt

.csv

Advantages:

  • Easy for humans to read
  • Easy to inspect with a text editor
  • Convenient for exchanging simple tabular data
  • Widely supported by other applications

The choice depends on the application.


52. Example: Saving Multiple Arrays

Suppose we have:

names = np.array(["Asha", "Rahul", "Kiran"])

 

marks = np.array([85, 91, 78])

 

attendance = np.array([90, 95, 88])

Store them together:

np.savez(

    "class_data.npz",

    names=names,

    marks=marks,

    attendance=attendance

)

Load:

data = np.load("class_data.npz")

 

print(data["names"])

print(data["marks"])

print(data["attendance"])


53. Example: Reading Selected Columns

Suppose:

101 85 90 95

102 78 88 91

103 92 95 89

If we only need student ID and the final mark:

data = np.genfromtxt(

    StringIO(text),

    usecols=(0, 3)

)

This avoids loading unnecessary columns into the resulting array.


54. Example: Cleaning Percentage Data

Suppose:

101,85%,90%

102,78%,88%

103,92%,95%

We can convert percentage strings:

def percentage(value):

    return float(value.strip("%"))

Then:

data = np.genfromtxt(

    StringIO(text),

    delimiter=",",

    converters={

        1: percentage,

        2: percentage

    }

)

Now the percentage columns are numeric.


55. Common Mistakes

Mistake 1: Forgetting the delimiter

For CSV files:

np.loadtxt("data.csv")

may not interpret the data correctly if commas separate the columns.

Use:

np.loadtxt(

    "data.csv",

    delimiter=","

)


Mistake 2: Using loadtxt() for messy data

If the file contains missing values such as:

10,20,30

40,,60

genfromtxt() is usually more appropriate.


Mistake 3: Forgetting zero-based indexing

For:

A B C D

the NumPy column indexes are:

A -> 0

B -> 1

C -> 2

D -> 3

Therefore:

usecols=(0, 3)

selects A and D.


Mistake 4: Ignoring data types

If a dataset contains integers, floating-point values, and strings, think carefully about the appropriate dtype.


Mistake 5: Replacing missing values without thinking

For example:

filling_values=0

is convenient, but zero may have a completely different meaning from "missing".

Choose replacement values according to the problem.


56. Mini Project: Student Dataset Import

Create a file named:

students.csv

with:

101,Asha,85,90

102,Rahul,78,88

103,Kiran,92,95

104,Meena,N/A,91

Try to:

  1. Read the file using genfromtxt().
  2. Use comma as the delimiter.
  3. Give names to the columns.
  4. Identify N/A as missing.
  5. Replace missing marks with 0.
  6. Select only the student ID and marks columns.
  7. Save the cleaned data as a .npy file.
  8. Load the .npy file again.
  9. Print the final array.

57. Exercises

Exercise 1: Save and Load

Create this array:

numbers = np.array([10, 20, 30, 40, 50])

Save it as:

numbers.npy

Then load it and print the result.


Exercise 2: Multiple Arrays

Create:

names = np.array(["A", "B", "C"])

marks = np.array([80, 90, 85])

Save both arrays into:

students.npz

Load the file and display both arrays.


Exercise 3: CSV File

Create:

marks = np.array([

    [80, 90],

    [75, 85],

    [92, 88]

])

Save it as:

marks.csv

Use a comma as the delimiter.

Then load the CSV file again.


Exercise 4: Skip Header

Consider:

Student Marks

80 90

75 85

92 88

Use genfromtxt() to skip the first line.


Exercise 5: Select Columns

Consider:

101 80 90 85

102 75 85 88

103 92 88 91

Read only:

Student ID

Final mark

Use usecols.


Exercise 6: Missing Values

Consider:

101,80,90

102,N/A,88

103,92,91

Use genfromtxt() to:

  • detect N/A
  • replace it with 0

Exercise 7: Percentage Converter

Consider:

101,80%

102,75%

103,92%

Create a converter that changes:

80% -> 80

75% -> 75

92% -> 92


Exercise 8: Column Names

Create an array using:

ID

Name

Marks

Read the data with column names and access the marks using the field name.


Exercise 9: Masked Array

Create data containing missing values.

Use:

usemask=True

Print the data and investigate which elements are masked.


Exercise 10: Mini Data Processing Project

Create a CSV file containing:

ID,Maths,Science,Computer

101,85,90,95

102,78,N/A,88

103,92,95,91

104,N/A,82,89

Write a NumPy program that:

  1. Reads the file.
  2. Handles the missing values.
  3. Assigns column names.
  4. Selects the three subject columns.
  5. Calculates the average.
  6. Saves the processed array as .npy.
  7. Loads the saved file.
  8. Displays the final result.

58. Quick Revision

The most important functions are:

np.save()

np.load()

 

np.savez()

np.savez_compressed()

 

np.loadtxt()

np.savetxt()

 

np.genfromtxt()

Remember:

.npy

  |

  +-- NumPy binary array

 

.npz

  |

  +-- Collection of NumPy arrays

 

.txt / .csv

  |

  +-- Human-readable text data

For simple clean numerical files:

np.loadtxt()

For more complicated text files:

np.genfromtxt()

For saving NumPy arrays:

np.save()

For loading NumPy arrays:

np.load()


59. Final Takeaway

NumPy I/O is an important skill for anyone working with data.

Creating an array is only the beginning. A practical program must also know how to read data from external sources and save processed results.

Start with these four functions:

np.save()

np.load()

np.loadtxt()

np.savetxt()

Then learn:

np.genfromtxt()

when you begin working with real-world datasets containing headers, comments, different delimiters, missing values, or inconsistent formats.

Once you understand these functions, you can build Python programs that move smoothly between files and NumPy arrays.

The key idea is simple:

External Data

     |

     v

Read with NumPy

     |

     v

NumPy Array

     |

     v

Process Data

     |

     v

Save Results

     |

     v

.npy / .npz / .txt / .csv

This forms an important foundation for data analysis, machine learning, scientific computing, and engineering applications using Python and NumPy.

Practice. Experiment. Build.

Try every example yourself instead of only reading the code. Small experiments with different file formats, delimiters, missing values, and data types will make NumPy I/O much easier to understand.



Comments